Class CodeConverter
javassist.expr
package).
Instances of this class specifies how to instrument of the
bytecodes representing a method body. They are passed to
CtClass.instrument()
or
CtMethod.instrument()
as a parameter.
Example:
ClassPool cp = ClassPool.getDefault(); CtClass point = cp.get("Point"); CtClass singleton = cp.get("Singleton"); CtClass client = cp.get("Client"); CodeConverter conv = new CodeConverter(); conv.replaceNew(point, singleton, "makePoint"); client.instrument(conv);
This program substitutes "Singleton.makePoint()
"
for all occurrences of "new Point()
"
appearing in methods declared in a Client
class.
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic interface
Interface containing the method names to be used as array access replacements.static class
Default implementation of theArrayAccessReplacementMethodNames
interface giving default values for method names to be used for replacing accesses to array elements. -
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionvoid
insertAfterMethod
(CtMethod origMethod, CtMethod afterMethod) Inserts a call to another method after an existing method call.void
insertBeforeMethod
(CtMethod origMethod, CtMethod beforeMethod) Insert a call to another method before an existing method call.void
redirectFieldAccess
(CtField field, CtClass newClass, String newFieldname) Modify a method body so that field read/write expressions access a different field from the original one.void
redirectMethodCall
(String oldMethodName, CtMethod newMethod) Correct invocations to a method that has been renamed.void
redirectMethodCall
(CtMethod origMethod, CtMethod substMethod) Modify method invocations in a method body so that a different method will be invoked.void
redirectMethodCallToStatic
(CtMethod origMethod, CtMethod staticMethod) Redirect non-static method invocations in a method body to a static method.void
replaceArrayAccess
(CtClass calledClass, CodeConverter.ArrayAccessReplacementMethodNames names) Modify a method body, so that ALL accesses to an array are replaced with calls to static methods within another class.void
replaceFieldRead
(CtField field, CtClass calledClass, String calledMethod) Modify a method body so that an expression reading the specified field is replaced with a call to the specified static method.void
replaceFieldWrite
(CtField field, CtClass calledClass, String calledMethod) Modify a method body so that an expression writing the specified field is replaced with a call to the specified static method.void
replaceNew
(CtClass oldClass, CtClass newClass) Modify a method body so that instantiation of the class specified byoldClass
is replaced with instantiation of another classnewClass
.void
replaceNew
(CtClass newClass, CtClass calledClass, String calledMethod) Modify a method body so that instantiation of the specified class is replaced with a call to the specified static method.
-
Constructor Details
-
CodeConverter
public CodeConverter()
-
-
Method Details
-
replaceNew
Modify a method body so that instantiation of the specified class is replaced with a call to the specified static method. For example,replaceNew(ctPoint, ctSingleton, "createPoint")
(wherectPoint
andctSingleton
are compile-time classes for classPoint
and classSingleton
, respectively) replaces all occurrences of:new Point(x, y)
in the method body with:Singleton.createPoint(x, y)
This enables to intercept instantiation of
Point
and change the samentics. For example, the followingcreatePoint()
implements the singleton pattern:public static Point createPoint(int x, int y) { if (aPoint == null) aPoint = new Point(x, y); return aPoint; }
The static method call substituted for the original
new
expression must be able to receive the same set of parameters as the original constructor. If there are multiple constructors with different parameter types, then there must be multiple static methods with the same name but different parameter types.The return type of the substituted static method must be the exactly same as the type of the instantiated class specified by
newClass
.- Parameters:
newClass
- the instantiated class.calledClass
- the class in which the static method is declared.calledMethod
- the name of the static method.
-
replaceNew
Modify a method body so that instantiation of the class specified byoldClass
is replaced with instantiation of another classnewClass
. For example,replaceNew(ctPoint, ctPoint2)
(wherectPoint
andctPoint2
are compile-time classes for classPoint
and classPoint2
, respectively) replaces all occurrences of:new Point(x, y)
in the method body with:new Point2(x, y)
Note that
Point2
must be type-compatible withPoint
. It must have the same set of methods, fields, and constructors as the replaced class. -
redirectFieldAccess
Modify a method body so that field read/write expressions access a different field from the original one.Note that this method changes only the filed name and the class declaring the field; the type of the target object does not change. Therefore, the substituted field must be declared in the same class or a superclass of the original class.
Also,
clazz
andnewClass
must specify the class directly declaring the field. They must not specify a subclass of that class.- Parameters:
field
- the originally accessed field.newClass
- the class declaring the substituted field.newFieldname
- the name of the substituted field.
-
replaceFieldRead
Modify a method body so that an expression reading the specified field is replaced with a call to the specified static method. This static method receives the target object of the original read expression as a parameter. It must return a value of the same type as the field.For example, the program below
Point p = new Point(); int newX = p.x + 3;
can be translated into:
Point p = new Point(); int newX = Accessor.readX(p) + 3;
where
public class Accessor { public static int readX(Object target) { ... } }
The type of the parameter of
readX()
must bejava.lang.Object
independently of the actual type oftarget
. The return type must be the same as the field type.- Parameters:
field
- the field.calledClass
- the class in which the static method is declared.calledMethod
- the name of the static method.
-
replaceFieldWrite
Modify a method body so that an expression writing the specified field is replaced with a call to the specified static method. This static method receives two parameters: the target object of the original write expression and the assigned value. The return type of the static method isvoid
.For example, the program below
Point p = new Point(); p.x = 3;
can be translated into:
Point p = new Point(); Accessor.writeX(3);
where
public class Accessor { public static void writeX(Object target, int value) { ... } }
The type of the first parameter of
writeX()
must bejava.lang.Object
independently of the actual type oftarget
. The type of the second parameter is the same as the field type.- Parameters:
field
- the field.calledClass
- the class in which the static method is declared.calledMethod
- the name of the static method.
-
replaceArrayAccess
public void replaceArrayAccess(CtClass calledClass, CodeConverter.ArrayAccessReplacementMethodNames names) throws NotFoundException Modify a method body, so that ALL accesses to an array are replaced with calls to static methods within another class. In the case of reading an element from the array, this is replaced with a call to a static method with the array and the index as arguments, the return value is the value read from the array. If writing to an array, this is replaced with a call to a static method with the array, index and new value as parameters, the return value of the static method isvoid
.The
calledClass
parameter is the class containing the static methods to be used for array replacement. Thenames
parameter points to an implementation ofArrayAccessReplacementMethodNames
which specifies the names of the method to be used for access for each type of array. For example reading from anint[]
will require a different method than if writing to anint[]
, and writing to along[]
will require a different method than if writing to abyte[]
. If the implementation ofArrayAccessReplacementMethodNames
does not contain the name for access for a type of array, that access is not replaced.A default implementation of
ArrayAccessReplacementMethodNames
calledDefaultArrayAccessReplacementMethodNames
has been provided and is what is used in the following example. This also assumes that'foo.ArrayAdvisor'
is the name of theCtClass
passed in.If we have the following class:
class POJO{ int[] ints = new int[]{1, 2, 3, 4, 5}; long[] longs = new int[]{10, 20, 30}; Object objects = new Object[]{true, false}; Integer[] integers = new Integer[]{new Integer(10)}; }
and this is accessed as:POJO p = new POJO(); //Write to int array p.ints[2] = 7; //Read from int array int i = p.ints[2]; //Write to long array p.longs[2] = 1000L; //Read from long array long l = p.longs[2]; //Write to Object array p.objects[2] = "Hello"; //Read from Object array Object o = p.objects[2]; //Write to Integer array Integer integer = new Integer(5); p.integers[0] = integer; //Read from Object array integer = p.integers[0];
Following instrumentation we will havePOJO p = new POJO(); //Write to int array ArrayAdvisor.arrayWriteInt(p.ints, 2, 7); //Read from int array int i = ArrayAdvisor.arrayReadInt(p.ints, 2); //Write to long array ArrayAdvisor.arrayWriteLong(p.longs, 2, 1000L); //Read from long array long l = ArrayAdvisor.arrayReadLong(p.longs, 2); //Write to Object array ArrayAdvisor.arrayWriteObject(p.objects, 2, "Hello"); //Read from Object array Object o = ArrayAdvisor.arrayReadObject(p.objects, 2); //Write to Integer array Integer integer = new Integer(5); ArrayAdvisor.arrayWriteObject(p.integers, 0, integer); //Read from Object array integer = ArrayAdvisor.arrayWriteObject(p.integers, 0);
- Parameters:
calledClass
- the class containing the static methods.names
- contains the names of the methods to replace the different kinds of array access with.- Throws:
NotFoundException
- See Also:
-
redirectMethodCall
public void redirectMethodCall(CtMethod origMethod, CtMethod substMethod) throws CannotCompileException Modify method invocations in a method body so that a different method will be invoked.Note that the target object, the parameters, or the type of invocation (static method call, interface call, or private method call) are not modified. Only the method name is changed. The substituted method must have the same signature that the original one has. If the original method is a static method, the substituted method must be static.
- Parameters:
origMethod
- original methodsubstMethod
- substituted method- Throws:
CannotCompileException
-
redirectMethodCall
public void redirectMethodCall(String oldMethodName, CtMethod newMethod) throws CannotCompileException Correct invocations to a method that has been renamed. If a method is renamed, calls to that method must be also modified so that the method with the new name will be called.The method must be declared in the same class before and after it is renamed.
Note that the target object, the parameters, or the type of invocation (static method call, interface call, or private method call) are not modified. Only the method name is changed.
- Parameters:
oldMethodName
- the old name of the method.newMethod
- the method with the new name.- Throws:
CannotCompileException
- See Also:
-
redirectMethodCallToStatic
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 ismove()
: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);
- Parameters:
origMethod
- original methodstaticMethod
- static method
-
insertBeforeMethod
public void insertBeforeMethod(CtMethod origMethod, CtMethod beforeMethod) throws CannotCompileException Insert a call to another method before an existing method call. That "before" method must be static. The return type must bevoid
. As parameters, the before method receives the target object and all the parameters to the originally invoked method. For example, if the originally invoked method ismove()
:class Point { Point move(int x, int y) { ... } }
Then the before method must be something like this:
class Verbose { static void 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:
int tmp1 = x + y; int tmp2 = 0; Verbose.print(p, tmp1, tmp2); Point p2 = p.move(tmp1, tmp2);
- Parameters:
origMethod
- the method originally invoked.beforeMethod
- the method invoked beforeorigMethod
.- Throws:
CannotCompileException
-
insertAfterMethod
public void insertAfterMethod(CtMethod origMethod, CtMethod afterMethod) throws CannotCompileException Inserts a call to another method after an existing method call. That "after" method must be static. The return type must bevoid
. As parameters, the after method receives the target object and all the parameters to the originally invoked method. For example, if the originally invoked method ismove()
:class Point { Point move(int x, int y) { ... } }
Then the after method must be something like this:
class Verbose { static void 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:
int tmp1 = x + y; int tmp2 = 0; Point p2 = p.move(tmp1, tmp2); Verbose.print(p, tmp1, tmp2);
- Parameters:
origMethod
- the method originally invoked.afterMethod
- the method invoked afterorigMethod
.- Throws:
CannotCompileException
-