import javassist.compiler.Javac;
import javassist.compiler.CompileError;
import javassist.expr.ExprEditor;
-import javassist.tools.Callback;
/**
* <code>CtBehavior</code> represents a method, a constructor,
declaringClass.checkModify();
}
- /**
- * Inserts callback at the beginning of the body.
- *
- * <p>If this object represents a constructor,
- * the callback is inserted before
- * a constructor in the super class or this class is called.
- * Therefore, the inserted callback is subject to constraints described
- * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
- * For example, it cannot access instance fields or methods although
- * it may assign a value to an instance field directly declared in this
- * class. Accessing static fields and methods is allowed.
- * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
- *
- * @param callback the source code representing the inserted bytecode.
- * It must be a single statement or block.
- * @see CtConstructor#insertBeforeBody(String)
- */
- public void insertBefore(Callback callback)
- throws CannotCompileException
- {
- insertBefore(callback.toString(), true);
- }
-
/**
* Inserts bytecode at the beginning of the body.
*
}
}
- /**
- * Inserts callback bytecode at the end of the body.
- * The callback is inserted just before every return insturction.
- * It is not executed when an exception is thrown.
- *
- * @param callback the source code representing the inserted bytecode.
- * It must be a single statement or block.
- */
- public void insertAfter(Callback callback)
- throws CannotCompileException
- {
- insertAfter(callback.toString(), false);
- }
-
- /**
- * Inserts callback bytecode at the end of the body.
- * The callback is inserted just before every return insturction.
- * It is not executed when an exception is thrown.
- *
- * @param callback the callback source code representing the inserted bytecode.
- * It must be a single statement or block.
- * @param asFinally true if the inserted bytecode is executed
- * not only when the control normally returns
- * but also when an exception is thrown.
- * If this parameter is true, the inserted code cannot
- * access local variables.
- */
- public void insertAfter(Callback callback, boolean asFinally)
- throws CannotCompileException
- {
- insertAfter(callback.toString(), asFinally);
- }
-
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return insturction.
return insertAt(lineNum, true, src);
}
- /**
- * Inserts callback bytecode at the specified line in the body.
- * It is equivalent to:
- *
- * <br><code>insertAt(lineNum, true, src)</code>
- *
- * <br>See this method as well.
- *
- * @param lineNum the line number. The bytecode is inserted at the
- * beginning of the code at the line specified by this
- * line number.
- * @param callback the callback source code representing the inserted bytecode.
- * It must be a single statement or block.
- * @return the line number at which the callback bytecode has been inserted.
- *
- * @see CtBehavior#insertAt(int,boolean,String)
- */
- public int insertAt(int lineNum, Callback callback)
- throws CannotCompileException
- {
- return insertAt(lineNum, true, callback.toString());
- }
-
/**
* Inserts bytecode at the specified line in the body.
*
throw new CannotCompileException(e);
}
}
-
- /**
- * Inserts callback bytecode at the specified line in the body.
- *
- * <p>If there is not
- * a statement at the specified line, the bytecode might be inserted
- * at the line including the first statement after that line specified.
- * For example, if there is only a closing brace at that line, the
- * bytecode would be inserted at another line below.
- * To know exactly where the bytecode will be inserted, call with
- * <code>modify</code> set to <code>false</code>.
- *
- * @param lineNum the line number. The bytecode is inserted at the
- * beginning of the code at the line specified by this
- * line number.
- * @param modify if false, this method does not insert the bytecode.
- * It instead only returns the line number at which
- * the bytecode would be inserted.
- * @param callback the callback source code representing the inserted bytecode.
- * It must be a single statement or block.
- * If modify is false, the value of src can be null.
- * @return the line number at which the bytecode has been inserted.
- */
- public int insertAt(int lineNum, boolean modify, Callback callback)
- throws CannotCompileException
- {
- return insertAt(lineNum,modify,callback.toString());
- }
}
package javassist.tools;
+import javassist.CannotCompileException;
+import javassist.CtBehavior;
+
import java.util.HashMap;
import java.util.UUID;
/**
- * Creates bytecode that when executed calls back to the objects result method.
+ * Creates bytecode that when executed calls back to the instance's result method.
+ *
+ * Inserts callbacks in <code>CtBehaviour</code>
+ *
*/
public abstract class Callback {
public static HashMap<String, Callback> callbacks = new HashMap<String, Callback>();
- private final String callbackCode;
+ private final String sourceCode;
/**
* Constructs a new <code>Callback</code> object.
*
- * @param src java code that returns one or many objects. if many objects
- * are returned they should be comma separated
+ * @param src The source code representing the inserted callback bytecode.
+ * Can be one or many single statements or blocks each returning one object.
+ * If many single statements or blocks are used they must be comma separated.
*/
public Callback(String src){
String uuid = UUID.randomUUID().toString();
callbacks.put(uuid, this);
- callbackCode = "((javassist.tools.Callback) javassist.tools.Callback.callbacks.get(\""+uuid+"\")).result(new Object[]{"+src+"});";
+ sourceCode = "((javassist.tools.Callback) javassist.tools.Callback.callbacks.get(\""+uuid+"\")).result(new Object[]{"+src+"});";
}
/**
* Gets called when bytecode is executed
*
- * @param objects java code that returns one or many objects. if many objects
- * are returned they should be comma separated
+ * @param objects Objects that the bytecode in callback returns
*/
public abstract void result(Object... objects);
@Override
public String toString(){
- return callbackCode;
+ return sourceCode();
+ }
+
+ public String sourceCode(){
+ return sourceCode;
+ }
+
+ /**
+ * Inserts callback at the beginning of the body.
+ *
+ * @param callback The callback
+ *
+ * @see CtBehavior#insertBefore(String)
+ */
+ public static void insertBefore(CtBehavior behavior, Callback callback)
+ throws CannotCompileException
+ {
+ behavior.insertBefore(callback.toString());
+ }
+
+ /**
+ * Inserts callback at the end of the body.
+ * The callback is inserted just before every return instruction.
+ * It is not executed when an exception is thrown.
+ *
+ * @param behavior The behaviour to insert callback in
+ * @param callback The callback
+ *
+ * @see CtBehavior#insertAfter(String, boolean)
+ */
+ public static void insertAfter(CtBehavior behavior,Callback callback)
+ throws CannotCompileException
+ {
+ behavior.insertAfter(callback.toString(), false);
+ }
+
+ /**
+ * Inserts callback at the end of the body.
+ * The callback is inserted just before every return instruction.
+ * It is not executed when an exception is thrown.
+ *
+ * @param behavior The behaviour to insert callback in
+ * @param callback The callback representing the inserted.
+ * @param asFinally True if the inserted is executed
+ * Not only when the control normally returns
+ * but also when an exception is thrown.
+ * If this parameter is true, the inserted code cannot
+ * access local variables.
+ *
+ * @see CtBehavior#insertAfter(String, boolean)
+ */
+ public static void insertAfter(CtBehavior behavior, Callback callback, boolean asFinally)
+ throws CannotCompileException
+ {
+ behavior.insertAfter(callback.toString(), asFinally);
+ }
+
+ /**
+ * Inserts callback at the specified line in the body.
+ *
+ * @param behavior The behaviour to insert callback in
+ * @param callback The callback representing.
+ * @param lineNum The line number. The callback is inserted at the
+ * beginning of the code at the line specified by this
+ * line number.
+ *
+ * @return The line number at which the callback has been inserted.
+ *
+ * @see CtBehavior#insertAt(int, String)
+ */
+ public static int insertAt(CtBehavior behavior, Callback callback, int lineNum)
+ throws CannotCompileException
+ {
+ return behavior.insertAt(lineNum, callback.toString());
}
}
--- /dev/null
+package javassist.tools;
+
+import javassist.*;
+import junit.framework.TestCase;
+import test.javassist.tools.DummyClass;
+
+import static javassist.tools.Callback.*;
+
+public class CallbackTest extends TestCase {
+
+ public void testSomeCallbacks() throws Exception {
+
+ // Get class and method to change
+ ClassPool pool = ClassPool.getDefault();
+ CtClass classToChange = pool.get("test.javassist.tools.DummyClass");
+ CtMethod methodToChange = classToChange.getDeclaredMethod("dummyMethod");
+
+ // Insert after
+ methodToChange.insertAfter(new Callback("Thread.currentThread(), dummyString") {
+ @Override
+ public void result(Object... objects) {
+ assertEquals(objects[0], Thread.currentThread());
+ assertEquals(objects[1], "dummyStringValue");
+ }
+ }.sourceCode());
+
+ // Insert after using utility method
+ insertAfter(methodToChange, new Callback("Thread.currentThread(), dummyString") {
+ @Override
+ public void result(Object... objects) {
+ assertEquals(objects[0], Thread.currentThread());
+ assertEquals(objects[1], "dummyStringValue");
+ }
+ });
+
+ // Change class and invoke method();
+ classToChange.toClass();
+ new DummyClass().dummyMethod();
+ }
+}
--- /dev/null
+package test.javassist.tools;
+
+public class DummyClass {
+
+ private String dummyString = "dummyStringValue";
+
+ public void dummyMethod(){}
+}