001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist;
017:
018: import javassist.bytecode.*;
019: import javassist.convert.*;
020:
021: /**
022: * Simple translator of method bodies
023: * (also see the <code>javassist.expr</code> package).
024: *
025: * <p>Instances of this class specifies how to instrument of the
026: * bytecodes representing a method body. They are passed to
027: * <code>CtClass.instrument()</code> or
028: * <code>CtMethod.instrument()</code> as a parameter.
029: *
030: * <p>Example:
031: * <ul><pre>
032: * ClassPool cp = ClassPool.getDefault();
033: * CtClass point = cp.get("Point");
034: * CtClass singleton = cp.get("Singleton");
035: * CtClass client = cp.get("Client");
036: * CodeConverter conv = new CodeConverter();
037: * conv.replaceNew(point, singleton, "makePoint");
038: * client.instrument(conv);
039: * </pre></ul>
040: *
041: * <p>This program substitutes "<code>Singleton.makePoint()</code>"
042: * for all occurrences of "<code>new Point()</code>"
043: * appearing in methods declared in a <code>Client</code> class.
044: *
045: * @see javassist.CtClass#instrument(CodeConverter)
046: * @see javassist.CtMethod#instrument(CodeConverter)
047: * @see javassist.expr.ExprEditor
048: */
049: public class CodeConverter {
050: Transformer transformers = null;
051:
052: /**
053: * Modify a method body so that instantiation of the specified class
054: * is replaced with a call to the specified static method. For example,
055: * <code>replaceNew(ctPoint, ctSingleton, "createPoint")</code>
056: * (where <code>ctPoint</code> and <code>ctSingleton</code> are
057: * compile-time classes for class <code>Point</code> and class
058: * <code>Singleton</code>, respectively)
059: * replaces all occurrences of:
060: *
061: * <ul><code>new Point(x, y)</code></ul>
062: *
063: * in the method body with:
064: *
065: * <ul><code>Singleton.createPoint(x, y)</code></ul>
066: *
067: * <p>This enables to intercept instantiation of <code>Point</code>
068: * and change the samentics. For example, the following
069: * <code>createPoint()</code> implements the singleton pattern:
070: *
071: * <ul><pre>public static Point createPoint(int x, int y) {
072: * if (aPoint == null)
073: * aPoint = new Point(x, y);
074: * return aPoint;
075: * }
076: * </pre></ul>
077: *
078: * <p>The static method call substituted for the original <code>new</code>
079: * expression must be
080: * able to receive the same set of parameters as the original
081: * constructor. If there are multiple constructors with different
082: * parameter types, then there must be multiple static methods
083: * with the same name but different parameter types.
084: *
085: * <p>The return type of the substituted static method must be
086: * the exactly same as the type of the instantiated class specified by
087: * <code>newClass</code>.
088: *
089: * @param newClass the instantiated class.
090: * @param calledClass the class in which the static method is
091: * declared.
092: * @param calledMethod the name of the static method.
093: */
094: public void replaceNew(CtClass newClass, CtClass calledClass,
095: String calledMethod) {
096: transformers = new TransformNew(transformers, newClass
097: .getName(), calledClass.getName(), calledMethod);
098: }
099:
100: /**
101: * Modify a method body so that field read/write expressions access
102: * a different field from the original one.
103: *
104: * <p>Note that this method changes only the filed name and the class
105: * declaring the field; the type of the target object does not change.
106: * Therefore, the substituted field must be declared in the same class
107: * or a superclass of the original class.
108: *
109: * <p>Also, <code>clazz</code> and <code>newClass</code> must specify
110: * the class directly declaring the field. They must not specify
111: * a subclass of that class.
112: *
113: * @param field the originally accessed field.
114: * @param newClass the class declaring the substituted field.
115: * @param newFieldname the name of the substituted field.
116: */
117: public void redirectFieldAccess(CtField field, CtClass newClass,
118: String newFieldname) {
119: transformers = new TransformFieldAccess(transformers, field,
120: newClass.getName(), newFieldname);
121: }
122:
123: /**
124: * Modify a method body so that an expression reading the specified
125: * field is replaced with a call to the specified <i>static</i> method.
126: * This static method receives the target object of the original
127: * read expression as a parameter. It must return a value of
128: * the same type as the field.
129: *
130: * <p>For example, the program below
131: *
132: * <ul><pre>Point p = new Point();
133: * int newX = p.x + 3;</pre></ul>
134: *
135: * <p>can be translated into:
136: *
137: * <ul><pre>Point p = new Point();
138: * int newX = Accessor.readX(p) + 3;</pre></ul>
139: *
140: * <p>where
141: *
142: * <ul><pre>public class Accessor {
143: * public static int readX(Object target) { ... }
144: * }</pre></ul>
145: *
146: * <p>The type of the parameter of <code>readX()</code> must
147: * be <code>java.lang.Object</code> independently of the actual
148: * type of <code>target</code>. The return type must be the same
149: * as the field type.
150: *
151: * @param field the field.
152: * @param calledClass the class in which the static method is
153: * declared.
154: * @param calledMethod the name of the static method.
155: */
156: public void replaceFieldRead(CtField field, CtClass calledClass,
157: String calledMethod) {
158: transformers = new TransformReadField(transformers, field,
159: calledClass.getName(), calledMethod);
160: }
161:
162: /**
163: * Modify a method body so that an expression writing the specified
164: * field is replaced with a call to the specified static method.
165: * This static method receives two parameters: the target object of
166: * the original
167: * write expression and the assigned value. The return type of the
168: * static method is <code>void</code>.
169: *
170: * <p>For example, the program below
171: *
172: * <ul><pre>Point p = new Point();
173: * p.x = 3;</pre></ul>
174: *
175: * <p>can be translated into:
176: *
177: * <ul><pre>Point p = new Point();
178: * Accessor.writeX(3);</pre></ul>
179: *
180: * <p>where
181: *
182: * <ul><pre>public class Accessor {
183: * public static void writeX(Object target, int value) { ... }
184: * }</pre></ul>
185: *
186: * <p>The type of the first parameter of <code>writeX()</code> must
187: * be <code>java.lang.Object</code> independently of the actual
188: * type of <code>target</code>. The type of the second parameter
189: * is the same as the field type.
190: *
191: * @param field the field.
192: * @param calledClass the class in which the static method is
193: * declared.
194: * @param calledMethod the name of the static method.
195: */
196: public void replaceFieldWrite(CtField field, CtClass calledClass,
197: String calledMethod) {
198: transformers = new TransformWriteField(transformers, field,
199: calledClass.getName(), calledMethod);
200: }
201:
202: /**
203: * Modify method invocations in a method body so that a different
204: * method will be invoked.
205: *
206: * <p>Note that the target object, the parameters, or
207: * the type of invocation
208: * (static method call, interface call, or private method call)
209: * are not modified. Only the method name is changed. The substituted
210: * method must have the same signature that the original one has.
211: * If the original method is a static method, the substituted method
212: * must be static.
213: *
214: * @param origMethod original method
215: * @param substMethod substituted method
216: */
217: public void redirectMethodCall(CtMethod origMethod,
218: CtMethod substMethod) throws CannotCompileException {
219: String d1 = origMethod.getMethodInfo2().getDescriptor();
220: String d2 = substMethod.getMethodInfo2().getDescriptor();
221: if (!d1.equals(d2))
222: throw new CannotCompileException("signature mismatch");
223:
224: int mod1 = origMethod.getModifiers();
225: int mod2 = substMethod.getModifiers();
226: if (Modifier.isStatic(mod1) != Modifier.isStatic(mod2)
227: || (Modifier.isPrivate(mod1) && !Modifier
228: .isPrivate(mod2))
229: || origMethod.getDeclaringClass().isInterface() != substMethod
230: .getDeclaringClass().isInterface())
231: throw new CannotCompileException("invoke-type mismatch");
232:
233: transformers = new TransformCall(transformers, origMethod,
234: substMethod);
235: }
236:
237: /**
238: * Correct invocations to a method that has been renamed.
239: * If a method is renamed, calls to that method must be also
240: * modified so that the method with the new name will be called.
241: *
242: * <p>The method must be declared in the same class before and
243: * after it is renamed.
244: *
245: * <p>Note that the target object, the parameters, or
246: * the type of invocation
247: * (static method call, interface call, or private method call)
248: * are not modified. Only the method name is changed.
249: *
250: * @param oldMethodName the old name of the method.
251: * @param newMethod the method with the new name.
252: * @see javassist.CtMethod#setName(String)
253: */
254: public void redirectMethodCall(String oldMethodName,
255: CtMethod newMethod) throws CannotCompileException {
256: transformers = new TransformCall(transformers, oldMethodName,
257: newMethod);
258: }
259:
260: /**
261: * Insert a call to another method before an existing method call.
262: * That "before" method must be static. The return type must be
263: * <code>void</code>. As parameters, the before method receives
264: * the target object and all the parameters to the originally invoked
265: * method. For example, if the originally invoked method is
266: * <code>move()</code>:
267: *
268: * <ul><pre>class Point {
269: * Point move(int x, int y) { ... }
270: * }</pre></ul>
271: *
272: * <p>Then the before method must be something like this:
273: *
274: * <ul><pre>class Verbose {
275: * static void print(Point target, int x, int y) { ... }
276: * }</pre></ul>
277: *
278: * <p>The <code>CodeConverter</code> would translate bytecode
279: * equivalent to:
280: *
281: * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
282: *
283: * <p>into the bytecode equivalent to:
284: *
285: * <ul><pre>int tmp1 = x + y;
286: * int tmp2 = 0;
287: * Verbose.print(p, tmp1, tmp2);
288: * Point p2 = p.move(tmp1, tmp2);</pre></ul>
289: *
290: * @param origMethod the method originally invoked.
291: * @param beforeMethod the method invoked before
292: * <code>origMethod</code>.
293: */
294: public void insertBeforeMethod(CtMethod origMethod,
295: CtMethod beforeMethod) throws CannotCompileException {
296: try {
297: transformers = new TransformBefore(transformers,
298: origMethod, beforeMethod);
299: } catch (NotFoundException e) {
300: throw new CannotCompileException(e);
301: }
302: }
303:
304: /**
305: * Inserts a call to another method after an existing method call.
306: * That "after" method must be static. The return type must be
307: * <code>void</code>. As parameters, the after method receives
308: * the target object and all the parameters to the originally invoked
309: * method. For example, if the originally invoked method is
310: * <code>move()</code>:
311: *
312: * <ul><pre>class Point {
313: * Point move(int x, int y) { ... }
314: * }</pre></ul>
315: *
316: * <p>Then the after method must be something like this:
317: *
318: * <ul><pre>class Verbose {
319: * static void print(Point target, int x, int y) { ... }
320: * }</pre></ul>
321: *
322: * <p>The <code>CodeConverter</code> would translate bytecode
323: * equivalent to:
324: *
325: * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
326: *
327: * <p>into the bytecode equivalent to:
328: *
329: * <ul><pre>int tmp1 = x + y;
330: * int tmp2 = 0;
331: * Point p2 = p.move(tmp1, tmp2);
332: * Verbose.print(p, tmp1, tmp2);</pre></ul>
333: *
334: * @param origMethod the method originally invoked.
335: * @param afterMethod the method invoked after
336: * <code>origMethod</code>.
337: */
338: public void insertAfterMethod(CtMethod origMethod,
339: CtMethod afterMethod) throws CannotCompileException {
340: try {
341: transformers = new TransformAfter(transformers, origMethod,
342: afterMethod);
343: } catch (NotFoundException e) {
344: throw new CannotCompileException(e);
345: }
346: }
347:
348: /**
349: * Performs code conversion.
350: */
351: void doit(CtClass clazz, MethodInfo minfo, ConstPool cp)
352: throws CannotCompileException {
353: Transformer t;
354:
355: CodeAttribute codeAttr = minfo.getCodeAttribute();
356: if (codeAttr == null || transformers == null)
357: return;
358:
359: for (t = transformers; t != null; t = t.getNext())
360: t.initialize(cp, codeAttr);
361:
362: CodeIterator iterator = codeAttr.iterator();
363: while (iterator.hasNext()) {
364: try {
365: int pos = iterator.next();
366: for (t = transformers; t != null; t = t.getNext())
367: pos = t.transform(clazz, pos, iterator, cp);
368: } catch (BadBytecode e) {
369: throw new CannotCompileException(e);
370: }
371: }
372:
373: int locals = 0;
374: for (t = transformers; t != null; t = t.getNext()) {
375: int s = t.extraLocals();
376: if (s > locals)
377: locals = s;
378: }
379:
380: for (t = transformers; t != null; t = t.getNext())
381: t.clean();
382:
383: codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals);
384: }
385: }
|