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