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 16package javassist.bytecode; 17 18import java.io.DataInputStream; 19import java.io.DataOutputStream; 20import java.io.IOException; 21import java.util.ArrayList; 22import java.util.List; 23import java.util.ListIterator; 24import java.util.Map; 25import javassist.CannotCompileException; 26 27/** 28 * <code>ClassFile</code> represents a Java <code>.class</code> file, which 29 * consists of a constant pool, methods, fields, and attributes. 30 * 31 * @see javassist.CtClass#getClassFile() 32 */ 33public final class ClassFile { 34 int major, minor; // version number 35 ConstPool constPool; 36 int thisClass; 37 int accessFlags; 38 int superClass; 39 int[] interfaces; 40 ArrayList fields; 41 ArrayList methods; 42 ArrayList attributes; 43 String thisclassname; // not JVM-internal name 44 String[] cachedInterfaces; 45 String cachedSuperclass; 46 47 /** 48 * The major version number of class files 49 * for JDK 1.1. 50 */ 51 public static final int JAVA_1 = 45; 52 53 /** 54 * The major version number of class files 55 * for JDK 1.2. 56 */ 57 public static final int JAVA_2 = 46; 58 59 /** 60 * The major version number of class files 61 * for JDK 1.3. 62 */ 63 public static final int JAVA_3 = 47; 64 65 /** 66 * The major version number of class files 67 * for JDK 1.4. 68 */ 69 public static final int JAVA_4 = 48; 70 71 /** 72 * The major version number of class files 73 * for JDK 1.5. 74 */ 75 public static final int JAVA_5 = 49; 76 77 /** 78 * The major version number of class files 79 * for JDK 1.6. 80 */ 81 public static final int JAVA_6 = 50; 82 83 /** 84 * The major version number of class files 85 * for JDK 1.7. 86 */ 87 public static final int JAVA_7 = 51; 88 89 /** 90 * The major version number of class files created 91 * from scratch. The default value is 47 (JDK 1.3) 92 * or 49 (JDK 1.5) if the JVM supports <code>java.lang.StringBuilder</code>. 93 */ 94 public static int MAJOR_VERSION = JAVA_3; 95 96 static { 97 try { 98 Class.forName("java.lang.StringBuilder"); 99 MAJOR_VERSION = JAVA_5; 100 } 101 catch (Throwable t) {} 102 } 103 104 /** 105 * Constructs a class file from a byte stream. 106 */ 107 public ClassFile(DataInputStream in) throws IOException { 108 read(in); 109 } 110 111 /** 112 * Constructs a class file including no members. 113 * 114 * @param isInterface 115 * true if this is an interface. false if this is a class. 116 * @param classname 117 * a fully-qualified class name 118 * @param superclass 119 * a fully-qualified super class name 120 */ 121 public ClassFile(boolean isInterface, String classname, String superclass) { 122 major = MAJOR_VERSION; 123 minor = 0; // JDK 1.3 or later 124 constPool = new ConstPool(classname); 125 thisClass = constPool.getThisClassInfo(); 126 if (isInterface) 127 accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT; 128 else 129 accessFlags = AccessFlag.SUPER; 130 131 initSuperclass(superclass); 132 interfaces = null; 133 fields = new ArrayList(); 134 methods = new ArrayList(); 135 thisclassname = classname; 136 137 attributes = new ArrayList(); 138 attributes.add(new SourceFileAttribute(constPool, 139 getSourcefileName(thisclassname))); 140 } 141 142 private void initSuperclass(String superclass) { 143 if (superclass != null) { 144 this.superClass = constPool.addClassInfo(superclass); 145 cachedSuperclass = superclass; 146 } 147 else { 148 this.superClass = constPool.addClassInfo("java.lang.Object"); 149 cachedSuperclass = "java.lang.Object"; 150 } 151 } 152 153 private static String getSourcefileName(String qname) { 154 int index = qname.lastIndexOf('.'); 155 if (index >= 0) 156 qname = qname.substring(index + 1); 157 158 return qname + ".java"; 159 } 160 161 /** 162 * Eliminates dead constant pool items. If a method or a field is removed, 163 * the constant pool items used by that method/field become dead items. This 164 * method recreates a constant pool. 165 */ 166 public void compact() { 167 ConstPool cp = compact0(); 168 ArrayList list = methods; 169 int n = list.size(); 170 for (int i = 0; i < n; ++i) { 171 MethodInfo minfo = (MethodInfo)list.get(i); 172 minfo.compact(cp); 173 } 174 175 list = fields; 176 n = list.size(); 177 for (int i = 0; i < n; ++i) { 178 FieldInfo finfo = (FieldInfo)list.get(i); 179 finfo.compact(cp); 180 } 181 182 attributes = AttributeInfo.copyAll(attributes, cp); 183 constPool = cp; 184 } 185 186 private ConstPool compact0() { 187 ConstPool cp = new ConstPool(thisclassname); 188 thisClass = cp.getThisClassInfo(); 189 String sc = getSuperclass(); 190 if (sc != null) 191 superClass = cp.addClassInfo(getSuperclass()); 192 193 if (interfaces != null) { 194 int n = interfaces.length; 195 for (int i = 0; i < n; ++i) 196 interfaces[i] 197 = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); 198 } 199 200 return cp; 201 } 202 203 /** 204 * Discards all attributes, associated with both the class file and the 205 * members such as a code attribute and exceptions attribute. The unused 206 * constant pool entries are also discarded (a new packed constant pool is 207 * constructed). 208 */ 209 public void prune() { 210 ConstPool cp = compact0(); 211 ArrayList newAttributes = new ArrayList(); 212 AttributeInfo invisibleAnnotations 213 = getAttribute(AnnotationsAttribute.invisibleTag); 214 if (invisibleAnnotations != null) { 215 invisibleAnnotations = invisibleAnnotations.copy(cp, null); 216 newAttributes.add(invisibleAnnotations); 217 } 218 219 AttributeInfo visibleAnnotations 220 = getAttribute(AnnotationsAttribute.visibleTag); 221 if (visibleAnnotations != null) { 222 visibleAnnotations = visibleAnnotations.copy(cp, null); 223 newAttributes.add(visibleAnnotations); 224 } 225 226 AttributeInfo signature 227 = getAttribute(SignatureAttribute.tag); 228 if (signature != null) { 229 signature = signature.copy(cp, null); 230 newAttributes.add(signature); 231 } 232 233 ArrayList list = methods; 234 int n = list.size(); 235 for (int i = 0; i < n; ++i) { 236 MethodInfo minfo = (MethodInfo)list.get(i); 237 minfo.prune(cp); 238 } 239 240 list = fields; 241 n = list.size(); 242 for (int i = 0; i < n; ++i) { 243 FieldInfo finfo = (FieldInfo)list.get(i); 244 finfo.prune(cp); 245 } 246 247 attributes = newAttributes; 248 constPool = cp; 249 } 250 251 /** 252 * Returns a constant pool table. 253 */ 254 public ConstPool getConstPool() { 255 return constPool; 256 } 257 258 /** 259 * Returns true if this is an interface. 260 */ 261 public boolean isInterface() { 262 return (accessFlags & AccessFlag.INTERFACE) != 0; 263 } 264 265 /** 266 * Returns true if this is a final class or interface. 267 */ 268 public boolean isFinal() { 269 return (accessFlags & AccessFlag.FINAL) != 0; 270 } 271 272 /** 273 * Returns true if this is an abstract class or an interface. 274 */ 275 public boolean isAbstract() { 276 return (accessFlags & AccessFlag.ABSTRACT) != 0; 277 } 278 279 /** 280 * Returns access flags. 281 * 282 * @see javassist.bytecode.AccessFlag 283 */ 284 public int getAccessFlags() { 285 return accessFlags; 286 } 287 288 /** 289 * Changes access flags. 290 * 291 * @see javassist.bytecode.AccessFlag 292 */ 293 public void setAccessFlags(int acc) { 294 if ((acc & AccessFlag.INTERFACE) == 0) 295 acc |= AccessFlag.SUPER; 296 297 accessFlags = acc; 298 } 299 300 /** 301 * Returns access and property flags of this nested class. 302 * This method returns -1 if the class is not a nested class. 303 * 304 * <p>The returned value is obtained from <code>inner_class_access_flags</code> 305 * of the entry representing this nested class itself 306 * in <code>InnerClasses_attribute</code>>. 307 */ 308 public int getInnerAccessFlags() { 309 InnerClassesAttribute ica 310 = (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag); 311 if (ica == null) 312 return -1; 313 314 String name = getName(); 315 int n = ica.tableLength(); 316 for (int i = 0; i < n; ++i) 317 if (name.equals(ica.innerClass(i))) 318 return ica.accessFlags(i); 319 320 return -1; 321 } 322 323 /** 324 * Returns the class name. 325 */ 326 public String getName() { 327 return thisclassname; 328 } 329 330 /** 331 * Sets the class name. This method substitutes the new name for all 332 * occurrences of the old class name in the class file. 333 */ 334 public void setName(String name) { 335 renameClass(thisclassname, name); 336 } 337 338 /** 339 * Returns the super class name. 340 */ 341 public String getSuperclass() { 342 if (cachedSuperclass == null) 343 cachedSuperclass = constPool.getClassInfo(superClass); 344 345 return cachedSuperclass; 346 } 347 348 /** 349 * Returns the index of the constant pool entry representing the super 350 * class. 351 */ 352 public int getSuperclassId() { 353 return superClass; 354 } 355 356 /** 357 * Sets the super class. 358 * 359 * <p> 360 * The new super class should inherit from the old super class. 361 * This method modifies constructors so that they call constructors declared 362 * in the new super class. 363 */ 364 public void setSuperclass(String superclass) throws CannotCompileException { 365 if (superclass == null) 366 superclass = "java.lang.Object"; 367 368 try { 369 this.superClass = constPool.addClassInfo(superclass); 370 ArrayList list = methods; 371 int n = list.size(); 372 for (int i = 0; i < n; ++i) { 373 MethodInfo minfo = (MethodInfo)list.get(i); 374 minfo.setSuperclass(superclass); 375 } 376 } 377 catch (BadBytecode e) { 378 throw new CannotCompileException(e); 379 } 380 cachedSuperclass = superclass; 381 } 382 383 /** 384 * Replaces all occurrences of a class name in the class file. 385 * 386 * <p> 387 * If class X is substituted for class Y in the class file, X and Y must 388 * have the same signature. If Y provides a method m(), X must provide it 389 * even if X inherits m() from the super class. If this fact is not 390 * guaranteed, the bytecode verifier may cause an error. 391 * 392 * @param oldname 393 * the replaced class name 394 * @param newname 395 * the substituted class name 396 */ 397 public final void renameClass(String oldname, String newname) { 398 ArrayList list; 399 int n; 400 401 if (oldname.equals(newname)) 402 return; 403 404 if (oldname.equals(thisclassname)) 405 thisclassname = newname; 406 407 oldname = Descriptor.toJvmName(oldname); 408 newname = Descriptor.toJvmName(newname); 409 constPool.renameClass(oldname, newname); 410 411 AttributeInfo.renameClass(attributes, oldname, newname); 412 list = methods; 413 n = list.size(); 414 for (int i = 0; i < n; ++i) { 415 MethodInfo minfo = (MethodInfo)list.get(i); 416 String desc = minfo.getDescriptor(); 417 minfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); 418 AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname); 419 } 420 421 list = fields; 422 n = list.size(); 423 for (int i = 0; i < n; ++i) { 424 FieldInfo finfo = (FieldInfo)list.get(i); 425 String desc = finfo.getDescriptor(); 426 finfo.setDescriptor(Descriptor.rename(desc, oldname, newname)); 427 AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname); 428 } 429 } 430 431 /** 432 * Replaces all occurrences of several class names in the class file. 433 * 434 * @param classnames 435 * specifies which class name is replaced with which new name. 436 * Class names must be described with the JVM-internal 437 * representation like <code>java/lang/Object</code>. 438 * @see #renameClass(String,String) 439 */ 440 public final void renameClass(Map classnames) { 441 String jvmNewThisName = (String)classnames.get(Descriptor 442 .toJvmName(thisclassname)); 443 if (jvmNewThisName != null) 444 thisclassname = Descriptor.toJavaName(jvmNewThisName); 445 446 constPool.renameClass(classnames); 447 448 AttributeInfo.renameClass(attributes, classnames); 449 ArrayList list = methods; 450 int n = list.size(); 451 for (int i = 0; i < n; ++i) { 452 MethodInfo minfo = (MethodInfo)list.get(i); 453 String desc = minfo.getDescriptor(); 454 minfo.setDescriptor(Descriptor.rename(desc, classnames)); 455 AttributeInfo.renameClass(minfo.getAttributes(), classnames); 456 } 457 458 list = fields; 459 n = list.size(); 460 for (int i = 0; i < n; ++i) { 461 FieldInfo finfo = (FieldInfo)list.get(i); 462 String desc = finfo.getDescriptor(); 463 finfo.setDescriptor(Descriptor.rename(desc, classnames)); 464 AttributeInfo.renameClass(finfo.getAttributes(), classnames); 465 } 466 } 467 468 /** 469 * Internal-use only. 470 * <code>CtClass.getRefClasses()</code> calls this method. 471 */ 472 public final void getRefClasses(Map classnames) { 473 constPool.renameClass(classnames); 474 475 AttributeInfo.getRefClasses(attributes, classnames); 476 ArrayList list = methods; 477 int n = list.size(); 478 for (int i = 0; i < n; ++i) { 479 MethodInfo minfo = (MethodInfo)list.get(i); 480 String desc = minfo.getDescriptor(); 481 Descriptor.rename(desc, classnames); 482 AttributeInfo.getRefClasses(minfo.getAttributes(), classnames); 483 } 484 485 list = fields; 486 n = list.size(); 487 for (int i = 0; i < n; ++i) { 488 FieldInfo finfo = (FieldInfo)list.get(i); 489 String desc = finfo.getDescriptor(); 490 Descriptor.rename(desc, classnames); 491 AttributeInfo.getRefClasses(finfo.getAttributes(), classnames); 492 } 493 } 494 495 /** 496 * Returns the names of the interfaces implemented by the class. 497 * The returned array is read only. 498 */ 499 public String[] getInterfaces() { 500 if (cachedInterfaces != null) 501 return cachedInterfaces; 502 503 String[] rtn = null; 504 if (interfaces == null) 505 rtn = new String[0]; 506 else { 507 int n = interfaces.length; 508 String[] list = new String[n]; 509 for (int i = 0; i < n; ++i) 510 list[i] = constPool.getClassInfo(interfaces[i]); 511 512 rtn = list; 513 } 514 515 cachedInterfaces = rtn; 516 return rtn; 517 } 518 519 /** 520 * Sets the interfaces. 521 * 522 * @param nameList 523 * the names of the interfaces. 524 */ 525 public void setInterfaces(String[] nameList) { 526 cachedInterfaces = null; 527 if (nameList != null) { 528 int n = nameList.length; 529 interfaces = new int[n]; 530 for (int i = 0; i < n; ++i) 531 interfaces[i] = constPool.addClassInfo(nameList[i]); 532 } 533 } 534 535 /** 536 * Appends an interface to the interfaces implemented by the class. 537 */ 538 public void addInterface(String name) { 539 cachedInterfaces = null; 540 int info = constPool.addClassInfo(name); 541 if (interfaces == null) { 542 interfaces = new int[1]; 543 interfaces[0] = info; 544 } 545 else { 546 int n = interfaces.length; 547 int[] newarray = new int[n + 1]; 548 System.arraycopy(interfaces, 0, newarray, 0, n); 549 newarray[n] = info; 550 interfaces = newarray; 551 } 552 } 553 554 /** 555 * Returns all the fields declared in the class. 556 * 557 * @return a list of <code>FieldInfo</code>. 558 * @see FieldInfo 559 */ 560 public List getFields() { 561 return fields; 562 } 563 564 /** 565 * Appends a field to the class. 566 * 567 * @throws DuplicateMemberException when the field is already included. 568 */ 569 public void addField(FieldInfo finfo) throws DuplicateMemberException { 570 testExistingField(finfo.getName(), finfo.getDescriptor()); 571 fields.add(finfo); 572 } 573 574 /** 575 * Just appends a field to the class. 576 * It does not check field duplication. 577 * Use this method only when minimizing performance overheads 578 * is seriously required. 579 * 580 * @since 3.13 581 */ 582 public final void addField2(FieldInfo finfo) { 583 fields.add(finfo); 584 } 585 586 private void testExistingField(String name, String descriptor) 587 throws DuplicateMemberException { 588 ListIterator it = fields.listIterator(0); 589 while (it.hasNext()) { 590 FieldInfo minfo = (FieldInfo)it.next(); 591 if (minfo.getName().equals(name)) 592 throw new DuplicateMemberException("duplicate field: " + name); 593 } 594 } 595 596 /** 597 * Returns all the methods declared in the class. 598 * 599 * @return a list of <code>MethodInfo</code>. 600 * @see MethodInfo 601 */ 602 public List getMethods() { 603 return methods; 604 } 605 606 /** 607 * Returns the method with the specified name. If there are multiple methods 608 * with that name, this method returns one of them. 609 * 610 * @return null if no such method is found. 611 */ 612 public MethodInfo getMethod(String name) { 613 ArrayList list = methods; 614 int n = list.size(); 615 for (int i = 0; i < n; ++i) { 616 MethodInfo minfo = (MethodInfo)list.get(i); 617 if (minfo.getName().equals(name)) 618 return minfo; 619 } 620 621 return null; 622 } 623 624 /** 625 * Returns a static initializer (class initializer), or null if it does not 626 * exist. 627 */ 628 public MethodInfo getStaticInitializer() { 629 return getMethod(MethodInfo.nameClinit); 630 } 631 632 /** 633 * Appends a method to the class. 634 * If there is a bridge method with the same name and signature, 635 * then the bridge method is removed before a new method is added. 636 * 637 * @throws DuplicateMemberException when the method is already included. 638 */ 639 public void addMethod(MethodInfo minfo) throws DuplicateMemberException { 640 testExistingMethod(minfo); 641 methods.add(minfo); 642 } 643 644 /** 645 * Just appends a method to the class. 646 * It does not check method duplication or remove a bridge method. 647 * Use this method only when minimizing performance overheads 648 * is seriously required. 649 * 650 * @since 3.13 651 */ 652 public final void addMethod2(MethodInfo minfo) { 653 methods.add(minfo); 654 } 655 656 private void testExistingMethod(MethodInfo newMinfo) 657 throws DuplicateMemberException 658 { 659 String name = newMinfo.getName(); 660 String descriptor = newMinfo.getDescriptor(); 661 ListIterator it = methods.listIterator(0); 662 while (it.hasNext()) 663 if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it)) 664 throw new DuplicateMemberException("duplicate method: " + name 665 + " in " + this.getName()); 666 } 667 668 private static boolean isDuplicated(MethodInfo newMethod, String newName, 669 String newDesc, MethodInfo minfo, 670 ListIterator it) 671 { 672 if (!minfo.getName().equals(newName)) 673 return false; 674 675 String desc = minfo.getDescriptor(); 676 if (!Descriptor.eqParamTypes(desc, newDesc)) 677 return false; 678 679 if (desc.equals(newDesc)) { 680 if (notBridgeMethod(minfo)) 681 return true; 682 else { 683 it.remove(); 684 return false; 685 } 686 } 687 else 688 return notBridgeMethod(minfo) && notBridgeMethod(newMethod); 689 } 690 691 /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed. 692 */ 693 private static boolean notBridgeMethod(MethodInfo minfo) { 694 return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0; 695 } 696 697 /** 698 * Returns all the attributes. The returned <code>List</code> object 699 * is shared with this object. If you add a new attribute to the list, 700 * the attribute is also added to the classs file represented by this 701 * object. If you remove an attribute from the list, it is also removed 702 * from the class file. 703 * 704 * @return a list of <code>AttributeInfo</code> objects. 705 * @see AttributeInfo 706 */ 707 public List getAttributes() { 708 return attributes; 709 } 710 711 /** 712 * Returns the attribute with the specified name. If there are multiple 713 * attributes with that name, this method returns either of them. It 714 * returns null if the specified attributed is not found. 715 * 716 * @param name attribute name 717 * @see #getAttributes() 718 */ 719 public AttributeInfo getAttribute(String name) { 720 ArrayList list = attributes; 721 int n = list.size(); 722 for (int i = 0; i < n; ++i) { 723 AttributeInfo ai = (AttributeInfo)list.get(i); 724 if (ai.getName().equals(name)) 725 return ai; 726 } 727 728 return null; 729 } 730 731 /** 732 * Appends an attribute. If there is already an attribute with the same 733 * name, the new one substitutes for it. 734 * 735 * @see #getAttributes() 736 */ 737 public void addAttribute(AttributeInfo info) { 738 AttributeInfo.remove(attributes, info.getName()); 739 attributes.add(info); 740 } 741 742 /** 743 * Returns the source file containing this class. 744 * 745 * @return null if this information is not available. 746 */ 747 public String getSourceFile() { 748 SourceFileAttribute sf 749 = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag); 750 if (sf == null) 751 return null; 752 else 753 return sf.getFileName(); 754 } 755 756 private void read(DataInputStream in) throws IOException { 757 int i, n; 758 int magic = in.readInt(); 759 if (magic != 0xCAFEBABE) 760 throw new IOException("bad magic number: " + Integer.toHexString(magic)); 761 762 minor = in.readUnsignedShort(); 763 major = in.readUnsignedShort(); 764 constPool = new ConstPool(in); 765 accessFlags = in.readUnsignedShort(); 766 thisClass = in.readUnsignedShort(); 767 constPool.setThisClassInfo(thisClass); 768 superClass = in.readUnsignedShort(); 769 n = in.readUnsignedShort(); 770 if (n == 0) 771 interfaces = null; 772 else { 773 interfaces = new int[n]; 774 for (i = 0; i < n; ++i) 775 interfaces[i] = in.readUnsignedShort(); 776 } 777 778 ConstPool cp = constPool; 779 n = in.readUnsignedShort(); 780 fields = new ArrayList(); 781 for (i = 0; i < n; ++i) 782 addField2(new FieldInfo(cp, in)); 783 784 n = in.readUnsignedShort(); 785 methods = new ArrayList(); 786 for (i = 0; i < n; ++i) 787 addMethod2(new MethodInfo(cp, in)); 788 789 attributes = new ArrayList(); 790 n = in.readUnsignedShort(); 791 for (i = 0; i < n; ++i) 792 addAttribute(AttributeInfo.read(cp, in)); 793 794 thisclassname = constPool.getClassInfo(thisClass); 795 } 796 797 /** 798 * Writes a class file represened by this object into an output stream. 799 */ 800 public void write(DataOutputStream out) throws IOException { 801 int i, n; 802 803 out.writeInt(0xCAFEBABE); // magic 804 out.writeShort(minor); // minor version 805 out.writeShort(major); // major version 806 constPool.write(out); // constant pool 807 out.writeShort(accessFlags); 808 out.writeShort(thisClass); 809 out.writeShort(superClass); 810 811 if (interfaces == null) 812 n = 0; 813 else 814 n = interfaces.length; 815 816 out.writeShort(n); 817 for (i = 0; i < n; ++i) 818 out.writeShort(interfaces[i]); 819 820 ArrayList list = fields; 821 n = list.size(); 822 out.writeShort(n); 823 for (i = 0; i < n; ++i) { 824 FieldInfo finfo = (FieldInfo)list.get(i); 825 finfo.write(out); 826 } 827 828 list = methods; 829 n = list.size(); 830 out.writeShort(n); 831 for (i = 0; i < n; ++i) { 832 MethodInfo minfo = (MethodInfo)list.get(i); 833 minfo.write(out); 834 } 835 836 out.writeShort(attributes.size()); 837 AttributeInfo.writeAll(attributes, out); 838 } 839 840 /** 841 * Get the Major version. 842 * 843 * @return the major version 844 */ 845 public int getMajorVersion() { 846 return major; 847 } 848 849 /** 850 * Set the major version. 851 * 852 * @param major 853 * the major version 854 */ 855 public void setMajorVersion(int major) { 856 this.major = major; 857 } 858 859 /** 860 * Get the minor version. 861 * 862 * @return the minor version 863 */ 864 public int getMinorVersion() { 865 return minor; 866 } 867 868 /** 869 * Set the minor version. 870 * 871 * @param minor 872 * the minor version 873 */ 874 public void setMinorVersion(int minor) { 875 this.minor = minor; 876 } 877 878 /** 879 * Sets the major and minor version to Java 5. 880 * 881 * If the major version is older than 49, Java 5 882 * extensions such as annotations are ignored 883 * by the JVM. 884 */ 885 public void setVersionToJava5() { 886 this.major = 49; 887 this.minor = 0; 888 } 889} 890