Java虚拟机中的堆栈

Java虚拟机中的堆栈,java,constructor,stack,java-bytecode-asm,Java,Constructor,Stack,Java Bytecode Asm,我正在尝试使用ASM动态生成一个类。以下是我尝试过的: public class ByteArrayClassLoader extends ClassLoader{ public Class<?> defineClass(byte[] classData){ return defineClass(null, classData, 0, classData.length); } } 默认构造函数的帧似乎将操作数堆栈大小设置为0。但为什么会这样?我认为C

我正在尝试使用
ASM
动态生成一个类。以下是我尝试过的:

public class ByteArrayClassLoader extends ClassLoader{
    public Class<?> defineClass(byte[] classData){
        return defineClass(null, classData, 0, classData.length);
    }
}
默认构造函数的帧似乎将操作数堆栈大小设置为0。但为什么会这样?我认为
ClassWriter.COPUTE\u FRAMES
标志用于自动设置操作数堆栈/本地变量数组大小。据我所知,异常是由于
aload_0
指令将
加载到
对象的操作数堆栈上造成的。


无论如何,我尝试将其显式设置为
defaultCtor.visitFrame(F_NEW,2,NEW Object[]{UNINITIALIZED_THIS,UNINITIALIZED_THIS},2,NEW Object[]{UNINITIALIZED_THIS,UNINITIALIZED_THIS})但是得到了相同的错误。如何修复默认构造函数?

首先,您对堆栈映射帧的用途有误解。必须在分支合并点插入这些帧,才能在此时声明堆栈帧状态。由于构造函数不包含分支,因此根本不需要插入任何框架。此外,您对两个局部变量和两个操作数堆栈项的声明与构造函数中任何一点的实际情况都不匹配

但是ASM的
COMPUTE_FRAMES
也意味着(重新)计算局部变量和操作数堆栈的最大值。代码的问题在于,即使没有使用参数,您仍然必须调用相关的
visitMaxs
方法,以向ASM表明您已经完成了代码,并且需要计算值。我在这里使用
-1
作为参数,以明确参数不是实际值,但ASM需要重新计算它们:

String className = "HelloClass";
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classWriter.visit(V1_8, ACC_PUBLIC, className, null,
    getInternalName(Object.class), new String[]{getInternalName(IntToLongFunction.class)});
MethodVisitor defaultCtor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V",null,null);
defaultCtor.visitVarInsn(ALOAD, 0);
defaultCtor.visitMethodInsn(INVOKESPECIAL,
                            getInternalName(Object.class), "<init>", "()V", false);
defaultCtor.visitInsn(RETURN);
defaultCtor.visitMaxs(-1, -1);
defaultCtor.visitEnd();
return new ByteArrayClassLoader().defineClass(classWriter.toByteArray());
Exception in thread "main" java.lang.VerifyError: Operand stack overflow
Exception Details:
  Location:
    HelloClass.<init>()V @0: aload_0
  Reason:
    Exceeded max stack size.
  Current Frame:
    bci: @0
    flags: { flagThisUninit }
    locals: { uninitializedThis }
    stack: { }
  Bytecode:
    0x0000000: 2ab7 000a b1                           

    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
    at java.lang.Class.getConstructor0(Class.java:3075)
    at java.lang.Class.newInstance(Class.java:412)
    at Tetst.main(Tetst.java:12)
String className = "HelloClass";
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classWriter.visit(V1_8, ACC_PUBLIC, className, null,
    getInternalName(Object.class), new String[]{getInternalName(IntToLongFunction.class)});
MethodVisitor defaultCtor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V",null,null);
defaultCtor.visitVarInsn(ALOAD, 0);
defaultCtor.visitMethodInsn(INVOKESPECIAL,
                            getInternalName(Object.class), "<init>", "()V", false);
defaultCtor.visitInsn(RETURN);
defaultCtor.visitMaxs(-1, -1);
defaultCtor.visitEnd();
return new ByteArrayClassLoader().defineClass(classWriter.toByteArray());
public static void main(String[] args) throws Throwable {
    IntToLongFunction i2l = (IntToLongFunction) getKlass().newInstance();
    System.out.println(i2l.applyAsLong(10));
}
public static Class<?> getKlass(){
    String className = "HelloClass";
    ClassWriter classWriter = new ClassWriter(0);
    classWriter.visit(V1_8, ACC_PUBLIC, className, null, getInternalName(Object.class),
                      new String[] { getInternalName(IntToLongFunction.class) } );
    MethodVisitor defaultCtor=classWriter.visitMethod(ACC_PUBLIC,"<init>","()V",null,null);
    defaultCtor.visitVarInsn(ALOAD, 0);
    defaultCtor.visitMethodInsn(INVOKESPECIAL,
                                getInternalName(Object.class), "<init>", "()V", false);
    defaultCtor.visitInsn(RETURN);
    defaultCtor.visitMaxs(1, 1);
    defaultCtor.visitEnd();
    MethodVisitor applyAsLong = classWriter.visitMethod(
                                    ACC_PUBLIC, "applyAsLong", "(I)J",null,null);
    applyAsLong.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
    applyAsLong.visitLdcInsn("hello generated code"); // stack [PrintStream,String]
    applyAsLong.visitMethodInsn(INVOKEVIRTUAL,
                    "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    applyAsLong.visitVarInsn(ILOAD, 1); // stack [int]
    applyAsLong.visitInsn(I2L); // stack [long,*]
    applyAsLong.visitInsn(LRETURN);
    applyAsLong.visitMaxs(2, 2);// max stack see above, vars: [this,arg1:int]
    applyAsLong.visitEnd();
    return new ByteArrayClassLoader().defineClass(classWriter.toByteArray());
}