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