新版ASMSupport使用教程

基本操作

在这一节中将会介绍java的基本操作在asmsupport的实现。在介绍操作之前,我们首先确定我们有哪些基本的操作,可以参考下表

  • 后置自增减 expr++ expr--
  • 一元操作: ++expr --expr +expr -expr ~ !
  • 乘除模: * / %
  • 加减操作: + -
  • 位移操作: <<,>>,>>>
  • 关系操作: <,>,<=,>=,instanceof
  • 等值操作: ==,!=
  • 按位与: &
  • 按位异或: ^
  • 按位或: |
  • 条件与: &&
  • 条件或: ||
  • 三元操作: ? :
  • 赋值操作: =,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>=,>>>=

除了上述的操作,还有一下操作

  • **创建变量 **
  • **数组操作 **
  • **返回操作 **
  • **方法调用 **
  • 获取field
  • **抛出异常 **

我们先介绍三个基础的操作1。获取常量值,2.获取AClass对象,3.创建变量,然后再按照上述顺序自上而下介绍。我们将会使用asmsupport先创建一个类OperatorTest,同时在这个类中添加一个main方法。在这里我们会提前使用到如何创建一个class或者method的知识,但是这并不是本节的重点,创建class/enum/interface等将在后面做详细介绍。生产的类和方法大致如下:

public class OperatorTest {
    public static void main(String[] args) {
        //我们将用asmsupport在这里位置生产我们期望的内容
    }
}

创建class

DummyClass dc = new DummyClass("asmsupport.OperatorTest").public_();
dc.newMethod("main").public_().static_().argTypes(Stirng[].class)._argNames("args")
  .body(new MethodBody(){
      @Override
      public void body(LocalVariable... args) {
          //在这里调用相印的操作生产字节码。
          //TODO
      }
  });
Class cls = dc.builder();

在上面的代码中我们就通过asmsupport创建出一个class的简单框架。我们也将在TODO位置生产我们预期的内容,也就是说下面我们介绍的操作的所有代码都将是在这个位置调用的,而在下面介绍操作的时候,将省去类创建的过程。

常量值

这里介绍asmsupport如何获得常量值,在asmsupport可以直接使用的值包括基本类型的值(boolean, byte, char, short, int, long, float, double), String类型,Class类型,以及null(null值允许是任何非基本类型).在asmsupport这些操作主要通过两种方式获得。

方式1

直接通过Value类的静态方法value获取非null值,获取null值通过方法getNullValue。代码如下:

IValue one = Value.val(1); //获取一个int类型的值1
IValue oneDotOne = Value.val(2.2D); //获取一个double类型的值2.2
IValue str = Value.val("asmsupport"); //获取一个String类型的"asmsupport"值
IValue nullVal = Value.getNullValue(String.class); //获取String类型的

方式2

如果你的代码已经在一个程序块中了,正如我们的实例,我们的代码在TODO的位置,那么我们就可以使用另一种方式,通过ValueAction接口来获取IValue对象。

IValue one =val(1); //获取一个int类型的值1
IValue oneDotOne = val(2.2D); //获取一个double类型的值2.2
IValue str = val("asmsupport"); //获取一个String类型的"asmsupport"值
IValue nullVal = null_(String.class); //获取String类型的

上面的Value.val方法和ValueAction.val方法均为重载方法,具体希望获取什么类型的值取决于你传入的参数。

数组值

上面介绍的是如何创建常量值,这里我们介绍如何获取一个数组值。通常在java中创建数组的时候一般有两种方式如下

new int[1][2][];
new String[]{"Hello", "ASMSupport"}

上面两种方式一个只创建了数组空间,而第二个则是在创建数组的时候就赋予了值。那么接下来就是使用asmsupport创建上面两个值,我们使用ArrayAction接口中的makeArray和newarray两个方法来实现。

makeArray(int[][][].class, val(1), val(2));
newArray(String[].class, new Parameterized[]{val("Hello"), val("ASMSupport")});

该接口下还有很多重载方法。

获取AClass

通常我们在使用asmsupport的时候,很多方法都直接使用Class对象即可,但是还有部分操作是要通过AClass完成的,比如获取静态field,和常量值一样,这里同意有两种方式获取AClass。

方式1

通过AClassFactory类,这个类中包含了多个静态方法。

1. AClass arrayList = AClassFactory.deftype(ArrayList.class);
2. AClass stringDoubleDimArray = AClassFactory.deftype(String[][].class);
3. AClass stringDoubleDimArray = AClassFactory.defArrayType(String[][].class);
4. AClass stringDoubleDimArray = AClassFactory.defArrayType(String.class, 2);
5. AClass stringDoubleDimArray = AClassFactory.defArrayType(AClassFactory.deftype(String.class), 2);

如果你的代码在一个asmsupport的程序块中,比如上面代码中的TODO位置,我们可以通过AClassDefAction接口来获取

1. AClass arrayList = deftype(ArrayList.class);
2. AClass stringDoubleDimArray = deftype(String[][].class);
3. AClass stringDoubleDimArray = defArrayType(String[][].class);
4. AClass stringDoubleDimArray = defArrayType(String.class, 2);
5. AClass stringDoubleDimArray = defArrayType(AClassFactory.deftype(String.class), 2);

上面的例子中,第一行创建的是一个ArrayList的AClass,2~5都是创建一个ArrayClass,都是String类型的一个二维数组。

创建变量

通过VariableAction接口的var方法即可创建局部变量。

 //创建String类型变量name,并且赋值为"asmsupport"
 //String name = "asmsupport";
1. LocalVariable name = var("name", String.class, val("asmsupport"));

//创建一个String数组
//String[] names = new String[]{"Hello", "ASMSupport"};
2. LocalVariable names = arrayvar("names", String[].class, 
        newArray(String[].class, new Parameterized[]{val("Hello"), val("ASMSupport")}););

后置自增减(expr++ expr--)

这个两个操作是通过CrementAction接口完成,该接口是完成自增减操作。

1. LocalVariable i = var("i", int.class, val(1));
2. postinc(i); //i++
3. postdec(i); //i--

一元操作(++expr --expr +expr -expr ~ !)

这里的操作都是一元操作,其中++expr --expr依然采用CrementAction接口的方法完成。+expr不需要任何操作,-expr是取负数采用ActionSet接口中的neg方法。~则是取反操作,使用BitwiseAction的reverse方法。

1. LocalVariable i = var("i", int.class, val(8));
2. perinc(i); // ++i;
3. perdec(i); // --i;
4. neg(i); // -i;
5. reverse(i); // ~i

乘除模(* / %)

这里实现了乘法除法取模操作,其方法是在ArithmeticAction接口中

1. LocalVariable i = var("i", int.class, val(8));
2. LocalVariable j = var("i", int.class, val(2));
3. mul(i, j); // i * j
4. div(i, j); // i / j
5. mod(i, j); // i % j

加减操作( + -)

加法减法操作

1. LocalVariable i = var("i", int.class, val(8));
2. LocalVariable j = var("i", int.class, val(2));
3. add(i, j); // i + j
4. sub(i, j); // i - j

位移操作(<<, >>, >>>)

这里的操作实现了位移操作分别是左移,右移,无符号右移,通过BitwiseAction接口的方法完成

1. LocalVariable i = var("i", int.class, val(8));
2. shl(i, val(2));  // i << 2 
3. shr(i, val(2));  // i >> 2
4. ushr(i, val(2)); // i >>> 2

关系操作(<,>,<=,>=,instanceof)

这组的操作关系操作,其中这一组(<,>,<=,>=)是使用RelationalAction接口,instanceof使用的是ActionSet接口的instanceof_方法。

1. LocalVariable i = var("i", int.class, val(8));
2. LocalVariable j = var("i", int.class, val(2));
3. lt(i, j); // i < j
4. gt(i, j); // i > j
5. le(i, j); // i <= j
6. ge(i, j); // i >= j
7. LocalVariable list = var("list", List.class, new(ArrayList.class))
8. instanceof_(list, ArrayList.class);(

等值操作(==,!=)

1. LocalVariable i = var("i", int.class, val(8));
2. LocalVariable j = var("i", int.class, val(2));
3. eq(i, j); // i == j
4. ne(i, j); // i != j)

这操同样能够比较两个对象比如obj1 == obj2或obj1 != obj2. 但是这个操作并不是会调用Object的equals的方法。

按位与(&)

这里的的与操作存在两种含义,一个是对数字类型的进行与运算,比如1&2,还有就是对于boolean类型的的逻辑与,但底层是同样的字节码实现。这样的两种操作很容易操作失误护着产生歧义,后期版本将会修复。

1. LocalVariable i = var("i", int.class, val(8));
2. LocalVariable j = var("i", int.class, val(2));
3. LocalVariable m = var("i", boolean.class, val(true));
4. LocalVariable n = var("i", boolean.class, val(false));
5. band(i, j); // i & j
6. logicAnd(m, n); // m & n

位异或(^)

异或操作,使用BitwiseAction接口

1. bxor(i, j); // i ^ j

位或(|)

这个操作同&操作类似,使用BitwiseAction接口

1. bor(i, j); // i | j
2. logicOr(m, n); // m | n

条件与(&&)

使用RelationalAction接口的and方法

1. and(val(true), val(false)) // true && false

条件或(||)

使用RelationalAction接口的or方法

1. or(val(true), val(false)) // true || false

三元操作(? : )

这个操作完成的是三元操作,使用的是ActionSet接口的ternary方法

1. ternary(gt(i, j), sub(i, j), add(i, j));// i > j ? i - j : i + j

赋值操作(=,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>=,>>>=)

这里的赋值操作非常多,但是asmsupport仅仅实现了单一的赋值操作,其余的都是先执行运算再赋值大同小异。使用的是VariableAction接口中的assign.

1. LocalVariable i = var("i", int.class, val(1));
2. assign(i, val(10));

数组操作

之前已经介绍如何创建一个数组,这里将介绍如何对数组操作,比如获取数组元素,获取数组长度,保存数组元素,这些操作均来自与接口ArrayAction接口

1. LocalVariable array = arrayvar("i", int[][].class, makeArray(int[][].class, val(2), val(2)));//int[][] i = new int[2][2];
2. arrayStore(array, val(100), val(1), val(1)); //array[1][1] = 100;
3. arrayLoad(array, val(1), val(1)); //array[1][1]
4. arrayLength(array, val[1]); //array[1].length

返回操作

在中我们可以使用return返回,如果该方法有返回值,则需要在return后添加返回值。在asmsupport中使用ActionSet接口中的return_()方法和return_(returnObj).

1. return_(); //return;
2. return_(add(val(1), val(2))); //return 1+2;

这里面需要注意的是,在java代码中一个没有返回值的方法,如果没有显式的执行return,那么java编译器会自动的在方法最后追加return操作。但是在asmsupport中并没这样的判断,所以需要显式的执行return方法。这一点要注意,后期版本将会支持。

获取field

有两种field获取,一个静态field,另一个是非静态field。和java一样静态方法通过AClass对象的field方法获取,非静态方法通过IVariable对象的field方法获取。但是如果通过 IVariable对象获取静态field同样也允许的。比如有下面这个类

public class A {
    
    public String field1 = "Hello";
    
    public static String field2 = "ASMSupport";

}

下面是asmsupport获取field的方式

1. LocalVariable a = var("a", new_(A.class)); //A a = new A();
2. a.field("field1"); // a.field1
3. a.field("field2"); // A.field2
4. defType(A.class).field("field2"); //A.field2

第2行是获取非静态field,第3,4行分别是通过变量和Class获取静态field。

方法调用

总共有三种类型的方法调用:1.调用构造方法,2.调用对象的方法,3.调用静态方法。这些操作是在MethodInvokeAction接口中

1. LocalVariable list = var("list", List.class, new_(ArrayList.class)); //创建一个ArrayList对象,new ArrayList();
2. call(list, "add", val(1)); // list.add(1);
3. call(Integer.class, "parseInt", val("100")); // Integer.parseInt("100")

第1行调用构造方法,第2行调用非静态方法,第3行调用静态方法。

抛出异常

抛出异常是通过throw关键字,在asmsupport中是通过ActionSet接口的throw_方法。

1. throw_(new_(RuntimeException.class));//throw new RuntimeException();

创建接口

构造对象

创建接口我们使用DummyInterface类来创建。首先创建一个DummyInterface对象,可以通过一下几种方式

1. DummyInterface dummy = new DummyInterface();
2. DummyInterface dummy = new DummyInterface("asmsupport.test.Test");
3. DummyInterface dummy = new DummyInterface("asmsupport.test", "Test");

上面三种方式,第一种仅仅知识啊创建一个DummyInterface对象,第二种方式在创建对象的同时赋予的接口的全限定名。第三种方式将包名和类名分开指定。

设置接口名

如果在构造DummyInterface的同时已经赋予了包名和类名,那么这里就可以不用为其设置。当然也可以在这里重新设置,将以前的值覆盖。

dummy.package_("asmsupport.test").name("Test")

设置接口的访问权限

接口的访问控制权限有public和默认(default)值。默认情况先创建的DummyInterface是default的访问权限,可以通过一下方式设置或者充值访问权限

dummy.public_() : 设置成public
dummy.default_() : 设置成默认

添加父类型

如果希望接口继承其他类型的接口通过一下方式实现

dummy.extends_(List.class, Cloneable.class);

这是一个变元方法,也就是允许继承多个。

创建Field

在接口里面创建的field是都是默认具有public static final修饰的。通过下面代码创建field

DummyField field = dummy.newField(String.class, "FIELD");

上面的代码为接口创建了FIELD的field。等同于"public static final String FIELD",如果仅仅值调用了上面的代码,那么FIELD的值就是null。有两种方式为.

Field赋值1

上面看到了newField返回的是DummyField对象。可以通过这个对象的一系列重载方法val()为其赋值。但是这样只允许根据field类型为其赋予基本的值。比如上面我们为FIELD赋值为"asmsupport"

field.val("ASMSupport").

Field赋值2

如果希望类型通过方法调用的方式赋值比如下面的代码

public String FIELD = String.valueOf(100);

那么赋值就需要在静态程序块中通过assign方法赋值,为什么在静态程序块可以参考文档site/bytecode/init_clinit.md

创建方法

这里将介绍如何创建接口的方法。通过DummyInterface的newMethod方法创

DummyInterfaceMethod method = dummy.newMethod("test");

通过上面的代码就已经告诉asmsupport了,创建一个test的方法,但是test方法仅仅是如下内容

public void test();

由于是接口方法,所以public的修饰符是无法改变的,但是方法的返回类型已经参数列表是可以改变了。

修改返回类型

method.return_(Stirng.class);

修改参数列表

method.argTypes(int.class, double.class);

抛出捕获

method.throws_(RuntimeException.class, IOException.class);

通过上面的修改创建出来的方法就变成如下内容

public String test(int arg1, double arg2) throws RuntimeException, IOException

创建静态程序块

在编写java代码的时候,接口内是不允许有static程序块的,但是使用asmsupport有这样的特权。通过下面的方式

dummy.newStaticBlock(new StaticBlockBody() {
    
    @Override
    public void body(){
        GlobalVariable FIELD = getMethodOwner().field("FIELD");
        assign(FIELD, call(String.class, "valueOf", val(100)));
        
        GlobalVariable out = defType(System.class).field("out");
        call(out, "println", val("Hello ASMSupport"))
        
        return_();
    }
    
});

在这里我们创建出了一个静态程序块,在程序块中为我们在上面创建的FIELD赋值,同时还打印了"Hello ASMSupport",这个在java中是无法实现的。最后我们显式的调用了return。

设置javaversion

我们会根据当前使用的JDK版本自动选择class的版本,但是也可以显式的设置class版本,通过下面的方式

dummy.setJavaVersion(Opcodes.V1_5)

设置ClassLoader

我们内置ClassLoader来完成我们生成的class的字节数组到class对象,当然也允许自定义classloader通过下面的方法

dummy.setClassLoader(classloader);

设置class文件输出目录

dummy.setClassOutPutPath("../classes");

设置这个属性,我们生成的class将会输出到指定目录,这样我们可以通过反编译大致看下class内容是否和预期相同。

开始创建

Class<?> clazz = dummy.build();

到这里我们就完成了一个接口的创建。生产的代码大致如下:

package asmsupport.test
 
public interface Test extends List, Cloneable{
    
    public String FIELD = String.valueOf(100);
    
    static {
        System.out.println("Hello ASMSupport");
    }
    
    public String test(int arg1, double arg2) throws RuntimeException, IOException;
}

创建类

构造对象

使用DummyClass创建class

1. DummyClass dummy = new DummyClass();
2. DummyClass dummy = new DummyClass("asmsupport.test.Test");
3. DummyClass dummy = new DummyClass("asmsupport.test", "Test");

上面三种方式,第一种仅仅知识啊创建一个DummyClass对象,第二种方式在创建对象的同时赋予的接口的全限定名。第三种方式将包名和类名分开指定。

设置类名

如果在构造DummyInterface的同时已经赋予了包名和类名,那么这里就可以不用为其设置。当然也可以在这里重新设置,将以前的值覆盖。

dummy.package_("asmsupport.test").name("Test")

设置接口的访问权限

接口的访问控制权限

dummy.public_() : 设置成public
dummy.protected_(); 设置为protected
dummy.default_() : 设置成默认
dummy.private_() : 设置为private

设置为抽象类

dummy.abstract_();

设置类为static

dummy.static_();

添加父类型

dummy.extends_(ArrayList.class);

添加接口

dummy.implements_(List.class, Cloneable.class);

创建Field

DummyField field = dummy.newField(String.class, "FIELD");

上面的代码为类创建了名为FIELD的字段。默认情况下使用的是默认的修饰符,上面的代码生成的对应java代码如下:

String FIElD;

设置field名

field.name("fieldName");

通过这个方法可以重新设置field名

设置field类型

field.type(int.class);

通过这个方法可以重新设置field的类型

修改field访问控制符号

默认为default。

field.public_(); 
field.protected_();
field.default_();
field.private_();

其他修饰

field.transient_(); //增加transient修饰
field.static_(); //设置为static
field.volatile_(); //增加volatile修饰
field.final_(); //增加final修饰

Field赋值1

上面看到了newField返回的是DummyField对象。可以通过这个对象的一系列重载方法val()为其赋值。但是这样只允许根据field类型为其赋予基本的值。比如上面我们为FIELD赋值为"asmsupport"

field.val("ASMSupport").

Field赋值2

如果希望类型通过方法调用的方式赋值比如下面的代码

public String FIELD = String.valueOf(100);

那么赋值就需要在静态程序块中通过assign方法赋值,为什么在静态程序块可以参考文档site/bytecode/init_clinit.md

创建方法

这里将介绍如何创建接口的方法。通过DummyInterface的newMethod方法创

DummyMethod method = dummy.newMethod("test");

通过上面的代码就已经告诉asmsupport了,创建一个test的方法,但是test方法仅仅是如下内容

public void test();

由于是接口方法,所以public的修饰符是无法改变的,但是方法的返回类型已经参数列表是可以改变了。

修改返回类型

method.return_(Stirng.class);

修改参数列表

method.argTypes(int.class, double.class);

设置方法参数名

mehtod.argNames("i", "d");

抛出捕获

method.throws_(RuntimeException.class, IOException.class);

修改访问权限

method.public_(); 
method.protected_();
method.default_();
method.private_();

修改其他修饰

method.abstract_(); //增加abstract修饰
method.static_(); //设置为static
method.synchronized_(); //增加synchronized修饰
method.final_(); //增加final修饰
method.native_(); //增加native修饰
method.strictfp_(); //增加strictfp修饰

增加方法体

method.body(new MethodBody() {
    
    @Override
    public void body(LocalVariable... args) {
        //DO method logic here.
    }
    
});

创建构造方法

DummyConstructor constructor = dummy.newConstructor();

修改参数列表

constructor.argTypes(int.class, double.class);

设置方法参数名

constructor.argNames("i", "d");

抛出捕获

constructor.throws_(RuntimeException.class, IOException.class);

修改构造方法修饰符

constructor.public_(); 
constructor.protected_();
constructor.default_();
constructor.private_();

增加方法体

constructor.body(new ConstructorBody(){
    
    @Override
    public void body(LocalVariable... args) {
        supercall();
        return_();
    }
});

注意如果需要调用父类的构造方法则需要通过supercall方法调用。

创建静态程序块

dummy.newStaticBlock(new StaticBlockBody() {
    
    @Override
    public void body(){
        GlobalVariable FIELD = getMethodOwner().field("FIELD");
        assign(FIELD, call(String.class, "valueOf", val(100)));
        
        GlobalVariable out = defType(System.class).field("out");
        call(out, "println", val("Hello ASMSupport"))
        
        return_();
    }
    
});

在这里我们创建出了一个静态程序块,在程序块中为我们在上面创建的FIELD赋值。最后我们显式的调用了return。

设置javaversion

我们会根据当前使用的JDK版本自动选择class的版本,但是也可以显式的设置class版本,通过下面的方式

dummy.setJavaVersion(Opcodes.V1_5)

设置ClassLoader

我们内置ClassLoader来完成我们生成的class的字节数组到class对象,当然也允许自定义classloader通过下面的方法

dummy.setClassLoader(classloader);

设置class文件输出目录

dummy.setClassOutPutPath("../classes/");

设置这个属性,我们生成的class将会输出到指定目录,这样我们可以通过反编译大致看下class内容是否和预期相同。

开始创建

Class<?> clazz = dummy.build();    

修改类

构造对象

使用DummyModifiedClass修改class

1. DummyModifiedClass dummy = new DummyModifiedClass(A.class);

传入的A.class为希望修改的类

新建Field/Method/构造方法

在被修改类中新建Field/Method/构造方法和创建Class的时候相同,可参考文档manual/class.md

修改方法

dummy.modify("test", new Class[]{int.class, String}, new ModifiedMethodBody(){

    @Override
     public void body(LocalVariable... args) {
         //TODO
     }

});

上面的代码是修改方法test(int a, String b). 目前修改方法我们只允许修改方法内容,理论上讲也是可以修改方法的修饰符参数列表的,但是这的意义并不大,因为我们修改方法的主要目的是想增加一些业务逻辑,而不是将方法签名都改了,如果真是这样还不如新建一个方法。

上面的第三个参数就是我们修改的方法方法体。我们希望修改的业务逻辑在标记"//TODO"的位置上。我们能够在这里写入自己的逻辑。比如下面:

@Override
public void body(LocalVariable... args) {
    GlobalVariable out = defType(System.class).field("out");
    call(out, "println", val("Hello ASMSupport"))
    return_();
}

我们将方法修改成了只打印一句"Hello ASMSupport",如果希望添加一个逻辑,就是计算方法执行时间,那么就需要调用方法被修改前的内容了,代码如下:

@Override
public void body(LocalVariable... args) {
    LocalVariable time = var("time", long.class, call(System.class, "currentTimeMillis"));         
    callOrig();
    GlobalVariable out = defType(System.class).field("out");
    call(out, "println", sub(call(System.class, "currentTimeMillis"), time));
    return_();
}

上面的代码中callOrig()方法就是调用原方法的内容。如果原方法是有返回值的,就可以使用下面代码:

@Override
public void body(LocalVariable... args) {
    LocalVariable time = var("time", long.class, call(System.class, "currentTimeMillis"));         
    LocalVariable result = var("result", getOrigReturnType(), callOrig());
    GlobalVariable out = defType(System.class).field("out");
    call(out, "println", sub(call(System.class, "currentTimeMillis"), time));
    return_(result);
}

其中getOrigReturnType()就是获取被修改方法返回类型。

修改构造方法

dummy.modifyConstructor(new Class[]{int.class, String.class}, new ModifiedMethodBody(){

    @Override
    public void body(LocalVariable... args) {
        //TODO
    }
 
});

和修改方法类似,但是没有传入方法名,因为是构造方法。但是在使用的时候注意,在修改构造方法的时候调用callOrig()是没有返回结果的。

修改静态程序块

dummy.modifyConstructor(new ModifiedMethodBody(){

    @Override
    public void body(LocalVariable... args) {
        //TODO
    }
 
});

和修改构造方法类似,但是没有传参数类型列表,同样调用callOrig()是没有返回结果的。

设置ClassLoader

我们内置ClassLoader来完成我们生成的class的字节数组到class对象,当然也允许自定义classloader通过下面的方法

dummy.setClassLoader(classloader);

设置class文件输出目录

dummy.setClassOutPutPath("../classes/");

设置这个属性,我们生成的class将会输出到指定目录,这样我们可以通过反编译大致看下class内容是否和预期相同。

开始创建

Class<?> clazz = dummy.build();   

#创建程序块

之前介绍过如何创建方法,通过DummyClass/DummyEmum/DummyModifiedClass的newMethod/newConstructor/newStaticBlock/modifyMethod/modifyConstructor/modifyConstructor方法编辑方法体。而我们创建方法体都是通过匿名类的方式,而我们希望创建的方法的逻辑内容就是通过实现匿名类的body方法,在该方法中完成。

if...else if...else

在body方法内通过调用CreateBlockAction的if_方法传入一个cn.wensiqun.asmsupport.client.IF的匿名类对象。如果下

@Override
public void body(LocalVariable... args) {
    if_(new IF(call("isTrue")){
        @Override
        public void body() {
            //Do if branch.
        }
    });
    return_();
}

上面代码对应的java代码如下:

if(isTrue()){
    //Do if branch.
} 

上述代码我们构造IF对象的时候传入了一个参数,这个参数就是当前if语句的条判断。if_放方法返回的是我们传入的IF对象。通过这个对象我们就能够添加else if分支和else分支。

@Override
public void body(LocalVariable... args) {
    if_(new IF(call("isTrue")){
        @Override
        public void body() {
            //Do if branch.
        }
    }).elseif(new ElseIF(call("isTrue")){
        @Override
        public void body() {
            //Do else...if branch.
        }
    });
    
    if_(new IF(call("isTrue")){
        @Override
        public void body() {
            //Do if branch.
        }
    }).else(new Else(){
        @Override
        public void body() {
            //Do else branch.
        }
    });
    return_();
}

上面代码对应的java代码如下:

if(isTrue()){
    //Do if branch.
} else if (isTrue()) {
    //Do else...if branch.
}

if(isTrue()){
    //Do if branch.
} else {
    //Do else branch.
}

上面我们通过调用IF对象的elseif方法和else方法引出了else..if分支和else分支,这个两个分支所产生的对象分别是cn.wensiqun.asmsupport.client.ElseIF和cn.wensiqun.asmsupport.client.Else, 构造ElseIF对象和构造IF类似同样要传入一个判断条件,而Else则没有。ElseIF对象继续可以展出else...if和else分支。而else则是终点站了。

@Override
public void body(LocalVariable... args) {
    if_(new IF(call("isTrue")){
        @Override
        public void body() {
            //Do if branch.
        }
    }).elseif(new ElseIF(call("isTrue")){
        @Override
        public void body() {
            //Do else...if branch.
        }
    }).elseif(new ElseIF(call("isTrue")){
        @Override
        public void body() {
            //Do else...if branch.
        }
    }).else(new Else(){
        @Override
        public void body() {
            //Do else branch.
        }
    });
    return_();
}

上面代码对应的java代码如下:

if(isTrue()){
    //Do if branch.
} else if (isTrue()) {
    //Do else...if branch.
} else if (isTrue()) {
    //Do else...if branch.
} else if {
    //Do else branch.
}

try...catch...finally

在body方法内通过调用CreateBlockAction的try_方法传入一个cn.wensiqun.asmsupport.client.Try的匿名类对象。 try_方法将会返回Try对象,我们同样通过该对象能够展出cn.wensiqun.asmsupport.client.Catch对象和cn.wensiqun.asmsupport.client.Finally对象。和if有所不同,Try对象至少要匹配一个Catch或Finally对象,否则将会报异常,这个和java语言一致的。

@Override
public void body(LocalVariable... args) {
    try_(new Try(){
        @Override
        public void body() {
            //Do try branch.
        }
    }).catch_(new Catch(RuntimeException.class){
        @Override
        public void body(LocalVariable e) {
            //Do catch block
        }
    }).catch_(new Catch(Exception.class){
        @Override
        public void body(LocalVariable e) {
            //Do catch block
        }
    }).finally_(new Finally(){
        @Override
        public void body() {
            //Do finally block
        }
    });;
    
    try_(new Try(){
        //Do try branch.
    }).finally_(new Finally(){
        @Override
        public void body() {
            //Do finally block
        }
    });
    return_();
}

上面的代码可以看出,和是使用java代码一样,通过Catch对象同样能够展出catch和finally分支。这里要注意下Catch类的body方法,该方法有一个参数"LocalVariable e", 这个参数就是捕获到的异常。上面的代码部分转换成java如下:

try{
    //Do try branch.
} catch (RuntimeException e) {
    // do catch block.
} catch (Exception e) {
    // do catch block.
} finally {
    //Do finally block
}

try{
    //Do try branch.
} finally {
    //Do finally block
}

do...while循环

在body方法内通过调用CreateBlockAction的dowhile方法传入一个cn.wensiqun.asmsupport.client.DoWhile的匿名类对象。如果下

@Override
public void body(LocalVariable... args) {
    dowhile(new DoWhile(call("isTrue")){
        @Override
        public void body(){
            //Do do..while block.
        }
    });
    return_();
}

对应java代码如下:

do {
    //Do do...while block
} while(isTrue())   

while循环

在body方法内通过调用CreateBlockAction的while_方法传入一个cn.wensiqun.asmsupport.client.While的匿名类对象,和do...while类似:

@Override
public void body(LocalVariable... args) {
    while_(new While(call("isTrue")){
        @Override
        public void body(){
            //Do while block.
        }
    });
    return_();
}

对应java代码如下:

while(isTrue()) {
    //Do do...while block
} 

for...each循环

在body方法内通过调用CreateBlockAction的foreach方法传入一个cn.wensiqun.asmsupport.client.ForEach的匿名类对象

@Override
public void body(LocalVariable... args) {
    foreach(new ForEach(arrayOrIterable, String.class){
        @Override
        public void body(LocalVariable ele){
            //Do for each loop
        }
    });
    return_();
}

通过上面代码可以看出,首先构造ForEach对象,这个对象传入两个参数,第一个是集合或是数组,第二个是该集合或者数组的每个元素的类型。ForEach的body方法有个参数,就是我们遍历的每一个数组元素,而这个数组元素我们强制转换成了String类型,就是构造方法的第二个参数所指代的类型。这样在body中我们就对每一个数组元素执行相应代码。

ForEach有三个构造方法

  • ForEach(ExplicitVariable iteratorVar) : 构造ForEach,使用默认的元素类型Object类型
  • ForEach(ExplicitVariable iteratorVar, AClass elementType) : 构造ForEach并且指代元素类型
  • ForEach(ExplicitVariable iteratorVar, Class elementType) : 构造ForEach并且指代元素类型

如果我们在上面的代码中使用第一个构造方法,那么在body方法内如果需要使用每个元素则先要强制转换如下:

@Override
public void body(LocalVariable... args) {
    foreach(new ForEach(arrayOrIterable){
        @Override
        public void body(LocalVariable ele){
            LocalVariable stringObj = var("stringObj", String.class, checkcast(ele, String.class))
            //Do for each loop
        }
    });
    return_();
}

所以建议使用后两个方法。上面的代码转换成java代码如下:

for(String ele : arrayOrIterable) {
    //Do for each loop
}

考虑到传统的while语句已经可以完成for循环的操作等其他因素,asmsupport并没有实现传统的for循环,后期考虑添加传统for循环。

synchronized块

相对来说synchronized比较简单,通过CreateBlockAction的sync方法完成。

@Override
public void body(LocalVariable... args) {
    sync(new Synchronized(lockObj){
        @Override
        public void body(Parameterized lock){
            //Do synchronized loop
        }
    });
    return_();
}

在构造Synchronized的时候需要指定一个同步对象。上述代码如下:

synchronized(lockObj) {
    //Do synchronized loop
}

转载于:https://my.oschina.net/wensiqun/blog/392238

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值