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.ClassPool; 21 import javassist.CtBehavior; 22 import javassist.CtClass; 23 import javassist.CtConstructor; 24 import javassist.NotFoundException; 25 import javassist.bytecode.BadBytecode; 26 import javassist.bytecode.Bytecode; 27 import javassist.bytecode.CodeAttribute; 28 import javassist.bytecode.CodeIterator; 29 import javassist.bytecode.ConstPool; 30 import javassist.bytecode.Descriptor; 31 import javassist.bytecode.MethodInfo; 32 import javassist.bytecode.Opcode; 33 import javassist.compiler.CompileError; 34 import javassist.compiler.Javac; 35 import javassist.compiler.JvstCodeGen; 36 import javassist.compiler.JvstTypeChecker; 37 import javassist.compiler.ProceedHandler; 38 import javassist.compiler.ast.ASTList; 39 40 /** 41 * Object creation (<code>new</code> expression). 42 */ 43 public class NewExpr extends Expr { 44 String newTypeName; 45 int newPos; 46 47 /** 48 * Undocumented constructor. Do not use; internal-use only. 49 */ NewExpr(int pos, CodeIterator i, CtClass declaring, MethodInfo m, String type, int np)50 protected NewExpr(int pos, CodeIterator i, CtClass declaring, 51 MethodInfo m, String type, int np) { 52 super(pos, i, declaring, m); 53 newTypeName = type; 54 newPos = np; 55 } 56 57 /* 58 * Not used 59 * 60 private int getNameAndType(ConstPool cp) { 61 int pos = currentPos; 62 int c = iterator.byteAt(pos); 63 int index = iterator.u16bitAt(pos + 1); 64 65 if (c == INVOKEINTERFACE) 66 return cp.getInterfaceMethodrefNameAndType(index); 67 else 68 return cp.getMethodrefNameAndType(index); 69 } */ 70 71 /** 72 * Returns the method or constructor containing the <code>new</code> 73 * expression represented by this object. 74 */ 75 @Override where()76 public CtBehavior where() { return super.where(); } 77 78 /** 79 * Returns the line number of the source line containing the 80 * <code>new</code> expression. 81 * 82 * @return -1 if this information is not available. 83 */ 84 @Override getLineNumber()85 public int getLineNumber() { 86 return super.getLineNumber(); 87 } 88 89 /** 90 * Returns the source file containing the <code>new</code> expression. 91 * 92 * @return null if this information is not available. 93 */ 94 @Override getFileName()95 public String getFileName() { 96 return super.getFileName(); 97 } 98 99 /** 100 * Returns the class of the created object. 101 */ getCtClass()102 private CtClass getCtClass() throws NotFoundException { 103 return thisClass.getClassPool().get(newTypeName); 104 } 105 106 /** 107 * Returns the class name of the created object. 108 */ getClassName()109 public String getClassName() { 110 return newTypeName; 111 } 112 113 /** 114 * Get the signature of the constructor 115 * 116 * The signature is represented by a character string 117 * called method descriptor, which is defined in the JVM specification. 118 * 119 * @see javassist.CtBehavior#getSignature() 120 * @see javassist.bytecode.Descriptor 121 * @return the signature 122 */ getSignature()123 public String getSignature() { 124 ConstPool constPool = getConstPool(); 125 int methodIndex = iterator.u16bitAt(currentPos + 1); // constructor 126 return constPool.getMethodrefType(methodIndex); 127 } 128 129 /** 130 * Returns the constructor called for creating the object. 131 */ getConstructor()132 public CtConstructor getConstructor() throws NotFoundException { 133 ConstPool cp = getConstPool(); 134 int index = iterator.u16bitAt(currentPos + 1); 135 String desc = cp.getMethodrefType(index); 136 return getCtClass().getConstructor(desc); 137 } 138 139 /** 140 * Returns the list of exceptions that the expression may throw. 141 * This list includes both the exceptions that the try-catch statements 142 * including the expression can catch and the exceptions that 143 * the throws declaration allows the method to throw. 144 */ 145 @Override mayThrow()146 public CtClass[] mayThrow() { 147 return super.mayThrow(); 148 } 149 150 /* 151 * Returns the parameter types of the constructor. 152 153 public CtClass[] getParameterTypes() throws NotFoundException { 154 ConstPool cp = getConstPool(); 155 int index = iterator.u16bitAt(currentPos + 1); 156 String desc = cp.getMethodrefType(index); 157 return Descriptor.getParameterTypes(desc, thisClass.getClassPool()); 158 } 159 */ 160 canReplace()161 private int canReplace() throws CannotCompileException { 162 int op = iterator.byteAt(newPos + 3); 163 if (op == Opcode.DUP) // Typical single DUP or Javaflow DUP DUP2_X2 POP2 164 return ((iterator.byteAt(newPos + 4) == Opcode.DUP2_X2 165 && iterator.byteAt(newPos + 5) == Opcode.POP2)) ? 6 : 4; 166 else if (op == Opcode.DUP_X1 167 && iterator.byteAt(newPos + 4) == Opcode.SWAP) 168 return 5; 169 else 170 return 3; // for Eclipse. The generated code may include no DUP. 171 // throw new CannotCompileException( 172 // "sorry, cannot edit NEW followed by no DUP"); 173 } 174 175 /** 176 * Replaces the <code>new</code> expression with the bytecode derived from 177 * the given source text. 178 * 179 * <p>$0 is available but the value is null. 180 * 181 * @param statement a Java statement except try-catch. 182 */ 183 @Override replace(String statement)184 public void replace(String statement) throws CannotCompileException { 185 thisClass.getClassFile(); // to call checkModify(). 186 187 final int bytecodeSize = 3; 188 int pos = newPos; 189 190 int newIndex = iterator.u16bitAt(pos + 1); 191 192 /* delete the preceding NEW and DUP (or DUP_X1, SWAP) instructions. 193 */ 194 int codeSize = canReplace(); 195 int end = pos + codeSize; 196 for (int i = pos; i < end; ++i) 197 iterator.writeByte(NOP, i); 198 199 ConstPool constPool = getConstPool(); 200 pos = currentPos; 201 int methodIndex = iterator.u16bitAt(pos + 1); // constructor 202 203 String signature = constPool.getMethodrefType(methodIndex); 204 205 Javac jc = new Javac(thisClass); 206 ClassPool cp = thisClass.getClassPool(); 207 CodeAttribute ca = iterator.get(); 208 try { 209 CtClass[] params = Descriptor.getParameterTypes(signature, cp); 210 CtClass newType = cp.get(newTypeName); 211 int paramVar = ca.getMaxLocals(); 212 jc.recordParams(newTypeName, params, 213 true, paramVar, withinStatic()); 214 int retVar = jc.recordReturnType(newType, true); 215 jc.recordProceed(new ProceedForNew(newType, newIndex, 216 methodIndex)); 217 218 /* Is $_ included in the source code? 219 */ 220 checkResultValue(newType, statement); 221 222 Bytecode bytecode = jc.getBytecode(); 223 storeStack(params, true, paramVar, bytecode); 224 jc.recordLocalVariables(ca, pos); 225 226 bytecode.addConstZero(newType); 227 bytecode.addStore(retVar, newType); // initialize $_ 228 229 jc.compileStmnt(statement); 230 if (codeSize > 3) // if the original code includes DUP. 231 bytecode.addAload(retVar); 232 233 replace0(pos, bytecode, bytecodeSize); 234 } 235 catch (CompileError e) { throw new CannotCompileException(e); } 236 catch (NotFoundException e) { throw new CannotCompileException(e); } 237 catch (BadBytecode e) { 238 throw new CannotCompileException("broken method"); 239 } 240 } 241 242 static class ProceedForNew implements ProceedHandler { 243 CtClass newType; 244 int newIndex, methodIndex; 245 ProceedForNew(CtClass nt, int ni, int mi)246 ProceedForNew(CtClass nt, int ni, int mi) { 247 newType = nt; 248 newIndex = ni; 249 methodIndex = mi; 250 } 251 252 @Override doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)253 public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) 254 throws CompileError 255 { 256 bytecode.addOpcode(NEW); 257 bytecode.addIndex(newIndex); 258 bytecode.addOpcode(DUP); 259 gen.atMethodCallCore(newType, MethodInfo.nameInit, args, 260 false, true, -1, null); 261 gen.setType(newType); 262 } 263 264 @Override setReturnType(JvstTypeChecker c, ASTList args)265 public void setReturnType(JvstTypeChecker c, ASTList args) 266 throws CompileError 267 { 268 c.atMethodCallCore(newType, MethodInfo.nameInit, args); 269 c.setType(newType); 270 } 271 } 272 } 273