/Users/lyon/j4p/src/javassist/CodeConverter.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16 package javassist;
17
18 import javassist.bytecode.*;
19 import javassist.convert.*;
20
21 /**
22 * Simple translator of method bodies
23 * (also see the <code>javassist.expr</code> package).
24 *
25 * <p>Instances of this class specifies how to instrument of the
26 * bytecodes representing a method body. They are passed to
27 * <code>CtClass.instrument()</code> or
28 * <code>CtMethod.instrument()</code> as a parameter.
29 *
30 * <p>Example:
31 * <ul><pre>
32 * ClassPool cp = ClassPool.getDefault();
33 * CtClass point = cp.get("Point");
34 * CtClass singleton = cp.get("Singleton");
35 * CtClass client = cp.get("Client");
36 * CodeConverter conv = new CodeConverter();
37 * conv.replaceNew(point, singleton, "makePoint");
38 * client.instrument(conv);
39 * </pre></ul>
40 *
41 * <p>This program substitutes "<code>Singleton.makePoint()</code>"
42 * for all occurrences of "<code>new Point()</code>"
43 * appearing in methods declared in a <code>Client</code> class.
44 *
45 * @see javassist.CtClass#instrument(CodeConverter)
46 * @see javassist.CtMethod#instrument(CodeConverter)
47 * @see javassist.expr.ExprEditor
48 */
49 public class CodeConverter {
50 Transformer transformers = null;
51
52 /**
53 * Modify a method body so that instantiation of the specified class
54 * is replaced with a call to the specified static method. For example,
55 * <code>replaceNew(ctPoint, ctSingleton, "createPoint")</code>
56 * (where <code>ctPoint</code> and <code>ctSingleton</code> are
57 * compile-time classes for class <code>Point</code> and class
58 * <code>Singleton</code>, respectively)
59 * replaces all occurrences of:
60 *
61 * <ul><code>new Point(x, y)</code></ul>
62 *
63 * in the method body with:
64 *
65 * <ul><code>Singleton.createPoint(x, y)</code></ul>
66 *
67 * <p>This enables to intercept instantiation of <code>Point</code>
68 * and change the samentics. For example, the following
69 * <code>createPoint()</code> implements the singleton pattern:
70 *
71 * <ul><pre>public static Point createPoint(int x, int y) {
72 * if (aPoint == null)
73 * aPoint = new Point(x, y);
74 * return aPoint;
75 * }
76 * </pre></ul>
77 *
78 * <p>The static method call substituted for the original <code>new</code>
79 * expression must be
80 * able to receive the same set of parameters as the original
81 * constructor. If there are multiple constructors with different
82 * parameter types, then there must be multiple static methods
83 * with the same name but different parameter types.
84 *
85 * <p>The return type of the substituted static method must be
86 * the exactly same as the type of the instantiated class specified by
87 * <code>newClass</code>.
88 *
89 * @param newClass the instantiated class.
90 * @param calledClass the class in which the static method is
91 * declared.
92 * @param calledMethod the name of the static method.
93 */
94 public void replaceNew(CtClass newClass,
95 CtClass calledClass, String calledMethod) {
96 transformers = new TransformNew(transformers, newClass.getName(),
97 calledClass.getName(), calledMethod);
98 }
99
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,
118 CtClass newClass, String newFieldname) {
119 transformers = new TransformFieldAccess(transformers, field,
120 newClass.getName(),
121 newFieldname);
122 }
123
124 /**
125 * Modify a method body so that an expression reading the specified
126 * field is replaced with a call to the specified <i>static</i> method.
127 * This static method receives the target object of the original
128 * read expression as a parameter. It must return a value of
129 * the same type as the field.
130 *
131 * <p>For example, the program below
132 *
133 * <ul><pre>Point p = new Point();
134 * int newX = p.x + 3;</pre></ul>
135 *
136 * <p>can be translated into:
137 *
138 * <ul><pre>Point p = new Point();
139 * int newX = Accessor.readX(p) + 3;</pre></ul>
140 *
141 * <p>where
142 *
143 * <ul><pre>public class Accessor {
144 * public static int readX(Object target) { ... }
145 * }</pre></ul>
146 *
147 * <p>The type of the parameter of <code>readX()</code> must
148 * be <code>java.lang.Object</code> independently of the actual
149 * type of <code>target</code>. The return type must be the same
150 * as the field type.
151 *
152 * @param field the field.
153 * @param calledClass the class in which the static method is
154 * declared.
155 * @param calledMethod the name of the static method.
156 */
157 public void replaceFieldRead(CtField field,
158 CtClass calledClass, String calledMethod) {
159 transformers = new TransformReadField(transformers, field,
160 calledClass.getName(),
161 calledMethod);
162 }
163
164 /**
165 * Modify a method body so that an expression writing the specified
166 * field is replaced with a call to the specified static method.
167 * This static method receives two parameters: the target object of
168 * the original
169 * write expression and the assigned value. The return type of the
170 * static method is <code>void</code>.
171 *
172 * <p>For example, the program below
173 *
174 * <ul><pre>Point p = new Point();
175 * p.x = 3;</pre></ul>
176 *
177 * <p>can be translated into:
178 *
179 * <ul><pre>Point p = new Point();
180 * Accessor.writeX(3);</pre></ul>
181 *
182 * <p>where
183 *
184 * <ul><pre>public class Accessor {
185 * public static void writeX(Object target, int value) { ... }
186 * }</pre></ul>
187 *
188 * <p>The type of the first parameter of <code>writeX()</code> must
189 * be <code>java.lang.Object</code> independently of the actual
190 * type of <code>target</code>. The type of the second parameter
191 * is the same as the field type.
192 *
193 * @param field the field.
194 * @param calledClass the class in which the static method is
195 * declared.
196 * @param calledMethod the name of the static method.
197 */
198 public void replaceFieldWrite(CtField field,
199 CtClass calledClass, String calledMethod) {
200 transformers = new TransformWriteField(transformers, field,
201 calledClass.getName(),
202 calledMethod);
203 }
204
205 /**
206 * Modify method invocations in a method body so that a different
207 * method is invoked.
208 *
209 * <p>Note that the target object, the parameters, or
210 * the type of invocation
211 * (static method call, interface call, or private method call)
212 * are not modified. Only the method name is changed. The substituted
213 * method must have the same signature that the original one has.
214 * If the original method is a static method, the substituted method
215 * must be static.
216 *
217 * @param origMethod original method
218 * @param substMethod substituted method
219 */
220 public void redirectMethodCall(CtMethod origMethod,
221 CtMethod substMethod)
222 throws CannotCompileException {
223 String d1 = origMethod.getMethodInfo2().getDescriptor();
224 String d2 = substMethod.getMethodInfo2().getDescriptor();
225 if (!d1.equals(d2))
226 throw new CannotCompileException("signature mismatch");
227
228 transformers = new TransformCall(transformers, origMethod,
229 substMethod);
230 }
231
232 /**
233 * Insert a call to another method before an existing method call.
234 * That "before" method must be static. The return type must be
235 * <code>void</code>. As parameters, the before method receives
236 * the target object and all the parameters to the originally invoked
237 * method. For example, if the originally invoked method is
238 * <code>move()</code>:
239 *
240 * <ul><pre>class Point {
241 * Point move(int x, int y) { ... }
242 * }</pre></ul>
243 *
244 * <p>Then the before method must be something like this:
245 *
246 * <ul><pre>class Verbose {
247 * static void print(Point target, int x, int y) { ... }
248 * }</pre></ul>
249 *
250 * <p>The <code>CodeConverter</code> would translate bytecode
251 * equivalent to:
252 *
253 * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
254 *
255 * <p>into the bytecode equivalent to:
256 *
257 * <ul><pre>int tmp1 = x + y;
258 * int tmp2 = 0;
259 * Verbose.print(p, tmp1, tmp2);
260 * Point p2 = p.move(tmp1, tmp2);</pre></ul>
261 *
262 * @param origMethod the method originally invoked.
263 * @param beforeMethod the method invoked before
264 * <code>origMethod</code>.
265 */
266 public void insertBeforeMethod(CtMethod origMethod,
267 CtMethod beforeMethod)
268 throws CannotCompileException {
269 try {
270 transformers = new TransformBefore(transformers, origMethod,
271 beforeMethod);
272 } catch (NotFoundException e) {
273 throw new CannotCompileException(e);
274 }
275 }
276
277 /**
278 * Inserts a call to another method after an existing method call.
279 * That "after" method must be static. The return type must be
280 * <code>void</code>. As parameters, the after method receives
281 * the target object and all the parameters to the originally invoked
282 * method. For example, if the originally invoked method is
283 * <code>move()</code>:
284 *
285 * <ul><pre>class Point {
286 * Point move(int x, int y) { ... }
287 * }</pre></ul>
288 *
289 * <p>Then the after method must be something like this:
290 *
291 * <ul><pre>class Verbose {
292 * static void print(Point target, int x, int y) { ... }
293 * }</pre></ul>
294 *
295 * <p>The <code>CodeConverter</code> would translate bytecode
296 * equivalent to:
297 *
298 * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
299 *
300 * <p>into the bytecode equivalent to:
301 *
302 * <ul><pre>int tmp1 = x + y;
303 * int tmp2 = 0;
304 * Point p2 = p.move(tmp1, tmp2);
305 * Verbose.print(p, tmp1, tmp2);</pre></ul>
306 *
307 * @param origMethod the method originally invoked.
308 * @param afterMethod the method invoked after
309 * <code>origMethod</code>.
310 */
311 public void insertAfterMethod(CtMethod origMethod,
312 CtMethod afterMethod)
313 throws CannotCompileException {
314 try {
315 transformers = new TransformAfter(transformers, origMethod,
316 afterMethod);
317 } catch (NotFoundException e) {
318 throw new CannotCompileException(e);
319 }
320 }
321
322 /**
323 * Performs code conversion.
324 */
325 void doit(CtClass clazz, MethodInfo minfo, ConstPool cp)
326 throws CannotCompileException {
327 Transformer t;
328
329 CodeAttribute codeAttr = minfo.getCodeAttribute();
330 if (codeAttr == null || transformers == null)
331 return;
332
333 for (t = transformers; t != null; t = t.getNext())
334 t.initialize(cp, codeAttr);
335
336 CodeIterator iterator = codeAttr.iterator();
337 while (iterator.hasNext()) {
338 try {
339 int pos = iterator.next();
340 for (t = transformers; t != null; t = t.getNext())
341 pos = t.transform(clazz, pos, iterator, cp);
342 } catch (BadBytecode e) {
343 throw new CannotCompileException(e);
344 }
345 }
346
347 int locals = 0;
348 for (t = transformers; t != null; t = t.getNext()) {
349 int s = t.extraLocals();
350 if (s > locals)
351 locals = s;
352 }
353
354 for (t = transformers; t != null; t = t.getNext())
355 t.clean();
356
357 codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals);
358 }
359 }
360