ASM дубликаты методов - PullRequest
       8

ASM дубликаты методов

0 голосов
/ 02 октября 2018

Я пытаюсь удалить все вызовы выхода из System #, и, похоже, он работает, но я получаю дубликаты методов.

Мой код выглядит так:

    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 голосов
/ 02 октября 2018

Вам следует ознакомиться с шаблоном Visitor .

Оператор

cr.accept(cm, ClassReader.EXPAND_FRAMES);

уже выполняет всю работу.ClassReader вызовет весь метод visit… для указанного ClassTransformer, который будет выполнять все необходимое делегирование ClassWriter при следовании стандартной модели программирования этого API.

Другими словами, этоВесь блок

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.Для таких случаев использования, когда вносятся только небольшие изменения, это позволяет оптимизировать генерацию кода.

...