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 
16 package javassist.expr;
17 
18 import javassist.*;
19 import javassist.bytecode.*;
20 import javassist.compiler.*;
21 import 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  */
29 public class NewArray extends Expr {
30     int opcode;
31 
NewArray(int pos, CodeIterator i, CtClass declaring, MethodInfo m, int op)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      */
where()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      */
getLineNumber()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      */
getFileName()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      */
mayThrow()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      */
getComponentType()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 
getPrimitiveType(int atype)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      */
getDimension()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      */
getCreatedDimensions()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      */
replace(String statement)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 
replace2(String statement)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 
ProceedForArray(CtClass type, int op, int i, int dim)245         ProceedForArray(CtClass type, int op, int i, int dim) {
246             arrayType = type;
247             opcode = op;
248             index = i;
249             dimension = dim;
250         }
251 
doit(JvstCodeGen gen, Bytecode bytecode, ASTList args)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 
setReturnType(JvstTypeChecker c, ASTList args)276         public void setReturnType(JvstTypeChecker c, ASTList args)
277             throws CompileError
278         {
279             c.setType(arrayType);
280         }
281     }
282 }
283