/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.convert.*; /** * Simple translator of method bodies * (also see the 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: *

* *

This program substitutes "Singleton.makePoint()" * for all occurrences of "new Point()" * appearing in methods declared in a Client class. * * @see javassist.CtClass#instrument(CodeConverter) * @see javassist.CtMethod#instrument(CodeConverter) * @see javassist.expr.ExprEditor */ public class CodeConverter { Transformer transformers = null; /** * 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") * (where ctPoint and ctSingleton are * compile-time classes for class Point and class * Singleton, respectively) * replaces all occurrences of: * *

* * in the method body with: * * * *

This enables to intercept instantiation of Point * and change the samentics. For example, the following * createPoint() implements the singleton pattern: * *

* *

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. * * @param newClass the instantiated class. * @param calledClass the class in which the static method is * declared. * @param calledMethod the name of the static method. */ public void replaceNew(CtClass newClass, CtClass calledClass, String calledMethod) { transformers = new TransformNew(transformers, newClass.getName(), calledClass.getName(), calledMethod); } /** * 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 and newClass must specify * the class directly declaring the field. They must not specify * a subclass of that class. * * @param field the originally accessed field. * @param newClass the class declaring the substituted field. * @param newFieldname the name of the substituted field. */ public void redirectFieldAccess(CtField field, CtClass newClass, String newFieldname) { transformers = new TransformFieldAccess(transformers, field, newClass.getName(), newFieldname); } /** * 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 * *

* *

can be translated into: * *

* *

where * *

* *

The type of the parameter of readX() must * be java.lang.Object independently of the actual * type of target. The return type must be the same * as the field type. * * @param field the field. * @param calledClass the class in which the static method is * declared. * @param calledMethod the name of the static method. */ public void replaceFieldRead(CtField field, CtClass calledClass, String calledMethod) { transformers = new TransformReadField(transformers, field, calledClass.getName(), calledMethod); } /** * 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 is void. * *

For example, the program below * *

* *

can be translated into: * *

* *

where * *

* *

The type of the first parameter of writeX() must * be java.lang.Object independently of the actual * type of target. The type of the second parameter * is the same as the field type. * * @param field the field. * @param calledClass the class in which the static method is * declared. * @param calledMethod the name of the static method. */ public void replaceFieldWrite(CtField field, CtClass calledClass, String calledMethod) { transformers = new TransformWriteField(transformers, field, calledClass.getName(), calledMethod); } /** * Modify method invocations in a method body so that a different * method is 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. * * @param origMethod original method * @param substMethod substituted method */ public void redirectMethodCall(CtMethod origMethod, CtMethod substMethod) throws CannotCompileException { String d1 = origMethod.getMethodInfo2().getDescriptor(); String d2 = substMethod.getMethodInfo2().getDescriptor(); if (!d1.equals(d2)) throw new CannotCompileException("signature mismatch"); transformers = new TransformCall(transformers, origMethod, substMethod); } /** * Insert a call to another method before an existing method call. * That "before" method must be static. The return type must be * void. 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 is * move(): * *

* *

Then the before method must be something like this: * *

* *

The CodeConverter would translate bytecode * equivalent to: * *

* *

into the bytecode equivalent to: * *

* * @param origMethod the method originally invoked. * @param beforeMethod the method invoked before * origMethod. */ public void insertBeforeMethod(CtMethod origMethod, CtMethod beforeMethod) throws CannotCompileException { try { transformers = new TransformBefore(transformers, origMethod, beforeMethod); } catch (NotFoundException e) { throw new CannotCompileException(e); } } /** * Inserts a call to another method after an existing method call. * That "after" method must be static. The return type must be * void. 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 is * move(): * * * *

Then the after method must be something like this: * *

* *

The CodeConverter would translate bytecode * equivalent to: * *

* *

into the bytecode equivalent to: * *

* * @param origMethod the method originally invoked. * @param afterMethod the method invoked after * origMethod. */ public void insertAfterMethod(CtMethod origMethod, CtMethod afterMethod) throws CannotCompileException { try { transformers = new TransformAfter(transformers, origMethod, afterMethod); } catch (NotFoundException e) { throw new CannotCompileException(e); } } /** * Performs code conversion. */ void doit(CtClass clazz, MethodInfo minfo, ConstPool cp) throws CannotCompileException { Transformer t; CodeAttribute codeAttr = minfo.getCodeAttribute(); if (codeAttr == null || transformers == null) return; for (t = transformers; t != null; t = t.getNext()) t.initialize(cp, codeAttr); CodeIterator iterator = codeAttr.iterator(); while (iterator.hasNext()) { try { int pos = iterator.next(); for (t = transformers; t != null; t = t.getNext()) pos = t.transform(clazz, pos, iterator, cp); } catch (BadBytecode e) { throw new CannotCompileException(e); } } int locals = 0; for (t = transformers; t != null; t = t.getNext()) { int s = t.extraLocals(); if (s > locals) locals = s; } for (t = transformers; t != null; t = t.getNext()) t.clean(); codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals); } }