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
16package javassist.expr;
17
18import javassist.CannotCompileException;
19import javassist.ClassPool;
20import javassist.CtBehavior;
21import javassist.CtClass;
22import javassist.CtConstructor;
23import javassist.CtPrimitiveType;
24import javassist.NotFoundException;
25import javassist.bytecode.AccessFlag;
26import javassist.bytecode.BadBytecode;
27import javassist.bytecode.Bytecode;
28import javassist.bytecode.ClassFile;
29import javassist.bytecode.CodeAttribute;
30import javassist.bytecode.CodeIterator;
31import javassist.bytecode.ConstPool;
32import javassist.bytecode.ExceptionTable;
33import javassist.bytecode.ExceptionsAttribute;
34import javassist.bytecode.MethodInfo;
35import javassist.bytecode.Opcode;
36import javassist.compiler.Javac;
37
38import java.util.Iterator;
39import java.util.LinkedList;
40
41/**
42 * Expression.
43 */
44public 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     */
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     */
70    public CtClass getEnclosingClass() { return thisClass; }
71
72    protected final ConstPool getConstPool() {
73        return thisMethod.getConstPool();
74    }
75
76    protected final boolean edited() {
77        return edited;
78    }
79
80    protected final int locals() {
81        return maxLocals;
82    }
83
84    protected final int stack() {
85        return maxStack;
86    }
87
88    /**
89     * Returns true if this method is static.
90     */
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     */
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     */
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
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     */
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     */
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     */
203    public String getFileName() {
204        ClassFile cf = thisClass.getClassFile2();
205        if (cf == null)
206            return null;
207        else
208            return cf.getSourceFile();
209    }
210
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     */
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
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     */
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     */
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
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
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