/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.*; import javassist.bytecode.*; import javassist.compiler.*; import javassist.compiler.ast.ASTList; /** * Object creation (<tt>new</tt> expression). */ public class NewExpr extends Expr { String newTypeName; int newPos; /** * Undocumented constructor. Do not use; internal-use only. */ protected NewExpr(int pos, CodeIterator i, CtClass declaring, MethodInfo m, String type, int np) { super(pos, i, declaring, m); newTypeName = type; newPos = np; } /* * Not used * private int getNameAndType(ConstPool cp) { int pos = currentPos; int c = iterator.byteAt(pos); int index = iterator.u16bitAt(pos + 1); if (c == INVOKEINTERFACE) return cp.getInterfaceMethodrefNameAndType(index); else return cp.getMethodrefNameAndType(index); } */ /** * Returns the method or constructor containing the <tt>new</tt> * expression represented by this object. */ public CtBehavior where() { return super.where(); } /** * Returns the line number of the source line containing the * <tt>new</tt> expression. * * @return -1 if this information is not available. */ public int getLineNumber() { return super.getLineNumber(); } /** * Returns the source file containing the <tt>new</tt> expression. * * @return null if this information is not available. */ public String getFileName() { return super.getFileName(); } /** * Returns the class of the created object. */ private CtClass getCtClass() throws NotFoundException { return thisClass.getClassPool().get(newTypeName); } /** * Returns the class name of the created object. */ public String getClassName() { return newTypeName; } /** * Get the signature of the constructor * * The signature is represented by a character string * called method descriptor, which is defined in the JVM specification. * * @see javassist.CtBehavior#getSignature() * @see javassist.bytecode.Descriptor * @return the signature */ public String getSignature() { ConstPool constPool = getConstPool(); int methodIndex = iterator.u16bitAt(currentPos + 1); // constructor return constPool.getMethodrefType(methodIndex); } /** * Returns the constructor called for creating the object. */ public CtConstructor getConstructor() throws NotFoundException { ConstPool cp = getConstPool(); int index = iterator.u16bitAt(currentPos + 1); String desc = cp.getMethodrefType(index); return getCtClass().getConstructor(desc); } /** * Returns the list of exceptions that the expression may throw. * This list includes both the exceptions that the try-catch statements * including the expression can catch and the exceptions that * the throws declaration allows the method to throw. */ public CtClass[] mayThrow() { return super.mayThrow(); } /* * Returns the parameter types of the constructor. public CtClass[] getParameterTypes() throws NotFoundException { ConstPool cp = getConstPool(); int index = iterator.u16bitAt(currentPos + 1); String desc = cp.getMethodrefType(index); return Descriptor.getParameterTypes(desc, thisClass.getClassPool()); } */ private int canReplace() throws CannotCompileException { int op = iterator.byteAt(newPos + 3); if (op == Opcode.DUP) return 4; else if (op == Opcode.DUP_X1 && iterator.byteAt(newPos + 4) == Opcode.SWAP) return 5; else return 3; // for Eclipse. The generated code may include no DUP. // throw new CannotCompileException( // "sorry, cannot edit NEW followed by no DUP"); } /** * Replaces the <tt>new</tt> expression with the bytecode derived from * the given source text. * * <p>$0 is available but the value is null. * * @param statement a Java statement except try-catch. */ public void replace(String statement) throws CannotCompileException { thisClass.getClassFile(); // to call checkModify(). final int bytecodeSize = 3; int pos = newPos; int newIndex = iterator.u16bitAt(pos + 1); /* delete the preceding NEW and DUP (or DUP_X1, SWAP) instructions. */ int codeSize = canReplace(); int end = pos + codeSize; for (int i = pos; i < end; ++i) iterator.writeByte(NOP, i); ConstPool constPool = getConstPool(); pos = currentPos; int methodIndex = iterator.u16bitAt(pos + 1); // constructor String signature = constPool.getMethodrefType(methodIndex); Javac jc = new Javac(thisClass); ClassPool cp = thisClass.getClassPool(); CodeAttribute ca = iterator.get(); try { CtClass[] params = Descriptor.getParameterTypes(signature, cp); CtClass newType = cp.get(newTypeName); int paramVar = ca.getMaxLocals(); jc.recordParams(newTypeName, params, true, paramVar, withinStatic()); int retVar = jc.recordReturnType(newType, true); jc.recordProceed(new ProceedForNew(newType, newIndex, methodIndex)); /* Is $_ included in the source code? */ checkResultValue(newType, statement); Bytecode bytecode = jc.getBytecode(); storeStack(params, true, paramVar, bytecode); jc.recordLocalVariables(ca, pos); bytecode.addConstZero(newType); bytecode.addStore(retVar, newType); // initialize $_ jc.compileStmnt(statement); if (codeSize > 3) // if the original code includes DUP. bytecode.addAload(retVar); replace0(pos, bytecode, bytecodeSize); } catch (CompileError e) { throw new CannotCompileException(e); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException("broken method"); } } static class ProceedForNew implements ProceedHandler { CtClass newType; int newIndex, methodIndex; ProceedForNew(CtClass nt, int ni, int mi) { newType = nt; newIndex = ni; methodIndex = mi; } public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) throws CompileError { bytecode.addOpcode(NEW); bytecode.addIndex(newIndex); bytecode.addOpcode(DUP); gen.atMethodCallCore(newType, MethodInfo.nameInit, args, false, true, -1, null); gen.setType(newType); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.atMethodCallCore(newType, MethodInfo.nameInit, args); c.setType(newType); } } }