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