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; 17 18 import javassist.bytecode.*; 19 20 /** 21 * An instance of <code>CtMethod</code> represents a method. 22 * 23 * <p>See the super class <code>CtBehavior</code> since 24 * a number of useful methods are in <code>CtBehavior</code>. 25 * A number of useful factory methods are in <code>CtNewMethod</code>. 26 * 27 * @see CtClass#getDeclaredMethods() 28 * @see CtNewMethod 29 */ 30 public final class CtMethod extends CtBehavior { 31 protected String cachedStringRep; 32 33 /** 34 * @see #make(MethodInfo minfo, CtClass declaring) 35 */ CtMethod(MethodInfo minfo, CtClass declaring)36 CtMethod(MethodInfo minfo, CtClass declaring) { 37 super(declaring, minfo); 38 cachedStringRep = null; 39 } 40 41 /** 42 * Creates a public abstract method. The created method must be 43 * added to a class with <code>CtClass.addMethod()</code>. 44 * 45 * @param declaring the class to which the created method is added. 46 * @param returnType the type of the returned value 47 * @param mname the method name 48 * @param parameters a list of the parameter types 49 * 50 * @see CtClass#addMethod(CtMethod) 51 */ CtMethod(CtClass returnType, String mname, CtClass[] parameters, CtClass declaring)52 public CtMethod(CtClass returnType, String mname, 53 CtClass[] parameters, CtClass declaring) { 54 this(null, declaring); 55 ConstPool cp = declaring.getClassFile2().getConstPool(); 56 String desc = Descriptor.ofMethod(returnType, parameters); 57 methodInfo = new MethodInfo(cp, mname, desc); 58 setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT); 59 } 60 61 /** 62 * Creates a copy of a <code>CtMethod</code> object. 63 * The created method must be 64 * added to a class with <code>CtClass.addMethod()</code>. 65 * 66 * <p>All occurrences of class names in the created method 67 * are replaced with names specified by 68 * <code>map</code> if <code>map</code> is not <code>null</code>. 69 * 70 * <p>For example, suppose that a method <code>at()</code> is as 71 * follows: 72 * 73 * <ul><pre>public X at(int i) { 74 * return (X)super.elementAt(i); 75 * }</pre></ul> 76 * 77 * <p>(<code>X</code> is a class name.) If <code>map</code> substitutes 78 * <code>String</code> for <code>X</code>, then the created method is: 79 * 80 * <ul><pre>public String at(int i) { 81 * return (String)super.elementAt(i); 82 * }</pre></ul> 83 * 84 * <p>By default, all the occurrences of the names of the class 85 * declaring <code>at()</code> and the superclass are replaced 86 * with the name of the class and the superclass that the 87 * created method is added to. 88 * This is done whichever <code>map</code> is null or not. 89 * To prevent this replacement, call <code>ClassMap.fix()</code> 90 * or <code>put()</code> to explicitly specify replacement. 91 * 92 * <p><b>Note:</b> if the <code>.class</code> notation (for example, 93 * <code>String.class</code>) is included in an expression, the 94 * Javac compiler may produce a helper method. 95 * Since this constructor never 96 * copies this helper method, the programmers have the responsiblity of 97 * copying it. Otherwise, use <code>Class.forName()</code> in the 98 * expression. 99 * 100 * @param src the source method. 101 * @param declaring the class to which the created method is added. 102 * @param map the hashtable associating original class names 103 * with substituted names. 104 * It can be <code>null</code>. 105 * 106 * @see CtClass#addMethod(CtMethod) 107 * @see ClassMap#fix(String) 108 */ CtMethod(CtMethod src, CtClass declaring, ClassMap map)109 public CtMethod(CtMethod src, CtClass declaring, ClassMap map) 110 throws CannotCompileException 111 { 112 this(null, declaring); 113 copy(src, false, map); 114 } 115 116 /** 117 * Compiles the given source code and creates a method. 118 * This method simply delegates to <code>make()</code> in 119 * <code>CtNewMethod</code>. See it for more details. 120 * <code>CtNewMethod</code> has a number of useful factory methods. 121 * 122 * @param src the source text. 123 * @param declaring the class to which the created method is added. 124 * @see CtNewMethod#make(String, CtClass) 125 */ make(String src, CtClass declaring)126 public static CtMethod make(String src, CtClass declaring) 127 throws CannotCompileException 128 { 129 return CtNewMethod.make(src, declaring); 130 } 131 132 /** 133 * Creates a method from a <code>MethodInfo</code> object. 134 * 135 * @param declaring the class declaring the method. 136 * @throws CannotCompileException if the the <code>MethodInfo</code> 137 * object and the declaring class have different 138 * <code>ConstPool</code> objects 139 * @since 3.6 140 */ make(MethodInfo minfo, CtClass declaring)141 public static CtMethod make(MethodInfo minfo, CtClass declaring) 142 throws CannotCompileException 143 { 144 if (declaring.getClassFile2().getConstPool() != minfo.getConstPool()) 145 throw new CannotCompileException("bad declaring class"); 146 147 return new CtMethod(minfo, declaring); 148 } 149 150 /** 151 * Returns a hash code value for the method. 152 * If two methods have the same name and signature, then 153 * the hash codes for the two methods are equal. 154 */ hashCode()155 public int hashCode() { 156 return getStringRep().hashCode(); 157 } 158 159 /** 160 * This method is invoked when setName() or replaceClassName() 161 * in CtClass is called. 162 */ nameReplaced()163 void nameReplaced() { 164 cachedStringRep = null; 165 } 166 167 /* This method is also called by CtClassType.getMethods0(). 168 */ getStringRep()169 final String getStringRep() { 170 if (cachedStringRep == null) 171 cachedStringRep = methodInfo.getName() 172 + Descriptor.getParamDescriptor(methodInfo.getDescriptor()); 173 174 return cachedStringRep; 175 } 176 177 /** 178 * Indicates whether <code>obj</code> has the same name and the 179 * same signature as this method. 180 */ equals(Object obj)181 public boolean equals(Object obj) { 182 return obj != null && obj instanceof CtMethod 183 && ((CtMethod)obj).getStringRep().equals(getStringRep()); 184 } 185 186 /** 187 * Returns the method name followed by parameter types 188 * such as <code>javassist.CtMethod.setBody(String)</code>. 189 * 190 * @since 3.5 191 */ getLongName()192 public String getLongName() { 193 return getDeclaringClass().getName() + "." 194 + getName() + Descriptor.toString(getSignature()); 195 } 196 197 /** 198 * Obtains the name of this method. 199 */ getName()200 public String getName() { 201 return methodInfo.getName(); 202 } 203 204 /** 205 * Changes the name of this method. 206 */ setName(String newname)207 public void setName(String newname) { 208 declaringClass.checkModify(); 209 methodInfo.setName(newname); 210 } 211 212 /** 213 * Obtains the type of the returned value. 214 */ getReturnType()215 public CtClass getReturnType() throws NotFoundException { 216 return getReturnType0(); 217 } 218 219 /** 220 * Returns true if the method body is empty, that is, <code>{}</code>. 221 * It also returns true if the method is an abstract method. 222 */ isEmpty()223 public boolean isEmpty() { 224 CodeAttribute ca = getMethodInfo2().getCodeAttribute(); 225 if (ca == null) // abstract or native 226 return (getModifiers() & Modifier.ABSTRACT) != 0; 227 228 CodeIterator it = ca.iterator(); 229 try { 230 return it.hasNext() && it.byteAt(it.next()) == Opcode.RETURN 231 && !it.hasNext(); 232 } 233 catch (BadBytecode e) {} 234 return false; 235 } 236 237 /** 238 * Copies a method body from another method. 239 * If this method is abstract, the abstract modifier is removed 240 * after the method body is copied. 241 * 242 * <p>All occurrences of the class names in the copied method body 243 * are replaced with the names specified by 244 * <code>map</code> if <code>map</code> is not <code>null</code>. 245 * 246 * @param src the method that the body is copied from. 247 * @param map the hashtable associating original class names 248 * with substituted names. 249 * It can be <code>null</code>. 250 */ setBody(CtMethod src, ClassMap map)251 public void setBody(CtMethod src, ClassMap map) 252 throws CannotCompileException 253 { 254 setBody0(src.declaringClass, src.methodInfo, 255 declaringClass, methodInfo, map); 256 } 257 258 /** 259 * Replace a method body with a new method body wrapping the 260 * given method. 261 * 262 * @param mbody the wrapped method 263 * @param constParam the constant parameter given to 264 * the wrapped method 265 * (maybe <code>null</code>). 266 * 267 * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) 268 */ setWrappedBody(CtMethod mbody, ConstParameter constParam)269 public void setWrappedBody(CtMethod mbody, ConstParameter constParam) 270 throws CannotCompileException 271 { 272 declaringClass.checkModify(); 273 274 CtClass clazz = getDeclaringClass(); 275 CtClass[] params; 276 CtClass retType; 277 try { 278 params = getParameterTypes(); 279 retType = getReturnType(); 280 } 281 catch (NotFoundException e) { 282 throw new CannotCompileException(e); 283 } 284 285 Bytecode code = CtNewWrappedMethod.makeBody(clazz, 286 clazz.getClassFile2(), 287 mbody, 288 params, retType, 289 constParam); 290 CodeAttribute cattr = code.toCodeAttribute(); 291 methodInfo.setCodeAttribute(cattr); 292 methodInfo.setAccessFlags(methodInfo.getAccessFlags() 293 & ~AccessFlag.ABSTRACT); 294 // rebuilding a stack map table is not needed. 295 } 296 297 // inner classes 298 299 /** 300 * Instances of this class represent a constant parameter. 301 * They are used to specify the parameter given to the methods 302 * created by <code>CtNewMethod.wrapped()</code>. 303 * 304 * @see CtMethod#setWrappedBody(CtMethod,CtMethod.ConstParameter) 305 * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass) 306 * @see CtNewConstructor#make(CtClass[],CtClass[],int,CtMethod,CtMethod.ConstParameter,CtClass) 307 */ 308 public static class ConstParameter { 309 /** 310 * Makes an integer constant. 311 * 312 * @param i the constant value. 313 */ integer(int i)314 public static ConstParameter integer(int i) { 315 return new IntConstParameter(i); 316 } 317 318 /** 319 * Makes a long integer constant. 320 * 321 * @param i the constant value. 322 */ integer(long i)323 public static ConstParameter integer(long i) { 324 return new LongConstParameter(i); 325 } 326 327 /** 328 * Makes an <code>String</code> constant. 329 * 330 * @param s the constant value. 331 */ string(String s)332 public static ConstParameter string(String s) { 333 return new StringConstParameter(s); 334 } 335 ConstParameter()336 ConstParameter() {} 337 338 /** 339 * @return the size of the stack consumption. 340 */ compile(Bytecode code)341 int compile(Bytecode code) throws CannotCompileException { 342 return 0; 343 } 344 descriptor()345 String descriptor() { 346 return defaultDescriptor(); 347 } 348 349 /** 350 * @see CtNewWrappedMethod 351 */ defaultDescriptor()352 static String defaultDescriptor() { 353 return "([Ljava/lang/Object;)Ljava/lang/Object;"; 354 } 355 356 /** 357 * Returns the descriptor for constructors. 358 * 359 * @see CtNewWrappedConstructor 360 */ constDescriptor()361 String constDescriptor() { 362 return defaultConstDescriptor(); 363 } 364 365 /** 366 * Returns the default descriptor for constructors. 367 */ defaultConstDescriptor()368 static String defaultConstDescriptor() { 369 return "([Ljava/lang/Object;)V"; 370 } 371 } 372 373 static class IntConstParameter extends ConstParameter { 374 int param; 375 IntConstParameter(int i)376 IntConstParameter(int i) { 377 param = i; 378 } 379 compile(Bytecode code)380 int compile(Bytecode code) throws CannotCompileException { 381 code.addIconst(param); 382 return 1; 383 } 384 descriptor()385 String descriptor() { 386 return "([Ljava/lang/Object;I)Ljava/lang/Object;"; 387 } 388 constDescriptor()389 String constDescriptor() { 390 return "([Ljava/lang/Object;I)V"; 391 } 392 } 393 394 static class LongConstParameter extends ConstParameter { 395 long param; 396 LongConstParameter(long l)397 LongConstParameter(long l) { 398 param = l; 399 } 400 compile(Bytecode code)401 int compile(Bytecode code) throws CannotCompileException { 402 code.addLconst(param); 403 return 2; 404 } 405 descriptor()406 String descriptor() { 407 return "([Ljava/lang/Object;J)Ljava/lang/Object;"; 408 } 409 constDescriptor()410 String constDescriptor() { 411 return "([Ljava/lang/Object;J)V"; 412 } 413 } 414 415 static class StringConstParameter extends ConstParameter { 416 String param; 417 StringConstParameter(String s)418 StringConstParameter(String s) { 419 param = s; 420 } 421 compile(Bytecode code)422 int compile(Bytecode code) throws CannotCompileException { 423 code.addLdc(param); 424 return 1; 425 } 426 descriptor()427 String descriptor() { 428 return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"; 429 } 430 constDescriptor()431 String constDescriptor() { 432 return "([Ljava/lang/Object;Ljava/lang/String;)V"; 433 } 434 } 435 } 436