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