• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 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.expr;
17 
18 import javassist.bytecode.*;
19 import javassist.CtClass;
20 import javassist.CannotCompileException;
21 
22 /**
23  * A translator of method bodies.
24  *
25  * <p>The users can define a subclass of this class to customize how to
26  * modify a method body.  The overall architecture is similar to the
27  * strategy pattern.
28  *
29  * <p>If <code>instrument()</code> is called in
30  * <code>CtMethod</code>, the method body is scanned from the beginning
31  * to the end.
32  * Whenever an expression, such as a method call and a <tt>new</tt>
33  * expression (object creation),
34  * is found, <code>edit()</code> is called in <code>ExprEdit</code>.
35  * <code>edit()</code> can inspect and modify the given expression.
36  * The modification is reflected on the original method body.  If
37  * <code>edit()</code> does nothing, the original method body is not
38  * changed.
39  *
40  * <p>The following code is an example:
41  *
42  * <ul><pre>
43  * CtMethod cm = ...;
44  * cm.instrument(new ExprEditor() {
45  *     public void edit(MethodCall m) throws CannotCompileException {
46  *         if (m.getClassName().equals("Point")) {
47  *             System.out.println(m.getMethodName() + " line: "
48  *                                + m.getLineNumber());
49  *     }
50  * });
51  * </pre></ul>
52  *
53  * <p>This code inspects all method calls appearing in the method represented
54  * by <code>cm</code> and it prints the names and the line numbers of the
55  * methods declared in class <code>Point</code>.  This code does not modify
56  * the body of the method represented by <code>cm</code>.  If the method
57  * body must be modified, call <code>replace()</code>
58  * in <code>MethodCall</code>.
59  *
60  * @see javassist.CtClass#instrument(ExprEditor)
61  * @see javassist.CtMethod#instrument(ExprEditor)
62  * @see javassist.CtConstructor#instrument(ExprEditor)
63  * @see MethodCall
64  * @see NewExpr
65  * @see FieldAccess
66  *
67  * @see javassist.CodeConverter
68  */
69 public class ExprEditor {
70     /**
71      * Default constructor.  It does nothing.
72      */
ExprEditor()73     public ExprEditor() {}
74 
75     /**
76      * Undocumented method.  Do not use; internal-use only.
77      */
doit(CtClass clazz, MethodInfo minfo)78     public boolean doit(CtClass clazz, MethodInfo minfo)
79         throws CannotCompileException
80     {
81         CodeAttribute codeAttr = minfo.getCodeAttribute();
82         if (codeAttr == null)
83             return false;
84 
85         CodeIterator iterator = codeAttr.iterator();
86         boolean edited = false;
87         LoopContext context = new LoopContext(codeAttr.getMaxLocals());
88 
89         while (iterator.hasNext())
90             if (loopBody(iterator, clazz, minfo, context))
91                 edited = true;
92 
93         ExceptionTable et = codeAttr.getExceptionTable();
94         int n = et.size();
95         for (int i = 0; i < n; ++i) {
96             Handler h = new Handler(et, i, iterator, clazz, minfo);
97             edit(h);
98             if (h.edited()) {
99                 edited = true;
100                 context.updateMax(h.locals(), h.stack());
101             }
102         }
103 
104         // codeAttr might be modified by other partiess
105         // so I check the current value of max-locals.
106         if (codeAttr.getMaxLocals() < context.maxLocals)
107             codeAttr.setMaxLocals(context.maxLocals);
108 
109         codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack);
110         try {
111             if (edited)
112                 minfo.rebuildStackMapIf6(clazz.getClassPool(),
113                                          clazz.getClassFile2());
114         }
115         catch (BadBytecode b) {
116             throw new CannotCompileException(b.getMessage(), b);
117         }
118 
119         return edited;
120     }
121 
122     /**
123      * Visits each bytecode in the given range.
124      */
doit(CtClass clazz, MethodInfo minfo, LoopContext context, CodeIterator iterator, int endPos)125     boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context,
126                  CodeIterator iterator, int endPos)
127         throws CannotCompileException
128     {
129         boolean edited = false;
130         while (iterator.hasNext() && iterator.lookAhead() < endPos) {
131             int size = iterator.getCodeLength();
132             if (loopBody(iterator, clazz, minfo, context)) {
133                 edited = true;
134                 int size2 = iterator.getCodeLength();
135                 if (size != size2)  // the body was modified.
136                     endPos += size2 - size;
137             }
138         }
139 
140         return edited;
141     }
142 
143     final static class NewOp {
144         NewOp next;
145         int pos;
146         String type;
147 
NewOp(NewOp n, int p, String t)148         NewOp(NewOp n, int p, String t) {
149             next = n;
150             pos = p;
151             type = t;
152         }
153     }
154 
155     final static class LoopContext {
156         NewOp newList;
157         int maxLocals;
158         int maxStack;
159 
LoopContext(int locals)160         LoopContext(int locals) {
161             maxLocals = locals;
162             maxStack = 0;
163             newList = null;
164         }
165 
updateMax(int locals, int stack)166         void updateMax(int locals, int stack) {
167             if (maxLocals < locals)
168                 maxLocals = locals;
169 
170             if (maxStack < stack)
171                 maxStack = stack;
172         }
173     }
174 
loopBody(CodeIterator iterator, CtClass clazz, MethodInfo minfo, LoopContext context)175     final boolean loopBody(CodeIterator iterator, CtClass clazz,
176                            MethodInfo minfo, LoopContext context)
177         throws CannotCompileException
178     {
179         try {
180             Expr expr = null;
181             int pos = iterator.next();
182             int c = iterator.byteAt(pos);
183 
184             if (c < Opcode.GETSTATIC)   // c < 178
185                 /* skip */;
186             else if (c < Opcode.NEWARRAY) { // c < 188
187                 if (c == Opcode.INVOKESTATIC
188                     || c == Opcode.INVOKEINTERFACE
189                     || c == Opcode.INVOKEVIRTUAL) {
190                     expr = new MethodCall(pos, iterator, clazz, minfo);
191                     edit((MethodCall)expr);
192                 }
193                 else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC
194                          || c == Opcode.PUTFIELD
195                          || c == Opcode.PUTSTATIC) {
196                     expr = new FieldAccess(pos, iterator, clazz, minfo, c);
197                     edit((FieldAccess)expr);
198                 }
199                 else if (c == Opcode.NEW) {
200                     int index = iterator.u16bitAt(pos + 1);
201                     context.newList = new NewOp(context.newList, pos,
202                                         minfo.getConstPool().getClassInfo(index));
203                 }
204                 else if (c == Opcode.INVOKESPECIAL) {
205                     NewOp newList = context.newList;
206                     if (newList != null
207                         && minfo.getConstPool().isConstructor(newList.type,
208                                             iterator.u16bitAt(pos + 1)) > 0) {
209                         expr = new NewExpr(pos, iterator, clazz, minfo,
210                                            newList.type, newList.pos);
211                         edit((NewExpr)expr);
212                         context.newList = newList.next;
213                     }
214                     else {
215                         MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo);
216                         if (mcall.getMethodName().equals(MethodInfo.nameInit)) {
217                             ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo);
218                             expr = ccall;
219                             edit(ccall);
220                         }
221                         else {
222                             expr = mcall;
223                             edit(mcall);
224                         }
225                     }
226                 }
227             }
228             else {  // c >= 188
229                 if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY
230                     || c == Opcode.MULTIANEWARRAY) {
231                     expr = new NewArray(pos, iterator, clazz, minfo, c);
232                     edit((NewArray)expr);
233                 }
234                 else if (c == Opcode.INSTANCEOF) {
235                     expr = new Instanceof(pos, iterator, clazz, minfo);
236                     edit((Instanceof)expr);
237                 }
238                 else if (c == Opcode.CHECKCAST) {
239                     expr = new Cast(pos, iterator, clazz, minfo);
240                     edit((Cast)expr);
241                 }
242             }
243 
244             if (expr != null && expr.edited()) {
245                 context.updateMax(expr.locals(), expr.stack());
246                 return true;
247             }
248             else
249                 return false;
250         }
251         catch (BadBytecode e) {
252             throw new CannotCompileException(e);
253         }
254     }
255 
256     /**
257      * Edits a <tt>new</tt> expression (overridable).
258      * The default implementation performs nothing.
259      *
260      * @param e         the <tt>new</tt> expression creating an object.
261      */
edit(NewExpr e)262     public void edit(NewExpr e) throws CannotCompileException {}
263 
264     /**
265      * Edits an expression for array creation (overridable).
266      * The default implementation performs nothing.
267      *
268      * @param a         the <tt>new</tt> expression for creating an array.
269      * @throws CannotCompileException
270      */
edit(NewArray a)271     public void edit(NewArray a) throws CannotCompileException {}
272 
273     /**
274      * Edits a method call (overridable).
275      *
276      * The default implementation performs nothing.
277      */
edit(MethodCall m)278     public void edit(MethodCall m) throws CannotCompileException {}
279 
280     /**
281      * Edits a constructor call (overridable).
282      * The constructor call is either
283      * <code>super()</code> or <code>this()</code>
284      * included in a constructor body.
285      *
286      * The default implementation performs nothing.
287      *
288      * @see #edit(NewExpr)
289      */
edit(ConstructorCall c)290     public void edit(ConstructorCall c) throws CannotCompileException {}
291 
292     /**
293      * Edits a field-access expression (overridable).
294      * Field access means both read and write.
295      * The default implementation performs nothing.
296      */
edit(FieldAccess f)297     public void edit(FieldAccess f) throws CannotCompileException {}
298 
299     /**
300      * Edits an instanceof expression (overridable).
301      * The default implementation performs nothing.
302      */
edit(Instanceof i)303     public void edit(Instanceof i) throws CannotCompileException {}
304 
305     /**
306      * Edits an expression for explicit type casting (overridable).
307      * The default implementation performs nothing.
308      */
edit(Cast c)309     public void edit(Cast c) throws CannotCompileException {}
310 
311     /**
312      * Edits a catch clause (overridable).
313      * The default implementation performs nothing.
314      */
edit(Handler h)315     public void edit(Handler h) throws CannotCompileException {}
316 }
317