@@ -20,7 +20,6 @@ import javassist.bytecode.*; | |||
import javassist.compiler.Javac; | |||
import javassist.compiler.CompileError; | |||
import javassist.expr.ExprEditor; | |||
import javassist.tools.Callback; | |||
/** | |||
* <code>CtBehavior</code> represents a method, a constructor, | |||
@@ -714,29 +713,6 @@ public abstract class CtBehavior extends CtMember { | |||
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. | |||
* | |||
@@ -802,39 +778,6 @@ public abstract class CtBehavior extends CtMember { | |||
} | |||
} | |||
/** | |||
* 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. | |||
@@ -1191,29 +1134,6 @@ public abstract class CtBehavior extends CtMember { | |||
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. | |||
* | |||
@@ -1290,32 +1210,4 @@ public abstract class CtBehavior extends CtMember { | |||
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()); | |||
} | |||
} |
@@ -1,39 +1,118 @@ | |||
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()); | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
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(); | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
package test.javassist.tools; | |||
public class DummyClass { | |||
private String dummyString = "dummyStringValue"; | |||
public void dummyMethod(){} | |||
} |