CheckClassAdapter 是否需要使用visitMaxs 的精确值?

问题描述

使用 ASM 创建类时,可以方便地将无意义的值传递给 visitMaxs

public static byte[] createClassNoAdapter() {
    var cw = new ClassWriter(ClassWriter.COmpuTE_FRAMES);

    cw.visit(Opcodes.V9,ACC_PUBLIC,"TestClass",null,"java/lang/Object",new String[0]);

    var mv = cw.visitMethod(ACC_PUBLIC,"test","()V",null);
    mv.visitCode();
    mv.visitInsn(Opcodes.RETURN);
    mv.visitMaxs(-1,-1);
    mv.visitEnd();

    return cw.toByteArray();
}

但是,我正在尝试诊断一些问题,并使用 CheckClassAdapter。如果我为 maxStackmaxLocals 明确传递正确的值,这会起作用。

public static byte[] createClassAdapter() {
    var cw = new ClassWriter(ClassWriter.COmpuTE_FRAMES);

    var cv = new CheckClassAdapter(cw);
    cv.visit(Opcodes.V9,"TestClass2",new String[0]);

    var mv = cv.visitMethod(ACC_PUBLIC,null);
    mv.visitCode();
    mv.visitInsn(Opcodes.RETURN);
    mv.visitMaxs(1,1);
    mv.visitEnd();

    return cw.toByteArray();
}

虽然这有效,但不太方便。调用 visitMaxs(-1,-1) 会导致错误

java.lang.IllegalArgumentException: Invalid max stack (must be an unsigned short): -1
    at org.objectweb.asm.util.CheckMethodAdapter.checkUnsignedShort(CheckMethodAdapter.java:1133)
at org.objectweb.asm.util.CheckMethodAdapter.visitMaxs(CheckMethodAdapter.java:1027)
at com.justinblank.strings.Example.createClassAdapter(Example.java:35)
at com.justinblank.strings.Example.main(Example.java:47)

代码清楚地表明您必须传递一个非负值,但传递 0 也不起作用:

java.lang.IllegalArgumentException: Data flow checking option requires valid,non zero maxLocals and maxStack.
    at org.objectweb.asm.util.CheckMethodAdapter$1.visitEnd(CheckMethodAdapter.java:456)
    at org.objectweb.asm.MethodVisitor.visitEnd(MethodVisitor.java:783)
    at org.objectweb.asm.util.CheckMethodAdapter.visitEnd(CheckMethodAdapter.java:1036)
    at com.justinblank.strings.Example.createClassAdapterZero(Example.java:51)
    at com.justinblank.strings.Example.main(Example.java:65)
Caused by: java.lang.indexoutofboundsexception: Trying to set an inexistant local variable 0
    at org.objectweb.asm.tree.analysis.Frame.setLocal(Frame.java:208)
    at org.objectweb.asm.tree.analysis.Analyzer.computeInitialFrame(Analyzer.java:466)
    at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:163)
    at org.objectweb.asm.util.CheckMethodAdapter$1.visitEnd(CheckMethodAdapter.java:453)
    ... 4 more

有什么方法可以避免在使用 CheckClassAdapter 时手动计算堆栈?

解决方法

看起来使用非常大的值是安全的。在几种情况下,以下值似乎对我有用:

public static byte[] createClassAdapterHacky() {
    var cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

    var cv = new CheckClassAdapter(cw);
    cv.visit(Opcodes.V9,ACC_PUBLIC,"TestClass3",null,"java/lang/Object",new String[0]);

    var mv = cv.visitMethod(ACC_PUBLIC,"test","()V",null);
    mv.visitCode();
    mv.visitMaxs(Short.MAX_VALUE * 2 + 1,Short.MAX_VALUE * 2 + 1);
    mv.visitEnd();

    return cw.toByteArray();
}

我不确定这是否是正确的解决方案,但它似乎有效。