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