不兼容的魔法值 0 ASM

Posted

技术标签:

【中文标题】不兼容的魔法值 0 ASM【英文标题】:Incompatible magic value 0 ASM 【发布时间】:2022-01-20 06:46:36 【问题描述】:

我有一个使用 ASM 核心 API 按以下方式生成的 .class 文件:

public void createEmptyClassWithinPackage(String packageName, String className)
    /* Creating ClassWriter object that creates class in bytecode representation
    *  Flag 0 - no need in computation of stack size and generation of frames */
    ClassWriter classWriter = new ClassWriter(0);
    ClassVisitor classVisitor = new ClassVisitor(ASM4, classWriter)  ;

    // Visiting class declaration: version, access, name, generic, super, interface
    classVisitor.visit(V9, ACC_PUBLIC, packageName + className, null,
            Type.getInternalName(Object.class), null);

    // Indicate that generation of class is done and get byte array
    classVisitor.visitEnd();
    classWriter.visitEnd();
    byte[] bytesOfClassToWrite = classWriter.toByteArray();

    checkClassPackage(packageName);
    writeClassToPackage(packageName, className, bytesOfClassToWrite);

最后的方法只是检查目录并将.class文件写入其中。 稍后当我想在我正在使用的类中添加一些东西时:

public void writeEmptyConstructor(String packageName, String className)
    /* Read generated class form package and generate empty constructor */
    byte[] bytesOfClassToRead = readClassFromPackage(packageName, className);

    ClassReader classReader = new ClassReader(bytesOfClassToRead);
    ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
    ClassVisitor classVisitor = new ClassVisitor(ASM4, classWriter)  ;
    classReader.accept(classVisitor, 0);

    generateDefaultConstructor(classVisitor);

    // Indicate that generation of constructor is done and get byte array
    classVisitor.visitEnd();
    classWriter.visitEnd();
    byte[] bytesOfClassToWrite = classWriter.toByteArray();

    writeClassToPackage(packageName, className, bytesOfClassToWrite);

当我尝试使用 ClassLoader 读取 Class 时出现问题:

File file = new File("result/classes/TestClass.class");
try(FileInputStream fileInputStream = new FileInputStream(file)) 
    byte[] classByteArray = new byte[(int) file.length()];
    GeneratedClassLoader generatedClassLoader = new GeneratedClassLoader();
    Class c = generatedClassLoader.defineClass("result.classes.TestClass", classByteArray);
 catch (FileNotFoundException e) 
    e.printStackTrace();
 catch (IOException e) 
    e.printStackTrace();

上面的代码报错:

Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 0 in class file result/classes/TestClass
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:878)
    at callers.GeneratedClassLoader.defineClass(GeneratedClassLoader.java:5)
    at Main.main(Main.java:28)

但是当我尝试使用 ClassLoader 而不在磁盘上写入字节数组时,它工作得非常好:

    public void createEmptyClassWithinPackage(String packageName, String className)
        /* Creating ClassWriter object that creates class in bytecode representation
        *  Flag 0 - no need in computation of stack size and generation of frames */
        ClassWriter classWriter = new ClassWriter(0);
        ClassVisitor classVisitor = new ClassVisitor(ASM4, classWriter)  ;

        // Visiting class declaration: version, access, name, generic, super, interface
        classVisitor.visit(V9, ACC_PUBLIC, packageName + className, null,
                Type.getInternalName(Object.class), null);

        // Indicate that generation of class is done and get byte array
        classVisitor.visitEnd();
        classWriter.visitEnd();
        byte[] bytesOfClassToWrite = classWriter.toByteArray();

        GeneratedClassLoader generatedClassLoader = new GeneratedClassLoader();
        Class c = generatedClassLoader.defineClass((packageName + className).replace("/", ".")
                , bytesOfClassToWrite);

//        checkClassPackage(packageName);
//        writeClassToPackage(packageName, className, bytesOfClassToWrite);
    

我的问题是:当我从光盘读取课程时,魔法值会发生什么?可能是我不应该使用 ClassLoader 并使用其他东西从光盘中读取 .class 文件作为字节数组?

【问题讨论】:

ClassWriter extends ClassVisitor。此处无需创建匿名类。 感谢您的评论。我这样做是为了保持一致性。以后可能会删除它。 【参考方案1】:

解决方法很简单,忘记写了:

fileInputStream.read(classByteArray);

我不会删除这篇文章,希望有人会觉得它有用。

【讨论】:

那么您应该将此答案标记为已接受,以便人们可以看到您的问题已解决。 感谢您的评论。我必须等待 2 天才能将自己的解决方案标记为已接受。 byte[] classByteArray = Files.readAllBytes(Paths.get("result/classes/TestClass.class"));简化你的代码,就不会再遇到这样的问题了……

以上是关于不兼容的魔法值 0 ASM的主要内容,如果未能解决你的问题,请参考以下文章

使用 Restkit 0.20 映射此类与键的键值编码不兼容

11G新特性 -- ASM的兼容性

asm 兼容性asm 主要参数管理

此类与键 XXXXXX 的键值编码不兼容

ValueError:层顺序的输入 0 与层不兼容:输入形状的预期轴 -1 具有值 3,但接收到的输入具有形状

DNN 中的错误:层序贯_10 的输入 0 与层不兼容