/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2005 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: * *
* CtMethod cm = ...; * cm.instrument(new ExprEditor() { * public void edit(MethodCall m) throws CannotCompileException { * if (m.getClassName().equals("Point")) { * System.out.println(m.getMethodName() + " line: " * + m.getLineNumber()); * } * }); ** *
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() {}
static class NewOp {
NewOp next;
int pos;
String type;
NewOp(NewOp n, int p, String t) {
next = n;
pos = p;
type = t;
}
}
/**
* 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;
int maxLocals = codeAttr.getMaxLocals();
int maxStack = 0;
NewOp newList = null;
ConstPool cp = minfo.getConstPool();
while (iterator.hasNext())
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);
newList = new NewOp(newList, pos,
cp.getClassInfo(index));
}
else if (c == Opcode.INVOKESPECIAL) {
if (newList != null && cp.isConstructor(newList.type,
iterator.u16bitAt(pos + 1)) > 0) {
expr = new NewExpr(pos, iterator, clazz, minfo,
newList.type, newList.pos);
edit((NewExpr)expr);
newList = newList.next;
}
else {
expr = new MethodCall(pos, iterator, clazz, minfo);
MethodCall mcall = (MethodCall)expr;
if (!mcall.getMethodName().equals(
MethodInfo.nameInit))
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()) {
edited = true;
maxLocals = max(maxLocals, expr.locals());
maxStack = max(maxStack, expr.stack());
}
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
}
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;
maxLocals = max(maxLocals, h.locals());
maxStack = max(maxStack, h.stack());
}
}
codeAttr.setMaxLocals(maxLocals);
codeAttr.setMaxStack(codeAttr.getMaxStack() + maxStack);
return edited;
}
private int max(int i, int j) {
return i > j ? i : j;
}
/**
* 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 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 {}
}