Как изменить постоянный пул с помощью ASM?

Я уже понимаю, как манипулировать классом во время выполнения с помощью ASM из этот пост< /а>.

Но у меня есть еще вопрос о том, как изменить постоянный пул. Ниже приведен пример программы Java, которую я хочу изменить.

Основной JAR-файл:

    public class test {    
    private static final String a = "Hello World";
    private static final String b = "ASM is awasome";

    public static void main(String[] args) { 
         int x = 10;
         int y = 25;
         int z = x * y;
         System.out.println(a);
         System.out.println(z);
         System.out.println(b);

    }

}

Я хочу изменить переменную a с "Hello World" на "Multiply Of x*y is: "

Мой класс агентов

import java.lang.instrument.*;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;

public class ExampleAgent implements ClassFileTransformer {
    private static final String TRANSFORM_CLASS = "src/test";
    private static final String TRANSFORM_METHOD_NAME = "main";
    private static final String TRANSFORM_METHOD_DESC = "([Ljava/lang/String;)V";

    public static void premain(String arg, Instrumentation instrumentation) {


        instrumentation.addTransformer(new ExampleAgent());

    }

    public byte[] transform(ClassLoader loader, String className, Class<?> cl,
                            ProtectionDomain pd, byte[] classfileBuffer) {
        if(!TRANSFORM_CLASS.equals(className)) return null;

        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(cr, 0);
        cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc,
                                             String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(
                                       access, name, desc, signature, exceptions);


                //Code to modify the value of a




                return mv;
            }
        }, 0);

        return cw.toByteArray();

    }
}

Результат в окне консоли должен быть

Multiply Of x*y is: 
250
ASM is awasome

person LEON    schedule 23.03.2018    source источник


Ответы (1)


Как сказано в этом комментарии, с ASM вы не вообще не иметь дело с постоянным пулом; вы имеете дело только с фактическим использованием констант(ы).

Что касается вашего примера, вы должны знать о том факте, что объявление типа
… final String a = "Hello World"; является константой времени компиляции. Следовательно, будет ConstantValue атрибут в поле, описывающем значение, но каждый доступ для чтения к константе будет заменен уже во время компиляции.

Таким образом, чтобы «изменить a» эффективно, вы должны заменить каждое фактическое использование постоянного значения, но вы должны знать, что вы не можете распознать, действительно ли вхождение значения связано с доступом к полю a или просто другим использование одного и того же постоянного значения.

Использование, как в вашем примере кода, легко изменить; вам нужно только найти ldc инструкции. Замена объявления постоянного значения самого поля также довольно проста. Более сложной будет замена констант в аннотациях, но действительно сложной будет замена использования константы в качестве метки case оператора switch, поскольку логика программы нарушится, если вы просто замените константу в таком сценарии. Вам придется переписать больше кода, чтобы переключение строки работало с другой константой.

Сосредоточив внимание только на более простых задачах, код преобразования становится таким:

public byte[] transform(ClassLoader loader, String className, Class<?> cl,
                        ProtectionDomain pd, byte[] classfileBuffer) {
    if(!TRANSFORM_CLASS.equals(className)) return null;

    ClassReader cr = new ClassReader(classfileBuffer);
    ClassWriter cw = new ClassWriter(cr, 0);
    cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
        @Override
        public FieldVisitor visitField(int access, String name, String desc,
                                       String signature, Object cst) {
            if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
            return super.visitField(access, name, desc, signature, cst);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                                         String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(
                                   access, name, desc, signature, exceptions);
            if(name.equals(TRANSFORM_METHOD_NAME)
            && desc.equals(TRANSFORM_METHOD_DESC)) {
                return new MethodVisitor(Opcodes.ASM5, mv) {
                    @Override
                    public void visitLdcInsn(Object cst) {
                        if("Hello World".equals(cst)) cst = "Multiply Of x*y is: ";
                        super.visitLdcInsn(cst);
                    }
                };
            }
            return mv;
        }
    }, 0);
    return cw.toByteArray();
}
person Holger    schedule 23.03.2018
comment
Опять же идеальное решение, вы отличный учитель. Вы не только предоставили решение, но и указали на то, что может возникнуть другая проблема. Спасибо за добрый ответ. Надеюсь, я буду иметь достаточно привилегии, чтобы учиться у вас в будущем. - person LEON; 24.03.2018