• 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 java.util.LinkedList;
20 import java.util.List;
21 
22 import javassist.CannotCompileException;
23 import javassist.ClassPool;
24 import javassist.CtBehavior;
25 import javassist.CtClass;
26 import javassist.CtConstructor;
27 import javassist.CtPrimitiveType;
28 import javassist.NotFoundException;
29 import javassist.bytecode.AccessFlag;
30 import javassist.bytecode.BadBytecode;
31 import javassist.bytecode.Bytecode;
32 import javassist.bytecode.ClassFile;
33 import javassist.bytecode.CodeAttribute;
34 import javassist.bytecode.CodeIterator;
35 import javassist.bytecode.ConstPool;
36 import javassist.bytecode.ExceptionTable;
37 import javassist.bytecode.ExceptionsAttribute;
38 import javassist.bytecode.MethodInfo;
39 import javassist.bytecode.Opcode;
40 import javassist.compiler.Javac;
41 
42 /**
43  * Expression.
44  */
45 public abstract class Expr implements Opcode {
46     int currentPos;
47     CodeIterator iterator;
48     CtClass thisClass;
49     MethodInfo thisMethod;
50     boolean edited;
51     int maxLocals, maxStack;
52 
53     static final String javaLangObject = "java.lang.Object";
54 
55     /**
56      * Undocumented constructor. Do not use; internal-use only.
57      */
Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m)58     protected Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) {
59         currentPos = pos;
60         iterator = i;
61         thisClass = declaring;
62         thisMethod = m;
63     }
64 
65     /**
66      * Returns the class that declares the method enclosing
67      * this expression.
68      *
69      * @since 3.7
70      */
getEnclosingClass()71     public CtClass getEnclosingClass() { return thisClass; }
72 
getConstPool()73     protected final ConstPool getConstPool() {
74         return thisMethod.getConstPool();
75     }
76 
edited()77     protected final boolean edited() {
78         return edited;
79     }
80 
locals()81     protected final int locals() {
82         return maxLocals;
83     }
84 
stack()85     protected final int stack() {
86         return maxStack;
87     }
88 
89     /**
90      * Returns true if this method is static.
91      */
withinStatic()92     protected final boolean withinStatic() {
93         return (thisMethod.getAccessFlags() & AccessFlag.STATIC) != 0;
94     }
95 
96     /**
97      * Returns the constructor or method containing the expression.
98      */
where()99     public CtBehavior where() {
100         MethodInfo mi = thisMethod;
101         CtBehavior[] cb = thisClass.getDeclaredBehaviors();
102         for (int i = cb.length - 1; i >= 0; --i)
103             if (cb[i].getMethodInfo2() == mi)
104                 return cb[i];
105 
106         CtConstructor init = thisClass.getClassInitializer();
107         if (init != null && init.getMethodInfo2() == mi)
108             return init;
109 
110         /* getDeclaredBehaviors() returns a list of methods/constructors.
111          * Although the list is cached in a CtClass object, it might be
112          * recreated for some reason.  Thus, the member name and the signature
113          * must be also checked.
114          */
115         for (int i = cb.length - 1; i >= 0; --i) {
116             if (thisMethod.getName().equals(cb[i].getMethodInfo2().getName())
117                 && thisMethod.getDescriptor()
118                              .equals(cb[i].getMethodInfo2().getDescriptor())) {
119                 return cb[i];
120             }
121         }
122 
123         throw new RuntimeException("fatal: not found");
124     }
125 
126     /**
127      * Returns the list of exceptions that the expression may throw. This list
128      * includes both the exceptions that the try-catch statements including the
129      * expression can catch and the exceptions that the throws declaration
130      * allows the method to throw.
131      */
mayThrow()132     public CtClass[] mayThrow() {
133         ClassPool pool = thisClass.getClassPool();
134         ConstPool cp = thisMethod.getConstPool();
135         List<CtClass> list = new LinkedList<CtClass>();
136         try {
137             CodeAttribute ca = thisMethod.getCodeAttribute();
138             ExceptionTable et = ca.getExceptionTable();
139             int pos = currentPos;
140             int n = et.size();
141             for (int i = 0; i < n; ++i)
142                 if (et.startPc(i) <= pos && pos < et.endPc(i)) {
143                     int t = et.catchType(i);
144                     if (t > 0)
145                         try {
146                             addClass(list, pool.get(cp.getClassInfo(t)));
147                         }
148                         catch (NotFoundException e) {
149                         }
150                 }
151         }
152         catch (NullPointerException e) {
153         }
154 
155         ExceptionsAttribute ea = thisMethod.getExceptionsAttribute();
156         if (ea != null) {
157             String[] exceptions = ea.getExceptions();
158             if (exceptions != null) {
159                 int n = exceptions.length;
160                 for (int i = 0; i < n; ++i)
161                     try {
162                         addClass(list, pool.get(exceptions[i]));
163                     }
164                     catch (NotFoundException e) {
165                     }
166             }
167         }
168 
169         return list.toArray(new CtClass[list.size()]);
170     }
171 
addClass(List<CtClass> list, CtClass c)172     private static void addClass(List<CtClass> list, CtClass c) {
173         if (list.contains(c))
174             return;
175 
176         list.add(c);
177     }
178 
179     /**
180      * Returns the index of the bytecode corresponding to the expression. It is
181      * the index into the byte array containing the Java bytecode that
182      * implements the method.
183      */
indexOfBytecode()184     public int indexOfBytecode() {
185         return currentPos;
186     }
187 
188     /**
189      * Returns the line number of the source line containing the expression.
190      *
191      * @return -1 if this information is not available.
192      */
getLineNumber()193     public int getLineNumber() {
194         return thisMethod.getLineNumber(currentPos);
195     }
196 
197     /**
198      * Returns the source file containing the expression.
199      *
200      * @return null if this information is not available.
201      */
getFileName()202     public String getFileName() {
203         ClassFile cf = thisClass.getClassFile2();
204         if (cf == null)
205             return null;
206         return cf.getSourceFile();
207     }
208 
checkResultValue(CtClass retType, String prog)209     static final boolean checkResultValue(CtClass retType, String prog)
210             throws CannotCompileException {
211         /*
212          * Is $_ included in the source code?
213          */
214         boolean hasIt = (prog.indexOf(Javac.resultVarName) >= 0);
215         if (!hasIt && retType != CtClass.voidType)
216             throw new CannotCompileException(
217                     "the resulting value is not stored in "
218                             + Javac.resultVarName);
219 
220         return hasIt;
221     }
222 
223     /*
224      * If isStaticCall is true, null is assigned to $0. So $0 must be declared
225      * by calling Javac.recordParams().
226      *
227      * After executing this method, the current stack depth might be less than
228      * 0.
229      */
storeStack(CtClass[] params, boolean isStaticCall, int regno, Bytecode bytecode)230     static final void storeStack(CtClass[] params, boolean isStaticCall,
231             int regno, Bytecode bytecode) {
232         storeStack0(0, params.length, params, regno + 1, bytecode);
233         if (isStaticCall)
234             bytecode.addOpcode(ACONST_NULL);
235 
236         bytecode.addAstore(regno);
237     }
238 
storeStack0(int i, int n, CtClass[] params, int regno, Bytecode bytecode)239     private static void storeStack0(int i, int n, CtClass[] params, int regno,
240             Bytecode bytecode) {
241         if (i >= n)
242             return;
243         CtClass c = params[i];
244         int size;
245         if (c instanceof CtPrimitiveType)
246             size = ((CtPrimitiveType)c).getDataSize();
247         else
248             size = 1;
249 
250         storeStack0(i + 1, n, params, regno + size, bytecode);
251         bytecode.addStore(regno, c);
252     }
253 
254     // The implementation of replace() should call thisClass.checkModify()
255     // so that isModify() will return true.  Otherwise, thisClass.classfile
256     // might be released during compilation and the compiler might generate
257     // bytecode with a wrong copy of ConstPool.
258 
259     /**
260      * Replaces this expression with the bytecode derived from
261      * the given source text.
262      *
263      * @param statement         a Java statement except try-catch.
264      */
replace(String statement)265     public abstract void replace(String statement) throws CannotCompileException;
266 
267     /**
268      * Replaces this expression with the bytecode derived from
269      * the given source text and <code>ExprEditor</code>.
270      *
271      * @param statement         a Java statement except try-catch.
272      * @param recursive         if not null, the substituted bytecode
273      *                          is recursively processed by the given
274      *                          <code>ExprEditor</code>.
275      * @since 3.1
276      */
replace(String statement, ExprEditor recursive)277     public void replace(String statement, ExprEditor recursive)
278         throws CannotCompileException
279     {
280         replace(statement);
281         if (recursive != null)
282             runEditor(recursive, iterator);
283     }
284 
replace0(int pos, Bytecode bytecode, int size)285     protected void replace0(int pos, Bytecode bytecode, int size)
286             throws BadBytecode {
287         byte[] code = bytecode.get();
288         edited = true;
289         int gap = code.length - size;
290         for (int i = 0; i < size; ++i)
291             iterator.writeByte(NOP, pos + i);
292 
293         if (gap > 0)
294             pos = iterator.insertGapAt(pos, gap, false).position;
295 
296         iterator.write(code, pos);
297         iterator.insert(bytecode.getExceptionTable(), pos);
298         maxLocals = bytecode.getMaxLocals();
299         maxStack = bytecode.getMaxStack();
300     }
301 
runEditor(ExprEditor ed, CodeIterator oldIterator)302     protected void runEditor(ExprEditor ed, CodeIterator oldIterator)
303         throws CannotCompileException
304     {
305         CodeAttribute codeAttr = oldIterator.get();
306         int orgLocals = codeAttr.getMaxLocals();
307         int orgStack = codeAttr.getMaxStack();
308         int newLocals = locals();
309         codeAttr.setMaxStack(stack());
310         codeAttr.setMaxLocals(newLocals);
311         ExprEditor.LoopContext context
312             = new ExprEditor.LoopContext(newLocals);
313         int size = oldIterator.getCodeLength();
314         int endPos = oldIterator.lookAhead();
315         oldIterator.move(currentPos);
316         if (ed.doit(thisClass, thisMethod, context, oldIterator, endPos))
317             edited = true;
318 
319         oldIterator.move(endPos + oldIterator.getCodeLength() - size);
320         codeAttr.setMaxLocals(orgLocals);
321         codeAttr.setMaxStack(orgStack);
322         maxLocals = context.maxLocals;
323         maxStack += context.maxStack;
324     }
325 }
326