add a useful CodeConverter method:redirectMethodCallToStatictags/rel_3_25_0_ga
import javassist.convert.TransformAfter; | import javassist.convert.TransformAfter; | ||||
import javassist.convert.TransformBefore; | import javassist.convert.TransformBefore; | ||||
import javassist.convert.TransformCall; | import javassist.convert.TransformCall; | ||||
import javassist.convert.TransformCallToStatic; | |||||
import javassist.convert.TransformFieldAccess; | import javassist.convert.TransformFieldAccess; | ||||
import javassist.convert.TransformNew; | import javassist.convert.TransformNew; | ||||
import javassist.convert.TransformNewClass; | import javassist.convert.TransformNewClass; | ||||
= new TransformCall(transformers, oldMethodName, newMethod); | = new TransformCall(transformers, oldMethodName, newMethod); | ||||
} | } | ||||
/** | |||||
* Redirect non-static method invocations in a method body to a static | |||||
* method. The return type must be same with the originally invoked method. | |||||
* As parameters, the static method receives | |||||
* the target object and all the parameters to the originally invoked | |||||
* method. For example, if the originally invoked method is | |||||
* <code>move()</code>: | |||||
* | |||||
* <pre>class Point { | |||||
* Point move(int x, int y) { ... } | |||||
* }</pre> | |||||
* | |||||
* <p>Then the static method must be something like this: | |||||
* | |||||
* <pre>class Verbose { | |||||
* static Point print(Point target, int x, int y) { ... } | |||||
* }</pre> | |||||
* | |||||
* <p>The <code>CodeConverter</code> would translate bytecode | |||||
* equivalent to: | |||||
* | |||||
* <pre>Point p2 = p.move(x + y, 0);</pre> | |||||
* | |||||
* <p>into the bytecode equivalent to: | |||||
* | |||||
* <pre>Point p2 = Verbose.print(p, x + y, 0);</pre> | |||||
* | |||||
* @param origMethod original method | |||||
* @param staticMethod static method | |||||
*/ | |||||
public void redirectMethodCallToStatic(CtMethod origMethod, | |||||
CtMethod staticMethod) { | |||||
transformers = new TransformCallToStatic(transformers, origMethod, | |||||
staticMethod); | |||||
} | |||||
/** | /** | ||||
* Insert a call to another method before an existing method call. | * Insert a call to another method before an existing method call. | ||||
* That "before" method must be static. The return type must be | * That "before" method must be static. The return type must be |
package javassist.convert; | |||||
import javassist.CtMethod; | |||||
import javassist.bytecode.BadBytecode; | |||||
import javassist.bytecode.CodeIterator; | |||||
import javassist.bytecode.ConstPool; | |||||
import javassist.bytecode.Descriptor; | |||||
import javassist.bytecode.Opcode; | |||||
public class TransformCallToStatic extends TransformCall { | |||||
public TransformCallToStatic(Transformer next, CtMethod origMethod, CtMethod substMethod) { | |||||
super(next, origMethod, substMethod); | |||||
methodDescriptor = origMethod.getMethodInfo2().getDescriptor(); | |||||
} | |||||
@Override | |||||
protected int match(int c, int pos, CodeIterator iterator, int typedesc, ConstPool cp) { | |||||
if (newIndex == 0) { | |||||
String desc = Descriptor.insertParameter(classname, methodDescriptor); | |||||
int nt = cp.addNameAndTypeInfo(newMethodname, desc); | |||||
int ci = cp.addClassInfo(newClassname); | |||||
newIndex = cp.addMethodrefInfo(ci, nt); | |||||
constPool = cp; | |||||
} | |||||
iterator.writeByte(Opcode.INVOKESTATIC, pos); | |||||
iterator.write16bit(newIndex, pos + 1); | |||||
return pos; | |||||
} | |||||
} |
assertEquals(524, invoke(obj, "test")); | assertEquals(524, invoke(obj, "test")); | ||||
} | } | ||||
public void testMethodRedirectToStatic() throws Exception { | |||||
CtClass targetClass = sloader.get("test3.MethodRedirectToStatic"); | |||||
CtClass staticClass = sloader.get("test3.MethodRedirectToStatic2"); | |||||
CtMethod targetMethod = targetClass.getDeclaredMethod("add"); | |||||
CtMethod staticMethod = staticClass.getDeclaredMethod("add2"); | |||||
CodeConverter conv = new CodeConverter(); | |||||
conv.redirectMethodCallToStatic(targetMethod, staticMethod); | |||||
targetClass.instrument(conv); | |||||
targetClass.writeFile(); | |||||
Object obj = make(targetClass.getName()); | |||||
assertEquals(30, invoke(obj, "test")); | |||||
} | |||||
public void testClassMap() throws Exception { | public void testClassMap() throws Exception { | ||||
ClassMap map = new ClassMap(); | ClassMap map = new ClassMap(); | ||||
map.put("aa", "AA"); | map.put("aa", "AA"); |
package test3; | |||||
public class MethodRedirectToStatic { | |||||
public static void main(String[] args) { | |||||
System.out.println(new MethodRedirectToStatic().test()); | |||||
} | |||||
int add(int a, int b) { | |||||
return a + b; | |||||
} | |||||
public int test() { | |||||
return add(1, 2); | |||||
} | |||||
} | |||||
class MethodRedirectToStatic2 { | |||||
public static int add2(MethodRedirectToStatic target, int a, int b) { | |||||
return target.add(a * 10, b * 10); | |||||
} | |||||
} |