Reputation: 9543
I have this simple function:
public int id() {
return 0;
}
I have this test function:
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);
}
I would like the output to be 1, 2, 3, 4, 5, 6
;
Right now I call instrument
on CtMethod
which is working fine.
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
But in the end none of the transformations has any effect. I have no clue what to do since there is so little information out there.
Here is the complete code:
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;
}
}
Upvotes: 0
Views: 435
Reputation: 16056
Not sure how you are running the code, since normally you need to start the JVM with a javaagent on the command line to inject an Instrumentation instance. In my example below, I am using ByteBuddy to acquire the Instrumentation instance at runtime.
You must also create a ClassFileTransformer that returns the byte code of the modified class, which you can acquire by calling CtClass.toByteCode()
The code below prints this output:
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
Basic code:
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;
}
}
Upvotes: 0