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