ASM复制方法

时间:2018-10-02 12:17:48

标签: java-bytecode-asm

我正在尝试删除所有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都是自定义类。让我知道,如果您也希望我提供它们。

1 个答案:

答案 0 :(得分:0)

您应该熟悉Visitor pattern

声明

cr.accept(cm, ClassReader.EXPAND_FRAMES);

已经完成了全部工作。 ClassReader将在指定的visit…上调用所有ClassTransformer方法,当遵循此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的构造函数。对于这样的用例,只需进行很小的更改,就可以优化代码生成。