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 * Array creation.
25 *
26 * <p>This class does not provide methods for obtaining the initial
27 * values of array elements.
28 */
29public class NewArray extends Expr {
30    int opcode;
31
32    protected NewArray(int pos, CodeIterator i, CtClass declaring,
33                       MethodInfo m, int op) {
34        super(pos, i, declaring, m);
35        opcode = op;
36    }
37
38    /**
39     * Returns the method or constructor containing the array creation
40     * represented by this object.
41     */
42    public CtBehavior where() { return super.where(); }
43
44    /**
45     * Returns the line number of the source line containing the
46     * array creation.
47     *
48     * @return -1       if this information is not available.
49     */
50    public int getLineNumber() {
51        return super.getLineNumber();
52    }
53
54    /**
55     * Returns the source file containing the array creation.
56     *
57     * @return null     if this information is not available.
58     */
59    public String getFileName() {
60        return super.getFileName();
61    }
62
63    /**
64     * Returns the list of exceptions that the expression may throw.
65     * This list includes both the exceptions that the try-catch statements
66     * including the expression can catch and the exceptions that
67     * the throws declaration allows the method to throw.
68     */
69    public CtClass[] mayThrow() {
70        return super.mayThrow();
71    }
72
73    /**
74     * Returns the type of array components.  If the created array is
75     * a two-dimensional array of <tt>int</tt>,
76     * the type returned by this method is
77     * not <tt>int[]</tt> but <tt>int</tt>.
78     */
79    public CtClass getComponentType() throws NotFoundException {
80        if (opcode == Opcode.NEWARRAY) {
81            int atype = iterator.byteAt(currentPos + 1);
82            return getPrimitiveType(atype);
83        }
84        else if (opcode == Opcode.ANEWARRAY
85                 || opcode == Opcode.MULTIANEWARRAY) {
86            int index = iterator.u16bitAt(currentPos + 1);
87            String desc = getConstPool().getClassInfo(index);
88            int dim = Descriptor.arrayDimension(desc);
89            desc = Descriptor.toArrayComponent(desc, dim);
90            return Descriptor.toCtClass(desc, thisClass.getClassPool());
91        }
92        else
93            throw new RuntimeException("bad opcode: " + opcode);
94    }
95
96    CtClass getPrimitiveType(int atype) {
97        switch (atype) {
98        case Opcode.T_BOOLEAN :
99            return CtClass.booleanType;
100        case Opcode.T_CHAR :
101            return CtClass.charType;
102        case Opcode.T_FLOAT :
103            return CtClass.floatType;
104        case Opcode.T_DOUBLE :
105            return CtClass.doubleType;
106        case Opcode.T_BYTE :
107            return CtClass.byteType;
108        case Opcode.T_SHORT :
109            return CtClass.shortType;
110        case Opcode.T_INT :
111            return CtClass.intType;
112        case Opcode.T_LONG :
113            return CtClass.longType;
114        default :
115            throw new RuntimeException("bad atype: " + atype);
116        }
117    }
118
119    /**
120     * Returns the dimension of the created array.
121     */
122    public int getDimension() {
123        if (opcode == Opcode.NEWARRAY)
124            return 1;
125        else if (opcode == Opcode.ANEWARRAY
126                 || opcode == Opcode.MULTIANEWARRAY) {
127            int index = iterator.u16bitAt(currentPos + 1);
128            String desc = getConstPool().getClassInfo(index);
129            return Descriptor.arrayDimension(desc)
130                    + (opcode == Opcode.ANEWARRAY ? 1 : 0);
131        }
132        else
133            throw new RuntimeException("bad opcode: " + opcode);
134    }
135
136    /**
137     * Returns the number of dimensions of arrays to be created.
138     * If the opcode is multianewarray, this method returns the second
139     * operand.  Otherwise, it returns 1.
140     */
141    public int getCreatedDimensions() {
142        if (opcode == Opcode.MULTIANEWARRAY)
143            return iterator.byteAt(currentPos + 3);
144        else
145            return 1;
146    }
147
148    /**
149     * Replaces the array creation with the bytecode derived from
150     * the given source text.
151     *
152     * <p>$0 is available even if the called method is static.
153     * If the field access is writing, $_ is available but the value
154     * of $_ is ignored.
155     *
156     * @param statement         a Java statement except try-catch.
157     */
158    public void replace(String statement) throws CannotCompileException {
159        try {
160            replace2(statement);
161        }
162        catch (CompileError e) { throw new CannotCompileException(e); }
163        catch (NotFoundException e) { throw new CannotCompileException(e); }
164        catch (BadBytecode e) {
165            throw new CannotCompileException("broken method");
166        }
167    }
168
169    private void replace2(String statement)
170        throws CompileError, NotFoundException, BadBytecode,
171               CannotCompileException
172    {
173        thisClass.getClassFile();   // to call checkModify().
174        ConstPool constPool = getConstPool();
175        int pos = currentPos;
176        CtClass retType;
177        int codeLength;
178        int index = 0;
179        int dim = 1;
180        String desc;
181        if (opcode == Opcode.NEWARRAY) {
182            index = iterator.byteAt(currentPos + 1);    // atype
183            CtPrimitiveType cpt = (CtPrimitiveType)getPrimitiveType(index);
184            desc = "[" + cpt.getDescriptor();
185            codeLength = 2;
186        }
187        else if (opcode == Opcode.ANEWARRAY) {
188            index = iterator.u16bitAt(pos + 1);
189            desc = constPool.getClassInfo(index);
190            if (desc.startsWith("["))
191                desc = "[" + desc;
192            else
193                desc = "[L" + desc + ";";
194
195            codeLength = 3;
196        }
197        else if (opcode == Opcode.MULTIANEWARRAY) {
198            index = iterator.u16bitAt(currentPos + 1);
199            desc = constPool.getClassInfo(index);
200            dim = iterator.byteAt(currentPos + 3);
201            codeLength = 4;
202        }
203        else
204            throw new RuntimeException("bad opcode: " + opcode);
205
206        retType = Descriptor.toCtClass(desc, thisClass.getClassPool());
207
208        Javac jc = new Javac(thisClass);
209        CodeAttribute ca = iterator.get();
210
211        CtClass[] params = new CtClass[dim];
212        for (int i = 0; i < dim; ++i)
213            params[i] = CtClass.intType;
214
215        int paramVar = ca.getMaxLocals();
216        jc.recordParams(javaLangObject, params,
217                        true, paramVar, withinStatic());
218
219        /* Is $_ included in the source code?
220         */
221        checkResultValue(retType, statement);
222        int retVar = jc.recordReturnType(retType, true);
223        jc.recordProceed(new ProceedForArray(retType, opcode, index, dim));
224
225        Bytecode bytecode = jc.getBytecode();
226        storeStack(params, true, paramVar, bytecode);
227        jc.recordLocalVariables(ca, pos);
228
229        bytecode.addOpcode(ACONST_NULL);        // initialize $_
230        bytecode.addAstore(retVar);
231
232        jc.compileStmnt(statement);
233        bytecode.addAload(retVar);
234
235        replace0(pos, bytecode, codeLength);
236    }
237
238    /* <array type> $proceed(<dim> ..)
239     */
240    static class ProceedForArray implements ProceedHandler {
241        CtClass arrayType;
242        int opcode;
243        int index, dimension;
244
245        ProceedForArray(CtClass type, int op, int i, int dim) {
246            arrayType = type;
247            opcode = op;
248            index = i;
249            dimension = dim;
250        }
251
252        public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)
253            throws CompileError
254        {
255            int num = gen.getMethodArgsLength(args);
256            if (num != dimension)
257                throw new CompileError(Javac.proceedName
258                        + "() with a wrong number of parameters");
259
260            gen.atMethodArgs(args, new int[num],
261                             new int[num], new String[num]);
262            bytecode.addOpcode(opcode);
263            if (opcode == Opcode.ANEWARRAY)
264                bytecode.addIndex(index);
265            else if (opcode == Opcode.NEWARRAY)
266                bytecode.add(index);
267            else /* if (opcode == Opcode.MULTIANEWARRAY) */ {
268                bytecode.addIndex(index);
269                bytecode.add(dimension);
270                bytecode.growStack(1 - dimension);
271            }
272
273            gen.setType(arrayType);
274        }
275
276        public void setReturnType(JvstTypeChecker c, ASTList args)
277            throws CompileError
278        {
279            c.setType(arrayType);
280        }
281    }
282}
283