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