在这一节中将会介绍java的基本操作在asmsupport的实现。在介绍操作之前,我们首先确定我们有哪些基本的操作,可以参考下表
expr++ expr--
++expr --expr +expr -expr ~ !
* / %
+ -
<<,>>,>>>
<,>,<=,>=,instanceof
==,!=
&
^
|
&&
||
? :
=,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>=,>>>=
除了上述的操作,还有一下操作
我们先介绍三个基础的操作1。获取常量值,2.获取AClass对象,3.创建变量,然后再按照上述顺序自上而下介绍。我们将会使用asmsupport先创建一个类OperatorTest,同时在这个类中添加一个main方法。在这里我们会提前使用到如何创建一个class或者method的知识,但是这并不是本节的重点,创建class/enum/interface等将在后面做详细介绍。生产的类和方法大致如下:
public class OperatorTest {
public static void main(String[] args) {
//我们将用asmsupport在这里位置生产我们期望的内容
}
}
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这些操作主要通过两种方式获得。
直接通过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类型的
如果你的代码已经在一个程序块中了,正如我们的实例,我们的代码在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")});
该接口下还有很多重载方法。
通常我们在使用asmsupport的时候,很多方法都直接使用Class对象即可,但是还有部分操作是要通过AClass完成的,比如获取静态field,和常量值一样,这里同意有两种方式获取AClass。
通过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")}););
这个两个操作是通过CrementAction接口完成,该接口是完成自增减操作。
1. LocalVariable i = var("i", int.class, val(1));
2. postinc(i); //i++
3. postdec(i); //i--
这里的操作都是一元操作,其中++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
这组的操作关系操作,其中这一组(<,>,<=,>=)是使用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。和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是都是默认具有public static final修饰的。通过下面代码创建field
DummyField field = dummy.newField(String.class, "FIELD");
上面的代码为接口创建了FIELD的field。等同于"public static final String FIELD”,如果仅仅值调用了上面的代码,那么FIELD的值就是null。有两种方式为.
上面看到了newField返回的是DummyField对象。可以通过这个对象的一系列重载方法val()为其赋值。但是这样只允许根据field类型为其赋予基本的值。比如上面我们为FIELD赋值为"asmsupport”
field.val("ASMSupport").
如果希望类型通过方法调用的方式赋值比如下面的代码
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。
我们会根据当前使用的JDK版本自动选择class的版本,但是也可以显式的设置class版本,通过下面的方式
dummy.setJavaVersion(Opcodes.V1_5)
我们内置ClassLoader来完成我们生成的class的字节数组到class对象,当然也允许自定义classloader通过下面的方法
dummy.setClassLoader(classloader);
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_();
dummy.static_();
dummy.extends_(ArrayList.class);
dummy.implements_(List.class, Cloneable.class);
DummyField field = dummy.newField(String.class, "FIELD");
上面的代码为类创建了名为FIELD的字段。默认情况下使用的是默认的修饰符,上面的代码生成的对应java代码如下:
String FIElD;
field.name("fieldName");
通过这个方法可以重新设置field名
field.type(int.class);
通过这个方法可以重新设置field的类型
默认为default。
field.public_();
field.protected_();
field.default_();
field.private_();
field.transient_(); //增加transient修饰
field.static_(); //设置为static
field.volatile_(); //增加volatile修饰
field.final_(); //增加final修饰
上面看到了newField返回的是DummyField对象。可以通过这个对象的一系列重载方法val()为其赋值。但是这样只允许根据field类型为其赋予基本的值。比如上面我们为FIELD赋值为"asmsupport”
field.val("ASMSupport").
如果希望类型通过方法调用的方式赋值比如下面的代码
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。
我们会根据当前使用的JDK版本自动选择class的版本,但是也可以显式的设置class版本,通过下面的方式
dummy.setJavaVersion(Opcodes.V1_5)
我们内置ClassLoader来完成我们生成的class的字节数组到class对象,当然也允许自定义classloader通过下面的方法
dummy.setClassLoader(classloader);
dummy.setClassOutPutPath("../classes/");
设置这个属性,我们生成的class将会输出到指定目录,这样我们可以通过反编译大致看下class内容是否和预期相同。
Class<?> clazz = dummy.build();
使用DummyModifiedClass修改class
1. DummyModifiedClass dummy = new DummyModifiedClass(A.class);
传入的A.class为希望修改的类
在被修改类中新建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来完成我们生成的class的字节数组到class对象,当然也允许自定义classloader通过下面的方法
dummy.setClassLoader(classloader);
dummy.setClassOutPutPath("../classes/");
设置这个属性,我们生成的class将会输出到指定目录,这样我们可以通过反编译大致看下class内容是否和预期相同。
Class<?> clazz = dummy.build();
之前介绍过如何创建方法,通过DummyClass/DummyEmum/DummyModifiedClass的newMethod/newConstructor/newStaticBlock/modifyMethod/modifyConstructor/modifyConstructor方法编辑方法体。而我们创建方法体都是通过匿名类的方式,而我们希望创建的方法的逻辑内容就是通过实现匿名类的body方法,在该方法中完成。
在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.
}
在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
}
在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())
在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
}
在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有三个构造方法
如果我们在上面的代码中使用第一个构造方法,那么在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比较简单,通过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
}