]> source.dussan.org Git - javassist.git/commitdiff
add a new CodeConverter method:redirectMethodCallToStatic 244/head
authorshifujun <shifujun@foxmail.com>
Fri, 1 Feb 2019 09:26:07 +0000 (17:26 +0800)
committershifujun <shifujun@foxmail.com>
Fri, 1 Feb 2019 09:33:00 +0000 (17:33 +0800)
src/main/javassist/CodeConverter.java
src/main/javassist/convert/TransformCallToStatic.java [new file with mode: 0644]
src/test/javassist/JvstTest3.java
src/test/test3/MethodRedirectToStatic.java [new file with mode: 0644]

index 6df3622c2106203d8b7ace1e6fbf69af36f73b83..10d7ddad3a71d977f5ce1eff087933ff01804a6a 100644 (file)
@@ -25,6 +25,7 @@ import javassist.convert.TransformAccessArrayField;
 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 class CodeConverter {
             = 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.
      * 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 (file)
index 0000000..87181ed
--- /dev/null
@@ -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;
+    }
+}
index 46f06b168c2ce4be58e90402794c6c8b55e1100f..c065170c770ee6441a6a357aa39c310ad8c00a51 100644 (file)
@@ -586,6 +586,20 @@ public class JvstTest3 extends JvstTestRoot {
         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 (file)
index 0000000..f1d68e3
--- /dev/null
@@ -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);
+    }
+}