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.bytecode; 18 19 import java.io.DataInputStream; 20 import java.io.DataOutputStream; 21 import java.io.IOException; 22 import java.util.ArrayList; 23 import java.util.List; 24 import java.util.Map; 25 26 import javassist.ClassPool; 27 import javassist.bytecode.stackmap.MapMaker; 28 29 /** 30 * <code>method_info</code> structure. 31 * 32 * <p>The bytecode sequence of the method is represented 33 * by a <code>CodeAttribute</code> object. 34 * 35 * <p>The following code adds the default constructor to a class: 36 * of <code>int</code> type: 37 * <blockquote><pre> 38 * ClassFile cf = ... 39 * Bytecode code = new Bytecode(cf.getConstPool()); 40 * code.addAload(0); 41 * code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V"); 42 * code.addReturn(null); 43 * code.setMaxLocals(1); 44 * 45 * MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V"); 46 * minfo.setCodeAttribute(code.toCodeAttribute()); 47 * cf.addMethod(minfo); 48 * </pre></blockquote> 49 * 50 * @see #getCodeAttribute() 51 * @see CodeAttribute 52 * @see Bytecode 53 * @see javassist.CtMethod#getMethodInfo() 54 * @see javassist.CtConstructor#getMethodInfo() 55 */ 56 public class MethodInfo { 57 ConstPool constPool; 58 int accessFlags; 59 int name; 60 String cachedName; 61 int descriptor; 62 List<AttributeInfo> attribute; // may be null 63 64 /** 65 * If this value is true, Javassist maintains a <code>StackMap</code> attribute 66 * generated by the <code>preverify</code> tool of J2ME (CLDC). The initial 67 * value of this field is <code>false</code>. 68 */ 69 public static boolean doPreverify = false; 70 71 /** 72 * The name of constructors: <code><init></code>. 73 */ 74 public static final String nameInit = "<init>"; 75 76 /** 77 * The name of class initializer (static initializer): 78 * <code><clinit></code>. 79 */ 80 public static final String nameClinit = "<clinit>"; 81 MethodInfo(ConstPool cp)82 private MethodInfo(ConstPool cp) { 83 constPool = cp; 84 attribute = null; 85 } 86 87 /** 88 * Constructs a <code>method_info</code> structure. The initial value of 89 * <code>access_flags</code> is zero. 90 * 91 * @param cp 92 * a constant pool table 93 * @param methodname 94 * method name 95 * @param desc 96 * method descriptor 97 * @see Descriptor 98 */ MethodInfo(ConstPool cp, String methodname, String desc)99 public MethodInfo(ConstPool cp, String methodname, String desc) { 100 this(cp); 101 accessFlags = 0; 102 name = cp.addUtf8Info(methodname); 103 cachedName = methodname; 104 descriptor = constPool.addUtf8Info(desc); 105 } 106 MethodInfo(ConstPool cp, DataInputStream in)107 MethodInfo(ConstPool cp, DataInputStream in) throws IOException { 108 this(cp); 109 read(in); 110 } 111 112 /** 113 * Constructs a copy of <code>method_info</code> structure. Class names 114 * appearing in the source <code>method_info</code> are renamed according 115 * to <code>classnameMap</code>. 116 * 117 * <p> 118 * Note: only <code>Code</code> and <code>Exceptions</code> attributes 119 * are copied from the source. The other attributes are ignored. 120 * 121 * @param cp 122 * a constant pool table 123 * @param methodname 124 * a method name 125 * @param src 126 * a source <code>method_info</code> 127 * @param classnameMap 128 * specifies pairs of replaced and substituted name. 129 * @see Descriptor 130 */ MethodInfo(ConstPool cp, String methodname, MethodInfo src, Map<String,String> classnameMap)131 public MethodInfo(ConstPool cp, String methodname, MethodInfo src, 132 Map<String,String> classnameMap) throws BadBytecode 133 { 134 this(cp); 135 read(src, methodname, classnameMap); 136 } 137 138 /** 139 * Returns a string representation of the object. 140 */ 141 @Override toString()142 public String toString() { 143 return getName() + " " + getDescriptor(); 144 } 145 146 /** 147 * Copies all constant pool items to a given new constant pool 148 * and replaces the original items with the new ones. 149 * This is used for garbage collecting the items of removed fields 150 * and methods. 151 * 152 * @param cp the destination 153 */ compact(ConstPool cp)154 void compact(ConstPool cp) { 155 name = cp.addUtf8Info(getName()); 156 descriptor = cp.addUtf8Info(getDescriptor()); 157 attribute = AttributeInfo.copyAll(attribute, cp); 158 constPool = cp; 159 } 160 prune(ConstPool cp)161 void prune(ConstPool cp) { 162 List<AttributeInfo> newAttributes = new ArrayList<AttributeInfo>(); 163 164 AttributeInfo invisibleAnnotations 165 = getAttribute(AnnotationsAttribute.invisibleTag); 166 if (invisibleAnnotations != null) { 167 invisibleAnnotations = invisibleAnnotations.copy(cp, null); 168 newAttributes.add(invisibleAnnotations); 169 } 170 171 AttributeInfo visibleAnnotations 172 = getAttribute(AnnotationsAttribute.visibleTag); 173 if (visibleAnnotations != null) { 174 visibleAnnotations = visibleAnnotations.copy(cp, null); 175 newAttributes.add(visibleAnnotations); 176 } 177 178 AttributeInfo parameterInvisibleAnnotations 179 = getAttribute(ParameterAnnotationsAttribute.invisibleTag); 180 if (parameterInvisibleAnnotations != null) { 181 parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null); 182 newAttributes.add(parameterInvisibleAnnotations); 183 } 184 185 AttributeInfo parameterVisibleAnnotations 186 = getAttribute(ParameterAnnotationsAttribute.visibleTag); 187 if (parameterVisibleAnnotations != null) { 188 parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null); 189 newAttributes.add(parameterVisibleAnnotations); 190 } 191 192 AnnotationDefaultAttribute defaultAttribute 193 = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag); 194 if (defaultAttribute != null) 195 newAttributes.add(defaultAttribute); 196 197 ExceptionsAttribute ea = getExceptionsAttribute(); 198 if (ea != null) 199 newAttributes.add(ea); 200 201 AttributeInfo signature 202 = getAttribute(SignatureAttribute.tag); 203 if (signature != null) { 204 signature = signature.copy(cp, null); 205 newAttributes.add(signature); 206 } 207 208 attribute = newAttributes; 209 name = cp.addUtf8Info(getName()); 210 descriptor = cp.addUtf8Info(getDescriptor()); 211 constPool = cp; 212 } 213 214 /** 215 * Returns a method name. 216 */ getName()217 public String getName() { 218 if (cachedName == null) 219 cachedName = constPool.getUtf8Info(name); 220 221 return cachedName; 222 } 223 224 /** 225 * Sets a method name. 226 */ setName(String newName)227 public void setName(String newName) { 228 name = constPool.addUtf8Info(newName); 229 cachedName = newName; 230 } 231 232 /** 233 * Returns true if this is not a constructor or a class initializer (static 234 * initializer). 235 */ isMethod()236 public boolean isMethod() { 237 String n = getName(); 238 return !n.equals(nameInit) && !n.equals(nameClinit); 239 } 240 241 /** 242 * Returns a constant pool table used by this method. 243 */ getConstPool()244 public ConstPool getConstPool() { 245 return constPool; 246 } 247 248 /** 249 * Returns true if this is a constructor. 250 */ isConstructor()251 public boolean isConstructor() { 252 return getName().equals(nameInit); 253 } 254 255 /** 256 * Returns true if this is a class initializer (static initializer). 257 */ isStaticInitializer()258 public boolean isStaticInitializer() { 259 return getName().equals(nameClinit); 260 } 261 262 /** 263 * Returns access flags. 264 * 265 * @see AccessFlag 266 */ getAccessFlags()267 public int getAccessFlags() { 268 return accessFlags; 269 } 270 271 /** 272 * Sets access flags. 273 * 274 * @see AccessFlag 275 */ setAccessFlags(int acc)276 public void setAccessFlags(int acc) { 277 accessFlags = acc; 278 } 279 280 /** 281 * Returns a method descriptor. 282 * 283 * @see Descriptor 284 */ getDescriptor()285 public String getDescriptor() { 286 return constPool.getUtf8Info(descriptor); 287 } 288 289 /** 290 * Sets a method descriptor. 291 * 292 * @see Descriptor 293 */ setDescriptor(String desc)294 public void setDescriptor(String desc) { 295 if (!desc.equals(getDescriptor())) 296 descriptor = constPool.addUtf8Info(desc); 297 } 298 299 /** 300 * Returns all the attributes. The returned <code>List</code> object 301 * is shared with this object. If you add a new attribute to the list, 302 * the attribute is also added to the method represented by this 303 * object. If you remove an attribute from the list, it is also removed 304 * from the method. 305 * 306 * @return a list of <code>AttributeInfo</code> objects. 307 * @see AttributeInfo 308 */ getAttributes()309 public List<AttributeInfo> getAttributes() { 310 if (attribute == null) 311 attribute = new ArrayList<AttributeInfo>(); 312 313 return attribute; 314 } 315 316 /** 317 * Returns the attribute with the specified name. If it is not found, this 318 * method returns null. 319 * 320 * <p>An attribute name can be obtained by, for example, 321 * {@link AnnotationsAttribute#visibleTag} or 322 * {@link AnnotationsAttribute#invisibleTag}. 323 * </p> 324 * 325 * @param name attribute name 326 * @return an <code>AttributeInfo</code> object or null. 327 * @see #getAttributes() 328 */ getAttribute(String name)329 public AttributeInfo getAttribute(String name) { 330 return AttributeInfo.lookup(attribute, name); 331 } 332 333 /** 334 * Removes an attribute with the specified name. 335 * 336 * @param name attribute name. 337 * @return the removed attribute or null. 338 * @since 3.21 339 */ removeAttribute(String name)340 public AttributeInfo removeAttribute(String name) { 341 return AttributeInfo.remove(attribute, name); 342 } 343 344 /** 345 * Appends an attribute. If there is already an attribute with the same 346 * name, the new one substitutes for it. 347 * 348 * @see #getAttributes() 349 */ addAttribute(AttributeInfo info)350 public void addAttribute(AttributeInfo info) { 351 if (attribute == null) 352 attribute = new ArrayList<AttributeInfo>(); 353 354 AttributeInfo.remove(attribute, info.getName()); 355 attribute.add(info); 356 } 357 358 /** 359 * Returns an Exceptions attribute. 360 * 361 * @return an Exceptions attribute or null if it is not specified. 362 */ getExceptionsAttribute()363 public ExceptionsAttribute getExceptionsAttribute() { 364 AttributeInfo info = AttributeInfo.lookup(attribute, 365 ExceptionsAttribute.tag); 366 return (ExceptionsAttribute)info; 367 } 368 369 /** 370 * Returns a Code attribute. 371 * 372 * @return a Code attribute or null if it is not specified. 373 */ getCodeAttribute()374 public CodeAttribute getCodeAttribute() { 375 AttributeInfo info = AttributeInfo.lookup(attribute, CodeAttribute.tag); 376 return (CodeAttribute)info; 377 } 378 379 /** 380 * Removes an Exception attribute. 381 */ removeExceptionsAttribute()382 public void removeExceptionsAttribute() { 383 AttributeInfo.remove(attribute, ExceptionsAttribute.tag); 384 } 385 386 /** 387 * Adds an Exception attribute. 388 * 389 * <p> 390 * The added attribute must share the same constant pool table as this 391 * <code>method_info</code> structure. 392 */ setExceptionsAttribute(ExceptionsAttribute cattr)393 public void setExceptionsAttribute(ExceptionsAttribute cattr) { 394 removeExceptionsAttribute(); 395 if (attribute == null) 396 attribute = new ArrayList<AttributeInfo>(); 397 398 attribute.add(cattr); 399 } 400 401 /** 402 * Removes a Code attribute. 403 */ removeCodeAttribute()404 public void removeCodeAttribute() { 405 AttributeInfo.remove(attribute, CodeAttribute.tag); 406 } 407 408 /** 409 * Adds a Code attribute. 410 * 411 * <p> 412 * The added attribute must share the same constant pool table as this 413 * <code>method_info</code> structure. 414 */ setCodeAttribute(CodeAttribute cattr)415 public void setCodeAttribute(CodeAttribute cattr) { 416 removeCodeAttribute(); 417 if (attribute == null) 418 attribute = new ArrayList<AttributeInfo>(); 419 420 attribute.add(cattr); 421 } 422 423 /** 424 * Rebuilds a stack map table if the class file is for Java 6 425 * or later. Java 5 or older Java VMs do not recognize a stack 426 * map table. If <code>doPreverify</code> is true, this method 427 * also rebuilds a stack map for J2ME (CLDC). 428 * 429 * @param pool used for making type hierarchy. 430 * @param cf rebuild if this class file is for Java 6 or later. 431 * @see #rebuildStackMap(ClassPool) 432 * @see #rebuildStackMapForME(ClassPool) 433 * @see #doPreverify 434 * @since 3.6 435 */ rebuildStackMapIf6(ClassPool pool, ClassFile cf)436 public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) 437 throws BadBytecode 438 { 439 if (cf.getMajorVersion() >= ClassFile.JAVA_6) 440 rebuildStackMap(pool); 441 442 if (doPreverify) 443 rebuildStackMapForME(pool); 444 } 445 446 /** 447 * Rebuilds a stack map table. If no stack map table is included, 448 * a new one is created. If this <code>MethodInfo</code> does not 449 * include a code attribute, nothing happens. 450 * 451 * @param pool used for making type hierarchy. 452 * @see StackMapTable 453 * @since 3.6 454 */ rebuildStackMap(ClassPool pool)455 public void rebuildStackMap(ClassPool pool) throws BadBytecode { 456 CodeAttribute ca = getCodeAttribute(); 457 if (ca != null) { 458 StackMapTable smt = MapMaker.make(pool, this); 459 ca.setAttribute(smt); 460 } 461 } 462 463 /** 464 * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included, 465 * a new one is created. If this <code>MethodInfo</code> does not 466 * include a code attribute, nothing happens. 467 * 468 * @param pool used for making type hierarchy. 469 * @see StackMap 470 * @since 3.12 471 */ rebuildStackMapForME(ClassPool pool)472 public void rebuildStackMapForME(ClassPool pool) throws BadBytecode { 473 CodeAttribute ca = getCodeAttribute(); 474 if (ca != null) { 475 StackMap sm = MapMaker.make2(pool, this); 476 ca.setAttribute(sm); 477 } 478 } 479 480 /** 481 * Returns the line number of the source line corresponding to the specified 482 * bytecode contained in this method. 483 * 484 * @param pos 485 * the position of the bytecode (>= 0). an index into the code 486 * array. 487 * @return -1 if this information is not available. 488 */ getLineNumber(int pos)489 public int getLineNumber(int pos) { 490 CodeAttribute ca = getCodeAttribute(); 491 if (ca == null) 492 return -1; 493 494 LineNumberAttribute ainfo = (LineNumberAttribute)ca 495 .getAttribute(LineNumberAttribute.tag); 496 if (ainfo == null) 497 return -1; 498 499 return ainfo.toLineNumber(pos); 500 } 501 502 /** 503 * Changes a super constructor called by this constructor. 504 * 505 * <p> 506 * This method modifies a call to <code>super()</code>, which should be 507 * at the head of a constructor body, so that a constructor in a different 508 * super class is called. This method does not change actual parameters. 509 * Hence the new super class must have a constructor with the same signature 510 * as the original one. 511 * 512 * <p> 513 * This method should be called when the super class of the class declaring 514 * this method is changed. 515 * 516 * <p> 517 * This method does not perform anything unless this <code>MethodInfo</code> 518 * represents a constructor. 519 * 520 * @param superclass 521 * the new super class 522 */ setSuperclass(String superclass)523 public void setSuperclass(String superclass) throws BadBytecode { 524 if (!isConstructor()) 525 return; 526 527 CodeAttribute ca = getCodeAttribute(); 528 byte[] code = ca.getCode(); 529 CodeIterator iterator = ca.iterator(); 530 int pos = iterator.skipSuperConstructor(); 531 if (pos >= 0) { // not this() 532 ConstPool cp = constPool; 533 int mref = ByteArray.readU16bit(code, pos + 1); 534 int nt = cp.getMethodrefNameAndType(mref); 535 int sc = cp.addClassInfo(superclass); 536 int mref2 = cp.addMethodrefInfo(sc, nt); 537 ByteArray.write16bit(mref2, code, pos + 1); 538 } 539 } 540 read(MethodInfo src, String methodname, Map<String,String> classnames)541 private void read(MethodInfo src, String methodname, Map<String,String> classnames) { 542 ConstPool destCp = constPool; 543 accessFlags = src.accessFlags; 544 name = destCp.addUtf8Info(methodname); 545 cachedName = methodname; 546 ConstPool srcCp = src.constPool; 547 String desc = srcCp.getUtf8Info(src.descriptor); 548 String desc2 = Descriptor.rename(desc, classnames); 549 descriptor = destCp.addUtf8Info(desc2); 550 551 attribute = new ArrayList<AttributeInfo>(); 552 ExceptionsAttribute eattr = src.getExceptionsAttribute(); 553 if (eattr != null) 554 attribute.add(eattr.copy(destCp, classnames)); 555 556 CodeAttribute cattr = src.getCodeAttribute(); 557 if (cattr != null) 558 attribute.add(cattr.copy(destCp, classnames)); 559 } 560 read(DataInputStream in)561 private void read(DataInputStream in) throws IOException { 562 accessFlags = in.readUnsignedShort(); 563 name = in.readUnsignedShort(); 564 descriptor = in.readUnsignedShort(); 565 int n = in.readUnsignedShort(); 566 attribute = new ArrayList<AttributeInfo>(); 567 for (int i = 0; i < n; ++i) 568 attribute.add(AttributeInfo.read(constPool, in)); 569 } 570 write(DataOutputStream out)571 void write(DataOutputStream out) throws IOException { 572 out.writeShort(accessFlags); 573 out.writeShort(name); 574 out.writeShort(descriptor); 575 576 if (attribute == null) 577 out.writeShort(0); 578 else { 579 out.writeShort(attribute.size()); 580 AttributeInfo.writeAll(attribute, out); 581 } 582 } 583 } 584