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