Изменение кода с помощью javassist не имеет никакого эффекта (MethodCall) - PullRequest
0 голосов
/ 04 ноября 2019

У меня есть эта простая функция:

 public int id() {
    return 0;
}

У меня есть эта тестовая функция:

void test() {
    int a = id();
    int b = id();
    int c = id();
    int d = id();
    int e = id();
    int f = id();
    System.out.println(a+" "+b+" "+c+" "+d+" "+e+" "+f);
}

Я бы хотел, чтобы вывод был 1, 2, 3, 4, 5, 6;

Прямо сейчас я звоню instrument на CtMethod, который работает нормально.

call to id! on line: 57
call to id! on line: 58
call to id! on line: 59
call to id! on line: 60
call to id! on line: 61
call to id! on line: 62

Но в итоге ни одно из преобразований не имеет никакого эффекта. Я понятия не имею, что делать, поскольку там так мало информации.

Вот полный код:

package doeke.method_call_test;


import java.lang.instrument.Instrumentation;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;


public class MethodCallTest {


    static int id = 1;


    public static void premain(String agentArgs, Instrumentation inst) {

        try {

            ClassPool classPool = ClassPool.getDefault();
            CtClass ctClass = classPool.getCtClass("doeke.method_call_test.MethodCallTest");
            CtMethod[] methods = ctClass.getDeclaredMethods();

            for (CtMethod cm : methods) {
                cm.instrument(
                    new ExprEditor() {
                        public void edit(MethodCall m) throws CannotCompileException {
                            if (m.getMethodName().equals("id")) {

                                // m.replace("{ $_ = "+id+"; }");
                                m.replace("$_ = 1; System.out.println(\"hello?\");");

                                System.out.println("call to id! on line: "+m.getLineNumber());

                                id++;
                            }
                        }
                    }
                );
            }

            inst.retransformClasses(MethodCallTest.class);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
            MethodCallTest mct = new MethodCallTest();
            mct.test();
    }


    void test() {
        int a = id();
        int b = id();
        int c = id();
        int d = id();
        int e = id();
        int f = id();
        System.out.println(a+" "+b+" "+c+" "+d+" "+e+" "+f);
    }



    public int id() {
        return 0;
    }





}

1 Ответ

0 голосов
/ 04 ноября 2019

Не уверен, как вы выполняете код, поскольку обычно вам нужно запустить JVM с javaagent в командной строке, чтобы внедрить экземпляр Instrumentation . В моем примере ниже я использую ByteBuddy для получения экземпляра Instrumentation во время выполнения.

Вы также должны создать ClassFileTransformer , который возвращает байт-код измененного класса, который вы можете приобрести, позвонив по номеру CtClass.toByteCode()

. Код, приведенный ниже, печатает этот вывод:

call to id! on line: 84
call to id! on line: 85
call to id! on line: 86
call to id! on line: 87
call to id! on line: 88
call to id! on line: 89
Transforming [doeke/method_call_test/MethodCallTest]
hello?
hello?
hello?
hello?
hello?
hello?
1 1 1 1 1 1

Основной код:

package doeke.method_call_test;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import net.bytebuddy.agent.ByteBuddyAgent;

public class MethodCallTest {



    static int id = 1;


    public static void premain(String agentArgs, Instrumentation inst) {

        try {

            ClassPool classPool = ClassPool.getDefault();
            CtClass ctClass = classPool.getCtClass("doeke.method_call_test.MethodCallTest");
            CtMethod[] methods = ctClass.getDeclaredMethods();

            for (CtMethod cm : methods) {
                cm.instrument(
                    new ExprEditor() {
                        public void edit(MethodCall m) throws CannotCompileException {
                            if (m.getMethodName().equals("id")) {

                                // m.replace("{ $_ = "+id+"; }");
                                m.replace("$_ = 1; System.out.println(\"hello?\");");

                                System.out.println("call to id! on line: "+m.getLineNumber());

                                id++;
                            }
                        }
                    }
                );
            }
            final byte[] byteCode = ctClass.toBytecode();
            final String rezName = MethodCallTest.class.getName().replace('.', '/');
            final ClassFileTransformer transformer = new ClassFileTransformer() {

                @Override
                public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                    if (rezName.equals(className)) {
                        System.out.println("Transforming [" + className + "]");
                        return byteCode;
                    }
                    return null;
                }

            };
            inst.addTransformer(transformer, true);
            try {
                inst.retransformClasses(MethodCallTest.class);
            } finally {
                inst.removeTransformer(transformer);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
            MethodCallTest mct = new MethodCallTest();
            Instrumentation inst = ByteBuddyAgent.install();
            MethodCallTest.premain("", inst);
            mct.test();
    }


    void test() {
        int a = id();
        int b = id();
        int c = id();
        int d = id();
        int e = id();
        int f = id();
        System.out.println(a+" "+b+" "+c+" "+d+" "+e+" "+f);
    }



    public int id() {
        return 0;
    }


}
...