com.feilong.lib.javassist.expr.ExprEditor Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- 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,
* or the Apache License Version 2.0.
*
* 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 com.feilong.lib.javassist.expr;
import com.feilong.lib.javassist.CannotCompileException;
import com.feilong.lib.javassist.CtClass;
import com.feilong.lib.javassist.bytecode.BadBytecode;
import com.feilong.lib.javassist.bytecode.CodeAttribute;
import com.feilong.lib.javassist.bytecode.CodeIterator;
import com.feilong.lib.javassist.bytecode.ExceptionTable;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.bytecode.Opcode;
/**
* 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 com.feilong.lib.javassist.CtClass#instrument(ExprEditor)
* @see com.feilong.lib.javassist.CtMethod#instrument(ExprEditor)
* @see com.feilong.lib.javassist.CtConstructor#instrument(ExprEditor)
* @see MethodCall
* @see NewExpr
* @see FieldAccess
*
* @see com.feilong.lib.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){
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){
/* 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;
}
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{
}
}