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