/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.bytecode.*; import javassist.CtClass; import javassist.CannotCompileException; /** * A translator of method bodies. * *

The users can define a subclass of this class to customize how to * modify a method body. The overall architecture is similar to the * strategy pattern. * *

If instrument() is called in * CtMethod, the method body is scanned from the beginning * to the end. * Whenever an expression, such as a method call and a new * expression (object creation), * is found, edit() is called in ExprEdit. * edit() can inspect and modify the given expression. * The modification is reflected on the original method body. If * edit() does nothing, the original method body is not * changed. * *

The following code is an example: * *

* *

This code inspects all method calls appearing in the method represented * by cm and it prints the names and the line numbers of the * methods declared in class Point. This code does not modify * the body of the method represented by cm. If the method * body must be modified, call replace() * in MethodCall. * * @see javassist.CtClass#instrument(ExprEditor) * @see javassist.CtMethod#instrument(ExprEditor) * @see javassist.CtConstructor#instrument(ExprEditor) * @see MethodCall * @see NewExpr * @see FieldAccess * * @see javassist.CodeConverter */ public class ExprEditor { /** * Default constructor. It does nothing. */ public ExprEditor() {} /** * Undocumented method. Do not use; internal-use only. */ public boolean doit(CtClass clazz, MethodInfo minfo) throws CannotCompileException { CodeAttribute codeAttr = minfo.getCodeAttribute(); if (codeAttr == null) return false; CodeIterator iterator = codeAttr.iterator(); boolean edited = false; LoopContext context = new LoopContext(codeAttr.getMaxLocals()); while (iterator.hasNext()) if (loopBody(iterator, clazz, minfo, context)) edited = true; ExceptionTable et = codeAttr.getExceptionTable(); int n = et.size(); for (int i = 0; i < n; ++i) { Handler h = new Handler(et, i, iterator, clazz, minfo); edit(h); if (h.edited()) { edited = true; context.updateMax(h.locals(), h.stack()); } } // codeAttr might be modified by other partiess // so I check the current value of max-locals. if (codeAttr.getMaxLocals() < context.maxLocals) codeAttr.setMaxLocals(context.maxLocals); codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack); try { if (edited) minfo.rebuildStackMapIf6(clazz.getClassPool(), clazz.getClassFile2()); } catch (BadBytecode b) { throw new CannotCompileException(b.getMessage(), b); } return edited; } /** * Visits each bytecode in the given range. */ boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context, CodeIterator iterator, int endPos) throws CannotCompileException { boolean edited = false; while (iterator.hasNext() && iterator.lookAhead() < endPos) { int size = iterator.getCodeLength(); if (loopBody(iterator, clazz, minfo, context)) { edited = true; int size2 = iterator.getCodeLength(); if (size != size2) // the body was modified. endPos += size2 - size; } } return edited; } final static class NewOp { NewOp next; int pos; String type; NewOp(NewOp n, int p, String t) { next = n; pos = p; type = t; } } final static class LoopContext { NewOp newList; int maxLocals; int maxStack; LoopContext(int locals) { maxLocals = locals; maxStack = 0; newList = null; } void updateMax(int locals, int stack) { if (maxLocals < locals) maxLocals = locals; if (maxStack < stack) maxStack = stack; } } final boolean loopBody(CodeIterator iterator, CtClass clazz, MethodInfo minfo, LoopContext context) throws CannotCompileException { try { Expr expr = null; int pos = iterator.next(); int c = iterator.byteAt(pos); if (c < Opcode.GETSTATIC) // c < 178 /* skip */; else if (c < Opcode.NEWARRAY) { // c < 188 if (c == Opcode.INVOKESTATIC || c == Opcode.INVOKEINTERFACE || c == Opcode.INVOKEVIRTUAL) { expr = new MethodCall(pos, iterator, clazz, minfo); edit((MethodCall)expr); } else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC || c == Opcode.PUTFIELD || c == Opcode.PUTSTATIC) { expr = new FieldAccess(pos, iterator, clazz, minfo, c); edit((FieldAccess)expr); } else if (c == Opcode.NEW) { int index = iterator.u16bitAt(pos + 1); context.newList = new NewOp(context.newList, pos, minfo.getConstPool().getClassInfo(index)); } else if (c == Opcode.INVOKESPECIAL) { NewOp newList = context.newList; if (newList != null && minfo.getConstPool().isConstructor(newList.type, iterator.u16bitAt(pos + 1)) > 0) { expr = new NewExpr(pos, iterator, clazz, minfo, newList.type, newList.pos); edit((NewExpr)expr); context.newList = newList.next; } else { MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo); if (mcall.getMethodName().equals(MethodInfo.nameInit)) { ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo); expr = ccall; edit(ccall); } else { expr = mcall; edit(mcall); } } } } else { // c >= 188 if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY || c == Opcode.MULTIANEWARRAY) { expr = new NewArray(pos, iterator, clazz, minfo, c); edit((NewArray)expr); } else if (c == Opcode.INSTANCEOF) { expr = new Instanceof(pos, iterator, clazz, minfo); edit((Instanceof)expr); } else if (c == Opcode.CHECKCAST) { expr = new Cast(pos, iterator, clazz, minfo); edit((Cast)expr); } } if (expr != null && expr.edited()) { context.updateMax(expr.locals(), expr.stack()); return true; } else return false; } catch (BadBytecode e) { throw new CannotCompileException(e); } } /** * Edits a new expression (overridable). * The default implementation performs nothing. * * @param e the new expression creating an object. */ public void edit(NewExpr e) throws CannotCompileException {} /** * Edits an expression for array creation (overridable). * The default implementation performs nothing. * * @param a the new expression for creating an array. * @throws CannotCompileException */ public void edit(NewArray a) throws CannotCompileException {} /** * Edits a method call (overridable). * * The default implementation performs nothing. */ public void edit(MethodCall m) throws CannotCompileException {} /** * Edits a constructor call (overridable). * The constructor call is either * super() or this() * included in a constructor body. * * The default implementation performs nothing. * * @see #edit(NewExpr) */ public void edit(ConstructorCall c) throws CannotCompileException {} /** * Edits a field-access expression (overridable). * Field access means both read and write. * The default implementation performs nothing. */ public void edit(FieldAccess f) throws CannotCompileException {} /** * Edits an instanceof expression (overridable). * The default implementation performs nothing. */ public void edit(Instanceof i) throws CannotCompileException {} /** * Edits an expression for explicit type casting (overridable). * The default implementation performs nothing. */ public void edit(Cast c) throws CannotCompileException {} /** * Edits a catch clause (overridable). * The default implementation performs nothing. */ public void edit(Handler h) throws CannotCompileException {} }