From c85bc4f090044d657419f82b5fc33434ab911c62 Mon Sep 17 00:00:00 2001 From: shifujun Date: Fri, 1 Feb 2019 17:26:07 +0800 Subject: [PATCH] add a new CodeConverter method:redirectMethodCallToStatic --- src/main/javassist/CodeConverter.java | 37 +++++++++++++++++++ .../convert/TransformCallToStatic.java | 29 +++++++++++++++ src/test/javassist/JvstTest3.java | 14 +++++++ src/test/test3/MethodRedirectToStatic.java | 22 +++++++++++ 4 files changed, 102 insertions(+) create mode 100644 src/main/javassist/convert/TransformCallToStatic.java create mode 100644 src/test/test3/MethodRedirectToStatic.java diff --git a/src/main/javassist/CodeConverter.java b/src/main/javassist/CodeConverter.java index 6df3622c..10d7ddad 100644 --- a/src/main/javassist/CodeConverter.java +++ b/src/main/javassist/CodeConverter.java @@ -25,6 +25,7 @@ import javassist.convert.TransformAfter; import javassist.convert.TransformBefore; import javassist.convert.TransformCall; +import javassist.convert.TransformCallToStatic; import javassist.convert.TransformFieldAccess; import javassist.convert.TransformNew; import javassist.convert.TransformNewClass; @@ -406,6 +407,42 @@ public void redirectMethodCall(String oldMethodName, = 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 + * move(): + * + *
class Point {
+     *     Point move(int x, int y) { ... }
+     * }
+ * + *

Then the static method must be something like this: + * + *

class Verbose {
+     *     static Point print(Point target, int x, int y) { ... }
+     * }
+ * + *

The CodeConverter would translate bytecode + * equivalent to: + * + *

Point p2 = p.move(x + y, 0);
+ * + *

into the bytecode equivalent to: + * + *

Point p2 = Verbose.print(p, x + y, 0);
+ * + * @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. * That "before" method must be static. The return type must be diff --git a/src/main/javassist/convert/TransformCallToStatic.java b/src/main/javassist/convert/TransformCallToStatic.java new file mode 100644 index 00000000..87181edf --- /dev/null +++ b/src/main/javassist/convert/TransformCallToStatic.java @@ -0,0 +1,29 @@ +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; + } +} diff --git a/src/test/javassist/JvstTest3.java b/src/test/javassist/JvstTest3.java index 46f06b16..c065170c 100644 --- a/src/test/javassist/JvstTest3.java +++ b/src/test/javassist/JvstTest3.java @@ -586,6 +586,20 @@ public void testMethodRedirect2() throws Exception { 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 { ClassMap map = new ClassMap(); map.put("aa", "AA"); diff --git a/src/test/test3/MethodRedirectToStatic.java b/src/test/test3/MethodRedirectToStatic.java new file mode 100644 index 00000000..f1d68e35 --- /dev/null +++ b/src/test/test3/MethodRedirectToStatic.java @@ -0,0 +1,22 @@ +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); + } +}