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