作者:昧凉生丶楚_139 | 来源:互联网 | 2023-05-18 14:42
我使用ASM库生成字节码,并且方法的"最大堆栈大小"将自动计算.在运行期间,我发现此值(最大堆栈大小)不正确.
我的源代码是:
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
....
MethodType initType = MethodType.methodType(void.class, clsList);
mv = cw.visitMethod(ACC_PUBLIC, "", initType.toMethodDescriptorString(), null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/BaseTemplate", "", "()V", false);
for(int i=0; i expClass =defineClass(..);
上面的代码将生成字节码:
Classfile /C:/temp/TGWD.class
Last modified Mar 11, 2015; size 403 bytes
MD5 checksum f58b96ad4cb0bc9e62f2ae5e11e63e90
public class TGWD extends java.lang.invoke.BaseTemplate
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Utf8 TGWD
#2 = Class #1 // TGWD
#3 = Utf8 java/lang/invoke/BaseTemplate
#4 = Class #3 // java/lang/invoke/BaseTemplate
#5 = Utf8 guard
#6 = Utf8 Ljava/lang/invoke/MethodHandle;
#7 = Utf8 trueTarget
#8 = Utf8 falseTarget
#9 = Utf8
#10 = Utf8 (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V
#11 = Utf8 ()V
#12 = NameAndType #9:#11 // "":()V
#13 = Methodref #4.#12 // java/lang/invoke/BaseTemplate."":()V
#14 = NameAndType #5:#6 // guard:Ljava/lang/invoke/MethodHandle;
#15 = Fieldref #2.#14 // TGWD.guard:Ljava/lang/invoke/MethodHandle;
#16 = NameAndType #7:#6 // trueTarget:Ljava/lang/invoke/MethodHandle;
#17 = Fieldref #2.#16 // TGWD.trueTarget:Ljava/lang/invoke/MethodHandle;
#18 = NameAndType #8:#6 // falseTarget:Ljava/lang/invoke/MethodHandle;
#19 = Fieldref #2.#18 // TGWD.falseTarget:Ljava/lang/invoke/MethodHandle;
#20 = Utf8 eval
#21 = Utf8 Code
{
final java.lang.invoke.MethodHandle guard;
flags: ACC_FINAL
final java.lang.invoke.MethodHandle trueTarget;
flags: ACC_FINAL
final java.lang.invoke.MethodHandle falseTarget;
flags: ACC_FINAL
public TGWD(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
flags: ACC_PUBLIC
Code:
stack=0, locals=4, args_size=4
0: aload_0
1: invokespecial #13 // Method java/lang/invoke/BaseTemplate."":()V
4: aload_0
5: aload_1
6: putfield #15 // Field guard:Ljava/lang/invoke/MethodHandle;
9: aload_0
10: aload_2
11: putfield #17 // Field trueTarget:Ljava/lang/invoke/MethodHandle;
14: aload_0
15: aload_3
16: putfield #19 // Field falseTarget:Ljava/lang/invoke/MethodHandle;
19: return
public void eval();
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
}
字节码报告错误:
org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 0: Insufficient maximum stack size.
at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source)
at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source)
因为构造方法:stack = 0,locals = 4,args_size = 4
正确的堆栈大小为2.
即使设置了ClassWriter(COMPUTE_MAX + COMPUTE_STACK),还有另一个线程ASM(来自ObjectWeb)没有正确计算MaxStack,表明如果其他地方的字节码无效,则可能错误地计算最大堆栈大小.
所以对我来说,问题是:
生成的文件中的无效字节码在哪里?
我仍然希望避免调用visitMax().因为有许多生成的字节码方法,并且这些值的手动计算并不是一件容易的事.
Holger..
9
你不能省略对它的调用visitMax
.来自以下文件ClassWriter.COMPUTE_MAXS
:
如果该标志被设置,则自变量visitMaxs所述的方法MethodVisitor中由返回visitMethod方法将被忽略,并且从签名和每个方法的字节码自动计算.
换句话说,当您指定标志时,您可以传入任何您想要的内容,例如调用visitMax(-1,-1)
以强调您没有提供实际值,但您仍然必须调用该方法来触发正确值的计算.
顺便说一句,既然你创建了一个版本的类文件,51
你应该指定COMPUTE_FRAMES
我怀疑你想要StackMapTable
手动创建属性.注意这COMPUTE_FRAMES
意味着COMPUTE_MAXS
行为.
1> Holger..:
你不能省略对它的调用visitMax
.来自以下文件ClassWriter.COMPUTE_MAXS
:
如果该标志被设置,则自变量visitMaxs所述的方法MethodVisitor中由返回visitMethod方法将被忽略,并且从签名和每个方法的字节码自动计算.
换句话说,当您指定标志时,您可以传入任何您想要的内容,例如调用visitMax(-1,-1)
以强调您没有提供实际值,但您仍然必须调用该方法来触发正确值的计算.
顺便说一句,既然你创建了一个版本的类文件,51
你应该指定COMPUTE_FRAMES
我怀疑你想要StackMapTable
手动创建属性.注意这COMPUTE_FRAMES
意味着COMPUTE_MAXS
行为.