Java 编程的动态性,第 7 部分: 用 BCEL 设计字节码
2010-03-18 00:00:00 来源:WEB开发网您可能会奇怪为什么在开始参数大小计算时要检查方法是否是静态的,如果是静态的,将堆栈帧槽初始化为零(不是静态正好相反)。这种方式与 Java 如何处理方法调用有关。对于非静态的方法,每次调用的第一个(隐藏的)参数是目标对象的 this 引用,在计算堆栈帧中完整参数集大小时我要考虑到这点。
清单 4. 设置包装的调用
// compute the size of the calling parameters
Type[] types = methgen.getArgumentTypes();
int slot = methgen.isStatic() ? 0 : 1;
for (int i = 0; i < types.length; i++) {
slot += types[i].getSize();
}
// save time prior to invocation
ilist.append(ifact.createInvoke("java.lang.System",
"currentTimeMillis", Type.LONG, Type.NO_ARGS,
Constants.INVOKESTATIC));
ilist.append(InstructionFactory.createStore(Type.LONG, slot));
清单 5 显示了生成对包装方法的调用并保存结果(如果有的话)的代码。这段代码的第一部分再次检查方法是否是静态的。如果方法不是静态的,那么就生成将 this 对象引用装载到堆栈中的代码,同时设置方法调用类型为 virtual (而不是 static )。然后 for 循环生成将所有调用参数值拷贝到堆栈中的代码, createInvoke() 方法生成对包装的方法的实际调用,最后 if 语句将结果值保存到位于堆栈帧中的另一个局部变量中(如果结果类型不是 void )。
清单 5. 调用包装的方法
// call the wrapped method
int offset = 0;
short invoke = Constants.INVOKESTATIC;
if (!methgen.isStatic()) {
ilist.append(InstructionFactory.createLoad(Type.OBJECT, 0));
offset = 1;
invoke = Constants.INVOKEVIRTUAL;
}
for (int i = 0; i < types.length; i++) {
Type type = types[i];
ilist.append(InstructionFactory.createLoad(type, offset));
offset += type.getSize();
}
Type result = methgen.getReturnType();
ilist.append(ifact.createInvoke(cname,
iname, result, types, invoke));
// store result for return later
if (result != Type.VOID) {
ilist.append(InstructionFactory.createStore(result, slot+2));
}
更多精彩
赞助商链接