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.expr;
017:
018: import javassist.bytecode.*;
019: import javassist.CtClass;
020: import javassist.CannotCompileException;
021:
022: /**
023: * A translator of method bodies.
024: *
025: * <p>The users can define a subclass of this class to customize how to
026: * modify a method body. The overall architecture is similar to the
027: * strategy pattern.
028: *
029: * <p>If <code>instrument()</code> is called in
030: * <code>CtMethod</code>, the method body is scanned from the beginning
031: * to the end.
032: * Whenever an expression, such as a method call and a <tt>new</tt>
033: * expression (object creation),
034: * is found, <code>edit()</code> is called in <code>ExprEdit</code>.
035: * <code>edit()</code> can inspect and modify the given expression.
036: * The modification is reflected on the original method body. If
037: * <code>edit()</code> does nothing, the original method body is not
038: * changed.
039: *
040: * <p>The following code is an example:
041: *
042: * <ul><pre>
043: * CtMethod cm = ...;
044: * cm.instrument(new ExprEditor() {
045: * public void edit(MethodCall m) throws CannotCompileException {
046: * if (m.getClassName().equals("Point")) {
047: * System.out.println(m.getMethodName() + " line: "
048: * + m.getLineNumber());
049: * }
050: * });
051: * </pre></ul>
052: *
053: * <p>This code inspects all method calls appearing in the method represented
054: * by <code>cm</code> and it prints the names and the line numbers of the
055: * methods declared in class <code>Point</code>. This code does not modify
056: * the body of the method represented by <code>cm</code>. If the method
057: * body must be modified, call <code>replace()</code>
058: * in <code>MethodCall</code>.
059: *
060: * @see javassist.CtClass#instrument(ExprEditor)
061: * @see javassist.CtMethod#instrument(ExprEditor)
062: * @see javassist.CtConstructor#instrument(ExprEditor)
063: * @see MethodCall
064: * @see NewExpr
065: * @see FieldAccess
066: *
067: * @see javassist.CodeConverter
068: */
069: public class ExprEditor {
070: /**
071: * Default constructor. It does nothing.
072: */
073: public ExprEditor() {
074: }
075:
076: /**
077: * Undocumented method. Do not use; internal-use only.
078: */
079: public boolean doit(CtClass clazz, MethodInfo minfo)
080: throws CannotCompileException {
081: CodeAttribute codeAttr = minfo.getCodeAttribute();
082: if (codeAttr == null)
083: return false;
084:
085: CodeIterator iterator = codeAttr.iterator();
086: boolean edited = false;
087: LoopContext context = new LoopContext(codeAttr.getMaxLocals());
088:
089: while (iterator.hasNext())
090: if (loopBody(iterator, clazz, minfo, context))
091: edited = true;
092:
093: ExceptionTable et = codeAttr.getExceptionTable();
094: int n = et.size();
095: for (int i = 0; i < n; ++i) {
096: Handler h = new Handler(et, i, iterator, clazz, minfo);
097: edit(h);
098: if (h.edited()) {
099: edited = true;
100: context.updateMax(h.locals(), h.stack());
101: }
102: }
103:
104: codeAttr.setMaxLocals(context.maxLocals);
105: codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack);
106: return edited;
107: }
108:
109: /**
110: * Visits each bytecode in the given range.
111: */
112: boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context,
113: CodeIterator iterator, int endPos)
114: throws CannotCompileException {
115: boolean edited = false;
116: while (iterator.hasNext() && iterator.lookAhead() < endPos) {
117: int size = iterator.getCodeLength();
118: if (loopBody(iterator, clazz, minfo, context)) {
119: edited = true;
120: int size2 = iterator.getCodeLength();
121: if (size != size2) // the body was modified.
122: endPos += size2 - size;
123: }
124: }
125:
126: return edited;
127: }
128:
129: final static class NewOp {
130: NewOp next;
131: int pos;
132: String type;
133:
134: NewOp(NewOp n, int p, String t) {
135: next = n;
136: pos = p;
137: type = t;
138: }
139: }
140:
141: final static class LoopContext {
142: NewOp newList;
143: int maxLocals;
144: int maxStack;
145:
146: LoopContext(int locals) {
147: maxLocals = locals;
148: maxStack = 0;
149: newList = null;
150: }
151:
152: void updateMax(int locals, int stack) {
153: if (maxLocals < locals)
154: maxLocals = locals;
155:
156: if (maxStack < stack)
157: maxStack = stack;
158: }
159: }
160:
161: final boolean loopBody(CodeIterator iterator, CtClass clazz,
162: MethodInfo minfo, LoopContext context)
163: throws CannotCompileException {
164: try {
165: Expr expr = null;
166: int pos = iterator.next();
167: int c = iterator.byteAt(pos);
168:
169: if (c < Opcode.GETSTATIC) // c < 178
170: /* skip */;
171: else if (c < Opcode.NEWARRAY) { // c < 188
172: if (c == Opcode.INVOKESTATIC
173: || c == Opcode.INVOKEINTERFACE
174: || c == Opcode.INVOKEVIRTUAL) {
175: expr = new MethodCall(pos, iterator, clazz, minfo);
176: edit((MethodCall) expr);
177: } else if (c == Opcode.GETFIELD
178: || c == Opcode.GETSTATIC
179: || c == Opcode.PUTFIELD
180: || c == Opcode.PUTSTATIC) {
181: expr = new FieldAccess(pos, iterator, clazz, minfo,
182: c);
183: edit((FieldAccess) expr);
184: } else if (c == Opcode.NEW) {
185: int index = iterator.u16bitAt(pos + 1);
186: context.newList = new NewOp(context.newList, pos,
187: minfo.getConstPool().getClassInfo(index));
188: } else if (c == Opcode.INVOKESPECIAL) {
189: NewOp newList = context.newList;
190: if (newList != null
191: && minfo.getConstPool().isConstructor(
192: newList.type,
193: iterator.u16bitAt(pos + 1)) > 0) {
194: expr = new NewExpr(pos, iterator, clazz, minfo,
195: newList.type, newList.pos);
196: edit((NewExpr) expr);
197: context.newList = newList.next;
198: } else {
199: MethodCall mcall = new MethodCall(pos,
200: iterator, clazz, minfo);
201: if (mcall.getMethodName().equals(
202: MethodInfo.nameInit)) {
203: ConstructorCall ccall = new ConstructorCall(
204: pos, iterator, clazz, minfo);
205: expr = ccall;
206: edit(ccall);
207: } else {
208: expr = mcall;
209: edit(mcall);
210: }
211: }
212: }
213: } else { // c >= 188
214: if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY
215: || c == Opcode.MULTIANEWARRAY) {
216: expr = new NewArray(pos, iterator, clazz, minfo, c);
217: edit((NewArray) expr);
218: } else if (c == Opcode.INSTANCEOF) {
219: expr = new Instanceof(pos, iterator, clazz, minfo);
220: edit((Instanceof) expr);
221: } else if (c == Opcode.CHECKCAST) {
222: expr = new Cast(pos, iterator, clazz, minfo);
223: edit((Cast) expr);
224: }
225: }
226:
227: if (expr != null && expr.edited()) {
228: context.updateMax(expr.locals(), expr.stack());
229: return true;
230: } else
231: return false;
232: } catch (BadBytecode e) {
233: throw new CannotCompileException(e);
234: }
235: }
236:
237: /**
238: * Edits a <tt>new</tt> expression (overridable).
239: * The default implementation performs nothing.
240: *
241: * @param e the <tt>new</tt> expression creating an object.
242: */
243: public void edit(NewExpr e) throws CannotCompileException {
244: }
245:
246: /**
247: * Edits an expression for array creation (overridable).
248: * The default implementation performs nothing.
249: *
250: * @param a the <tt>new</tt> expression for creating an array.
251: * @throws CannotCompileException
252: */
253: public void edit(NewArray a) throws CannotCompileException {
254: }
255:
256: /**
257: * Edits a method call (overridable).
258: *
259: * The default implementation performs nothing.
260: */
261: public void edit(MethodCall m) throws CannotCompileException {
262: }
263:
264: /**
265: * Edits a constructor call (overridable).
266: * The constructor call is either
267: * <code>super()</code> or <code>this()</code>
268: * included in a constructor body.
269: *
270: * The default implementation performs nothing.
271: *
272: * @see #edit(NewExpr)
273: */
274: public void edit(ConstructorCall c) throws CannotCompileException {
275: }
276:
277: /**
278: * Edits a field-access expression (overridable).
279: * Field access means both read and write.
280: * The default implementation performs nothing.
281: */
282: public void edit(FieldAccess f) throws CannotCompileException {
283: }
284:
285: /**
286: * Edits an instanceof expression (overridable).
287: * The default implementation performs nothing.
288: */
289: public void edit(Instanceof i) throws CannotCompileException {
290: }
291:
292: /**
293: * Edits an expression for explicit type casting (overridable).
294: * The default implementation performs nothing.
295: */
296: public void edit(Cast c) throws CannotCompileException {
297: }
298:
299: /**
300: * Edits a catch clause (overridable).
301: * The default implementation performs nothing.
302: */
303: public void edit(Handler h) throws CannotCompileException {
304: }
305: }
|