您可以简单地指定
COMPUTE_FRAMES
到
ClassWriter
–s构造函数,让ASM同时计算最大堆栈和;局部变量和堆栈映射表框架项。正如文档中所述
–computeFrames意味着computeMaxs
。
但是,我始终建议尝试理解堆栈映射,因为从头开始的计算不仅成本高昂,而且还有一些基本限制(如中所述
this answer
)。既然您应该已经知道堆栈框架应该是什么样子,那么对这些知识进行编码应该不会太难。由于这还意味着知道局部变量和操作数堆栈项的最大数量,因此手动指定它们也是一致的。
然而,您尝试的解决方案还远远不够:
mv.visitFrame(F_APPEND, 0, new Object[]{ }, 0, new Object[]{ trueLabel });
F_APPEND
意味着新的
变量
已添加,这与您添加
堆栈
进入此外,只有在引用
NEW
指令,表示未初始化的对象。但在这里,你推了一个
INTEGER
价值
正确的代码如下所示:
String className = "TestClass";
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC, className, null, getInternalName(Object.class), null);
MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()Z",null, null);
Label trueLabel = new Label();
Label afterFalseLabel = new Label();
mv.visitFieldInsn(GETSTATIC, getInternalName(Boolean.class),"TRUE","Ljava/lang/Boolean;");
mv.visitMethodInsn(INVOKEVIRTUAL,getInternalName(Boolean.class),"booleanValue","()Z",false);
mv.visitJumpInsn(IFEQ, trueLabel);
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, afterFalseLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitLabel(trueLabel);
mv.visitInsn(ICONST_0);
mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{ INTEGER });
mv.visitLabel(afterFalseLabel);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
请注意,对于特殊的压缩帧类型,大多数参数都是隐含的
F_SAME
,所有其他参数都是无关的,因为
F_SAME1
,只有最后一个参数中指定的新堆栈条目类型才重要。
但您不需要处理不同类型的压缩帧。如果有疑问,您可以随时指定
F_NEW
完整描述假定的堆栈框架布局。唯一的区别是类文件(稍微)更大。对于动态类生成,这可能是完全不相关的,甚至对于在部署之前添加到应用程序中的生成类,差异也可以忽略不计:
String className = "TestClass";
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC, className, null, getInternalName(Object.class), null);
MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()Z",null, null);
Label trueLabel = new Label();
Label afterFalseLabel = new Label();
mv.visitFieldInsn(GETSTATIC, getInternalName(Boolean.class),"TRUE","Ljava/lang/Boolean;");
mv.visitMethodInsn(INVOKEVIRTUAL,getInternalName(Boolean.class),"booleanValue","()Z",false);
mv.visitJumpInsn(IFEQ, trueLabel);
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, afterFalseLabel);
mv.visitFrame(F_NEW, 0, null, 0, null);
mv.visitLabel(trueLabel);
mv.visitInsn(ICONST_0);
mv.visitFrame(F_NEW, 0, null, 1, new Object[]{ INTEGER });
mv.visitLabel(afterFalseLabel);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
顺便说一下,我发现结合抽象名称生成有点奇怪,比如
getInternalName( Boolean.class)
,具有硬编码签名,如
"Ljava/lang/Boolean;"
. 两者都是有效的,但最好是一致地决定哪种方式。