ASM 重复方法

ASM duplicate methods

我正在尝试删除所有 System#exit 调用,它似乎可以正常工作,但我最终遇到了重复的方法。

我的代码如下所示:

    JarInputStream jis = new JarInputStream(new FileInputStream(new File(input)));
    JarOutputStream jos = new JarOutputStream(new FileOutputStream(new File(output)));

    byte[] buffer = new byte[4096];

    JarEntry entry;

    while ((entry = jis.getNextJarEntry()) != null) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int size;

        while ((size = jis.read(buffer)) != -1) {
            bos.write(buffer, 0, size);
        }

        bos.close();

        jos.putNextEntry(new JarEntry(entry.getName()));

        if (entry.getName().endsWith(".class")) {
            ClassReader cr = new ClassReader(bos.toByteArray());
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

            ClassTransformer cm = new ClassTransformer(cw);

            ClassNode cn = new ClassNode();
            cr.accept(cn, 0);

            System.out.println("Analyzing bytecode in class " + cn.name);

            List<MethodNode> methods = cn.methods;

            for (MethodNode method : methods) {
                System.out.println("Analyzing bytecode in method " + method.name);

                String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);

                cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
            }

            cr.accept(cm, ClassReader.EXPAND_FRAMES);
            jos.write(cw.toByteArray());
        } else {
            jos.write(bos.toByteArray());
        }
    }

    jos.close();
    jis.close();

ClassTransformer 和 MethodTransformer 都是自定义的 类。如果你想让我也提供它们,请告诉我。

您应该熟悉 Visitor pattern

声明

cr.accept(cm, ClassReader.EXPAND_FRAMES);

已经完成了全部工作。 ClassReader 将在指定的 ClassTransformer 上调用所有 visit… 方法,这将在遵循此 API 的标准编程模型时对 ClassWriter 进行所有必要的委托.

换句话说,整个区块

ClassNode cn = new ClassNode();
cr.accept(cn, 0);

System.out.println("Analyzing bytecode in class " + cn.name);

List<MethodNode> methods = cn.methods;

for (MethodNode method : methods) {
    System.out.println("Analyzing bytecode in method " + method.name);

    String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);

    cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
}

已过时,因为它循环遍历所有方法并为每个方法调用 visitMethod 除了已经完成整个工作的语句之外,所以每个方法两次都不足为奇。

所以只需删除该代码块。

嗯,当然,你可以保留打印语句...


顺便说一句,您可以通过简单地将 JarInputStream 传递给 ClassReader(InputStream) 构造函数来进一步简化您的代码,而无需先复制到 ByteArrayOutputStream

您的代码的清理版本如下所示:

try(JarInputStream jis = new JarInputStream(new FileInputStream(input));
    JarOutputStream jos = new JarOutputStream(new FileOutputStream(output)) ) {

    JarEntry entry;
    while((entry = jis.getNextJarEntry()) != null) {
        jos.putNextEntry(new JarEntry(entry.getName()));

        if (entry.getName().endsWith(".class")) {
            ClassReader cr = new ClassReader(jis);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
            ClassTransformer cm = new ClassTransformer(cw);
            System.out.println("Analyzing bytecode in class " + cr.getClassName());
            cr.accept(cm, ClassReader.EXPAND_FRAMES);
            jos.write(cw.toByteArray());
        } else {
            jis.transferTo(jos); // Java 9
            /* before Java 9:
            byte[] buffer = new byte[8192];
            for(int read; (read = jis.read(buffer, 0, buffer.length)) >= 0; )
                jos.write(buffer, 0, read);
            */
        }
    }
}

请注意,我还将 ClassReader 传递给了 ClassWriter 的构造函数。对于像这样的用例,只进行了很小的更改,这允许优化代码生成。