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