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