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