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.*;
19import javassist.bytecode.*;
20import javassist.compiler.*;
21
22/**
23 * Method invocation (caller-side expression).
24 */
25public class MethodCall extends Expr {
26    /**
27     * Undocumented constructor.  Do not use; internal-use only.
28     */
29    protected MethodCall(int pos, CodeIterator i, CtClass declaring,
30                         MethodInfo m) {
31        super(pos, i, declaring, m);
32    }
33
34    private int getNameAndType(ConstPool cp) {
35        int pos = currentPos;
36        int c = iterator.byteAt(pos);
37        int index = iterator.u16bitAt(pos + 1);
38
39        if (c == INVOKEINTERFACE)
40            return cp.getInterfaceMethodrefNameAndType(index);
41        else
42            return cp.getMethodrefNameAndType(index);
43    }
44
45    /**
46     * Returns the method or constructor containing the method-call
47     * expression represented by this object.
48     */
49    public CtBehavior where() { return super.where(); }
50
51    /**
52     * Returns the line number of the source line containing the
53     * method call.
54     *
55     * @return -1       if this information is not available.
56     */
57    public int getLineNumber() {
58        return super.getLineNumber();
59    }
60
61    /**
62     * Returns the source file containing the method call.
63     *
64     * @return null     if this information is not available.
65     */
66    public String getFileName() {
67        return super.getFileName();
68    }
69
70    /**
71     * Returns the class of the target object,
72     * which the method is called on.
73     */
74    protected CtClass getCtClass() throws NotFoundException {
75        return thisClass.getClassPool().get(getClassName());
76    }
77
78    /**
79     * Returns the class name of the target object,
80     * which the method is called on.
81     */
82    public String getClassName() {
83        String cname;
84
85        ConstPool cp = getConstPool();
86        int pos = currentPos;
87        int c = iterator.byteAt(pos);
88        int index = iterator.u16bitAt(pos + 1);
89
90        if (c == INVOKEINTERFACE)
91            cname = cp.getInterfaceMethodrefClassName(index);
92        else
93            cname = cp.getMethodrefClassName(index);
94
95         if (cname.charAt(0) == '[')
96             cname = Descriptor.toClassName(cname);
97
98         return cname;
99    }
100
101    /**
102     * Returns the name of the called method.
103     */
104    public String getMethodName() {
105        ConstPool cp = getConstPool();
106        int nt = getNameAndType(cp);
107        return cp.getUtf8Info(cp.getNameAndTypeName(nt));
108    }
109
110    /**
111     * Returns the called method.
112     */
113    public CtMethod getMethod() throws NotFoundException {
114        return getCtClass().getMethod(getMethodName(), getSignature());
115    }
116
117    /**
118     * Returns the method signature (the parameter types
119     * and the return type).
120     * The method signature is represented by a character string
121     * called method descriptor, which is defined in the JVM specification.
122     *
123     * @see javassist.CtBehavior#getSignature()
124     * @see javassist.bytecode.Descriptor
125     * @since 3.1
126     */
127    public String getSignature() {
128        ConstPool cp = getConstPool();
129        int nt = getNameAndType(cp);
130        return cp.getUtf8Info(cp.getNameAndTypeDescriptor(nt));
131    }
132
133    /**
134     * Returns the list of exceptions that the expression may throw.
135     * This list includes both the exceptions that the try-catch statements
136     * including the expression can catch and the exceptions that
137     * the throws declaration allows the method to throw.
138     */
139    public CtClass[] mayThrow() {
140        return super.mayThrow();
141    }
142
143    /**
144     * Returns true if the called method is of a superclass of the current
145     * class.
146     */
147    public boolean isSuper() {
148        return iterator.byteAt(currentPos) == INVOKESPECIAL
149            && !where().getDeclaringClass().getName().equals(getClassName());
150    }
151
152    /*
153     * Returns the parameter types of the called method.
154
155    public CtClass[] getParameterTypes() throws NotFoundException {
156        return Descriptor.getParameterTypes(getMethodDesc(),
157                                            thisClass.getClassPool());
158    }
159    */
160
161    /*
162     * Returns the return type of the called method.
163
164    public CtClass getReturnType() throws NotFoundException {
165        return Descriptor.getReturnType(getMethodDesc(),
166                                        thisClass.getClassPool());
167    }
168    */
169
170    /**
171     * Replaces the method call with the bytecode derived from
172     * the given source text.
173     *
174     * <p>$0 is available even if the called method is static.
175     *
176     * @param statement         a Java statement except try-catch.
177     */
178    public void replace(String statement) throws CannotCompileException {
179        thisClass.getClassFile();   // to call checkModify().
180        ConstPool constPool = getConstPool();
181        int pos = currentPos;
182        int index = iterator.u16bitAt(pos + 1);
183
184        String classname, methodname, signature;
185        int opcodeSize;
186        int c = iterator.byteAt(pos);
187        if (c == INVOKEINTERFACE) {
188            opcodeSize = 5;
189            classname = constPool.getInterfaceMethodrefClassName(index);
190            methodname = constPool.getInterfaceMethodrefName(index);
191            signature = constPool.getInterfaceMethodrefType(index);
192        }
193        else if (c == INVOKESTATIC
194                 || c == INVOKESPECIAL || c == INVOKEVIRTUAL) {
195            opcodeSize = 3;
196            classname = constPool.getMethodrefClassName(index);
197            methodname = constPool.getMethodrefName(index);
198            signature = constPool.getMethodrefType(index);
199        }
200        else
201            throw new CannotCompileException("not method invocation");
202
203        Javac jc = new Javac(thisClass);
204        ClassPool cp = thisClass.getClassPool();
205        CodeAttribute ca = iterator.get();
206        try {
207            CtClass[] params = Descriptor.getParameterTypes(signature, cp);
208            CtClass retType = Descriptor.getReturnType(signature, cp);
209            int paramVar = ca.getMaxLocals();
210            jc.recordParams(classname, params,
211                            true, paramVar, withinStatic());
212            int retVar = jc.recordReturnType(retType, true);
213            if (c == INVOKESTATIC)
214                jc.recordStaticProceed(classname, methodname);
215            else if (c == INVOKESPECIAL)
216                jc.recordSpecialProceed(Javac.param0Name, classname,
217                                        methodname, signature);
218            else
219                jc.recordProceed(Javac.param0Name, methodname);
220
221            /* Is $_ included in the source code?
222             */
223            checkResultValue(retType, statement);
224
225            Bytecode bytecode = jc.getBytecode();
226            storeStack(params, c == INVOKESTATIC, paramVar, bytecode);
227            jc.recordLocalVariables(ca, pos);
228
229            if (retType != CtClass.voidType) {
230                bytecode.addConstZero(retType);
231                bytecode.addStore(retVar, retType);     // initialize $_
232            }
233
234            jc.compileStmnt(statement);
235            if (retType != CtClass.voidType)
236                bytecode.addLoad(retVar, retType);
237
238            replace0(pos, bytecode, opcodeSize);
239        }
240        catch (CompileError e) { throw new CannotCompileException(e); }
241        catch (NotFoundException e) { throw new CannotCompileException(e); }
242        catch (BadBytecode e) {
243            throw new CannotCompileException("broken method");
244        }
245    }
246}
247