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.CannotCompileException; 19 import javassist.ClassPool; 20 import javassist.CtBehavior; 21 import javassist.CtClass; 22 import javassist.CtConstructor; 23 import javassist.CtPrimitiveType; 24 import javassist.NotFoundException; 25 import javassist.bytecode.AccessFlag; 26 import javassist.bytecode.BadBytecode; 27 import javassist.bytecode.Bytecode; 28 import javassist.bytecode.ClassFile; 29 import javassist.bytecode.CodeAttribute; 30 import javassist.bytecode.CodeIterator; 31 import javassist.bytecode.ConstPool; 32 import javassist.bytecode.ExceptionTable; 33 import javassist.bytecode.ExceptionsAttribute; 34 import javassist.bytecode.MethodInfo; 35 import javassist.bytecode.Opcode; 36 import javassist.compiler.Javac; 37 38 import java.util.Iterator; 39 import java.util.LinkedList; 40 41 /** 42 * Expression. 43 */ 44 public abstract class Expr implements Opcode { 45 int currentPos; 46 CodeIterator iterator; 47 CtClass thisClass; 48 MethodInfo thisMethod; 49 boolean edited; 50 int maxLocals, maxStack; 51 52 static final String javaLangObject = "java.lang.Object"; 53 54 /** 55 * Undocumented constructor. Do not use; internal-use only. 56 */ 57 protected Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { 58 currentPos = pos; 59 iterator = i; 60 thisClass = declaring; 61 thisMethod = m; 62 } 63 64 /** 65 * Returns the class that declares the method enclosing 66 * this expression. 67 * 68 * @since 3.7 69 */ 70 public CtClass getEnclosingClass() { return thisClass; } 71 72 protected final ConstPool getConstPool() { 73 return thisMethod.getConstPool(); 74 } 75 76 protected final boolean edited() { 77 return edited; 78 } 79 80 protected final int locals() { 81 return maxLocals; 82 } 83 84 protected final int stack() { 85 return maxStack; 86 } 87 88 /** 89 * Returns true if this method is static. 90 */ 91 protected final boolean withinStatic() { 92 return (thisMethod.getAccessFlags() & AccessFlag.STATIC) != 0; 93 } 94 95 /** 96 * Returns the constructor or method containing the expression. 97 */ 98 public CtBehavior where() { 99 MethodInfo mi = thisMethod; 100 CtBehavior[] cb = thisClass.getDeclaredBehaviors(); 101 for (int i = cb.length - 1; i >= 0; --i) 102 if (cb[i].getMethodInfo2() == mi) 103 return cb[i]; 104 105 CtConstructor init = thisClass.getClassInitializer(); 106 if (init != null && init.getMethodInfo2() == mi) 107 return init; 108 109 /* getDeclaredBehaviors() returns a list of methods/constructors. 110 * Although the list is cached in a CtClass object, it might be 111 * recreated for some reason. Thus, the member name and the signature 112 * must be also checked. 113 */ 114 for (int i = cb.length - 1; i >= 0; --i) { 115 if (thisMethod.getName().equals(cb[i].getMethodInfo2().getName()) 116 && thisMethod.getDescriptor() 117 .equals(cb[i].getMethodInfo2().getDescriptor())) { 118 return cb[i]; 119 } 120 } 121 122 throw new RuntimeException("fatal: not found"); 123 } 124 125 /** 126 * Returns the list of exceptions that the expression may throw. This list 127 * includes both the exceptions that the try-catch statements including the 128 * expression can catch and the exceptions that the throws declaration 129 * allows the method to throw. 130 */ 131 public CtClass[] mayThrow() { 132 ClassPool pool = thisClass.getClassPool(); 133 ConstPool cp = thisMethod.getConstPool(); 134 LinkedList list = new LinkedList(); 135 try { 136 CodeAttribute ca = thisMethod.getCodeAttribute(); 137 ExceptionTable et = ca.getExceptionTable(); 138 int pos = currentPos; 139 int n = et.size(); 140 for (int i = 0; i < n; ++i) 141 if (et.startPc(i) <= pos && pos < et.endPc(i)) { 142 int t = et.catchType(i); 143 if (t > 0) 144 try { 145 addClass(list, pool.get(cp.getClassInfo(t))); 146 } 147 catch (NotFoundException e) { 148 } 149 } 150 } 151 catch (NullPointerException e) { 152 } 153 154 ExceptionsAttribute ea = thisMethod.getExceptionsAttribute(); 155 if (ea != null) { 156 String[] exceptions = ea.getExceptions(); 157 if (exceptions != null) { 158 int n = exceptions.length; 159 for (int i = 0; i < n; ++i) 160 try { 161 addClass(list, pool.get(exceptions[i])); 162 } 163 catch (NotFoundException e) { 164 } 165 } 166 } 167 168 return (CtClass[])list.toArray(new CtClass[list.size()]); 169 } 170 171 private static void addClass(LinkedList list, CtClass c) { 172 Iterator it = list.iterator(); 173 while (it.hasNext()) 174 if (it.next() == c) 175 return; 176 177 list.add(c); 178 } 179 180 /** 181 * Returns the index of the bytecode corresponding to the expression. It is 182 * the index into the byte array containing the Java bytecode that 183 * implements the method. 184 */ 185 public int indexOfBytecode() { 186 return currentPos; 187 } 188 189 /** 190 * Returns the line number of the source line containing the expression. 191 * 192 * @return -1 if this information is not available. 193 */ 194 public int getLineNumber() { 195 return thisMethod.getLineNumber(currentPos); 196 } 197 198 /** 199 * Returns the source file containing the expression. 200 * 201 * @return null if this information is not available. 202 */ 203 public String getFileName() { 204 ClassFile cf = thisClass.getClassFile2(); 205 if (cf == null) 206 return null; 207 else 208 return cf.getSourceFile(); 209 } 210 211 static final boolean checkResultValue(CtClass retType, String prog) 212 throws CannotCompileException { 213 /* 214 * Is $_ included in the source code? 215 */ 216 boolean hasIt = (prog.indexOf(Javac.resultVarName) >= 0); 217 if (!hasIt && retType != CtClass.voidType) 218 throw new CannotCompileException( 219 "the resulting value is not stored in " 220 + Javac.resultVarName); 221 222 return hasIt; 223 } 224 225 /* 226 * If isStaticCall is true, null is assigned to $0. So $0 must be declared 227 * by calling Javac.recordParams(). 228 * 229 * After executing this method, the current stack depth might be less than 230 * 0. 231 */ 232 static final void storeStack(CtClass[] params, boolean isStaticCall, 233 int regno, Bytecode bytecode) { 234 storeStack0(0, params.length, params, regno + 1, bytecode); 235 if (isStaticCall) 236 bytecode.addOpcode(ACONST_NULL); 237 238 bytecode.addAstore(regno); 239 } 240 241 private static void storeStack0(int i, int n, CtClass[] params, int regno, 242 Bytecode bytecode) { 243 if (i >= n) 244 return; 245 else { 246 CtClass c = params[i]; 247 int size; 248 if (c instanceof CtPrimitiveType) 249 size = ((CtPrimitiveType)c).getDataSize(); 250 else 251 size = 1; 252 253 storeStack0(i + 1, n, params, regno + size, bytecode); 254 bytecode.addStore(regno, c); 255 } 256 } 257 258 // The implementation of replace() should call thisClass.checkModify() 259 // so that isModify() will return true. Otherwise, thisClass.classfile 260 // might be released during compilation and the compiler might generate 261 // bytecode with a wrong copy of ConstPool. 262 263 /** 264 * Replaces this expression with the bytecode derived from 265 * the given source text. 266 * 267 * @param statement a Java statement except try-catch. 268 */ 269 public abstract void replace(String statement) throws CannotCompileException; 270 271 /** 272 * Replaces this expression with the bytecode derived from 273 * the given source text and <code>ExprEditor</code>. 274 * 275 * @param statement a Java statement except try-catch. 276 * @param recursive if not null, the substituted bytecode 277 * is recursively processed by the given 278 * <code>ExprEditor</code>. 279 * @since 3.1 280 */ 281 public void replace(String statement, ExprEditor recursive) 282 throws CannotCompileException 283 { 284 replace(statement); 285 if (recursive != null) 286 runEditor(recursive, iterator); 287 } 288 289 protected void replace0(int pos, Bytecode bytecode, int size) 290 throws BadBytecode { 291 byte[] code = bytecode.get(); 292 edited = true; 293 int gap = code.length - size; 294 for (int i = 0; i < size; ++i) 295 iterator.writeByte(NOP, pos + i); 296 297 if (gap > 0) 298 pos = iterator.insertGapAt(pos, gap, false).position; 299 300 iterator.write(code, pos); 301 iterator.insert(bytecode.getExceptionTable(), pos); 302 maxLocals = bytecode.getMaxLocals(); 303 maxStack = bytecode.getMaxStack(); 304 } 305 306 protected void runEditor(ExprEditor ed, CodeIterator oldIterator) 307 throws CannotCompileException 308 { 309 CodeAttribute codeAttr = oldIterator.get(); 310 int orgLocals = codeAttr.getMaxLocals(); 311 int orgStack = codeAttr.getMaxStack(); 312 int newLocals = locals(); 313 codeAttr.setMaxStack(stack()); 314 codeAttr.setMaxLocals(newLocals); 315 ExprEditor.LoopContext context 316 = new ExprEditor.LoopContext(newLocals); 317 int size = oldIterator.getCodeLength(); 318 int endPos = oldIterator.lookAhead(); 319 oldIterator.move(currentPos); 320 if (ed.doit(thisClass, thisMethod, context, oldIterator, endPos)) 321 edited = true; 322 323 oldIterator.move(endPos + oldIterator.getCodeLength() - size); 324 codeAttr.setMaxLocals(orgLocals); 325 codeAttr.setMaxStack(orgStack); 326 maxLocals = context.maxLocals; 327 maxStack += context.maxStack; 328 } 329 } 330