comparison src/com/go/trove/classfile/ClassFile.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3dc0c5604566
1 /* ====================================================================
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
3 * ====================================================================
4 * The Tea Software License, Version 1.1
5 *
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution,
21 * if any, must include the following acknowledgment:
22 * "This product includes software developed by the
23 * Walt Disney Internet Group (http://opensource.go.com/)."
24 * Alternately, this acknowledgment may appear in the software itself,
25 * if and wherever such third-party acknowledgments normally appear.
26 *
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28 * not be used to endorse or promote products derived from this
29 * software without prior written permission. For written
30 * permission, please contact opensource@dig.com.
31 *
32 * 5. Products derived from this software may not be called "Tea",
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35 * written permission of the Walt Disney Internet Group.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48 * ====================================================================
49 *
50 * For more information about Tea, please see http://opensource.go.com/.
51 */
52
53 package com.go.trove.classfile;
54
55 import java.lang.reflect.*;
56 import java.util.Set;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.ArrayList;
60 import java.util.Map;
61 import java.util.HashMap;
62 import java.io.*;
63
64 /******************************************************************************
65 * A class used to create Java class files. Call the writeTo method
66 * to produce a class file.
67 *
68 * <p>See <i>The Java Virtual Machine Specification</i> (ISBN 0-201-63452-X)
69 * for information on how class files are structured. Section 4.1 describes
70 * the ClassFile structure.
71 *
72 * @author Brian S O'Neill
73 * @version
74 * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/17 <!-- $-->
75 */
76 public class ClassFile {
77 private static final int MAGIC = 0xCAFEBABE;
78 private static final int JDK1_1_MAJOR_VERSION = 45;
79 private static final int JDK1_1_MINOR_VERSION = 3;
80
81 private int mMajorVersion = JDK1_1_MAJOR_VERSION;
82 private int mMinorVersion = JDK1_1_MINOR_VERSION;
83
84 private String mClassName;
85 private String mSuperClassName;
86 private String mInnerClassName;
87 private TypeDescriptor mType;
88
89 private ConstantPool mCp;
90
91 private AccessFlags mAccessFlags;
92
93 private ConstantClassInfo mThisClass;
94 private ConstantClassInfo mSuperClass;
95
96 // Holds ConstantInfo objects.
97 private List mInterfaces = new ArrayList(2);
98 private Set mInterfaceSet = new HashSet(7);
99
100 // Holds objects.
101 private List mFields = new ArrayList();
102 private List mMethods = new ArrayList();
103 private List mAttributes = new ArrayList();
104
105 private SourceFileAttr mSource;
106
107 private List mInnerClasses;
108 private int mAnonymousInnerClassCount = 0;
109 private InnerClassesAttr mInnerClassesAttr;
110
111 // Is non-null for inner classes.
112 private ClassFile mOuterClass;
113
114 /**
115 * By default, the ClassFile defines public, non-final, concrete classes.
116 * This constructor creates a ClassFile for a class that extends
117 * java.lang.Object.
118 * <p>
119 * Use the {@link #getAccessFlags access flags} to change the default
120 * modifiers for this class or to turn it into an interface.
121 *
122 * @param className Full class name of the form ex: "java.lang.String".
123 */
124 public ClassFile(String className) {
125 this(className, (String)null);
126 }
127
128 /**
129 * By default, the ClassFile defines public, non-final, concrete classes.
130 * <p>
131 * Use the {@link #getAccessFlags access flags} to change the default
132 * modifiers for this class or to turn it into an interface.
133 *
134 * @param className Full class name of the form ex: "java.lang.String".
135 * @param superClass Super class.
136 */
137 public ClassFile(String className, Class superClass) {
138 this(className, superClass.getName());
139 }
140
141 /**
142 * By default, the ClassFile defines public, non-final, concrete classes.
143 * <p>
144 * Use the {@link #getAccessFlags access flags} to change the default
145 * modifiers for this class or to turn it into an interface.
146 *
147 * @param className Full class name of the form ex: "java.lang.String".
148 * @param superClassName Full super class name.
149 */
150 public ClassFile(String className, String superClassName) {
151 if (superClassName == null) {
152 if (!className.equals(Object.class.getName())) {
153 superClassName = Object.class.getName();
154 }
155 }
156
157 mCp = new ConstantPool();
158
159 // public, non-final, concrete class
160 mAccessFlags = new AccessFlags(Modifier.PUBLIC);
161
162 mThisClass = ConstantClassInfo.make(mCp, className);
163 mSuperClass = ConstantClassInfo.make(mCp, superClassName);
164
165 mClassName = className;
166 mSuperClassName = superClassName;
167 }
168
169 /**
170 * Used to construct a ClassFile when read from a stream.
171 */
172 private ClassFile(ConstantPool cp, AccessFlags accessFlags,
173 ConstantClassInfo thisClass,
174 ConstantClassInfo superClass,
175 ClassFile outerClass) {
176
177 mCp = cp;
178
179 mAccessFlags = accessFlags;
180
181 mThisClass = thisClass;
182 mSuperClass = superClass;
183
184 mClassName = thisClass.getClassName();
185 if (superClass != null) {
186 mSuperClassName = superClass.getClassName();
187 }
188
189 mOuterClass = outerClass;
190 }
191
192 public String getClassName() {
193 return mClassName;
194 }
195
196 public String getSuperClassName() {
197 return mSuperClassName;
198 }
199
200 /**
201 * Returns a TypeDescriptor for the type of this ClassFile.
202 */
203 public TypeDescriptor getType() {
204 if (mType == null) {
205 mType = new TypeDescriptor(mClassName);
206 }
207 return mType;
208 }
209
210 public AccessFlags getAccessFlags() {
211 return mAccessFlags;
212 }
213
214 /**
215 * Returns the names of all the interfaces that this class implements.
216 */
217 public String[] getInterfaces() {
218 int size = mInterfaces.size();
219 String[] names = new String[size];
220
221 for (int i=0; i<size; i++) {
222 names[i] = ((ConstantClassInfo)mInterfaces.get(i)).getClassName();
223 }
224
225 return names;
226 }
227
228 /**
229 * Returns all the fields defined in this class.
230 */
231 public FieldInfo[] getFields() {
232 FieldInfo[] fields = new FieldInfo[mFields.size()];
233 return (FieldInfo[])mFields.toArray(fields);
234 }
235
236 /**
237 * Returns all the methods defined in this class, not including
238 * constructors and static initializers.
239 */
240 public MethodInfo[] getMethods() {
241 int size = mMethods.size();
242 List methodsOnly = new ArrayList(size);
243
244 for (int i=0; i<size; i++) {
245 MethodInfo method = (MethodInfo)mMethods.get(i);
246 String name = method.getName();
247 if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
248 methodsOnly.add(method);
249 }
250 }
251
252 MethodInfo[] methodsArray = new MethodInfo[methodsOnly.size()];
253 return (MethodInfo[])methodsOnly.toArray(methodsArray);
254 }
255
256 /**
257 * Returns all the constructors defined in this class.
258 */
259 public MethodInfo[] getConstructors() {
260 int size = mMethods.size();
261 List ctorsOnly = new ArrayList(size);
262
263 for (int i=0; i<size; i++) {
264 MethodInfo method = (MethodInfo)mMethods.get(i);
265 if ("<init>".equals(method.getName())) {
266 ctorsOnly.add(method);
267 }
268 }
269
270 MethodInfo[] ctorsArray = new MethodInfo[ctorsOnly.size()];
271 return (MethodInfo[])ctorsOnly.toArray(ctorsArray);
272 }
273
274 /**
275 * Returns the static initializer defined in this class or null if there
276 * isn't one.
277 */
278 public MethodInfo getInitializer() {
279 int size = mMethods.size();
280
281 for (int i=0; i<size; i++) {
282 MethodInfo method = (MethodInfo)mMethods.get(i);
283 if ("<clinit>".equals(method.getName())) {
284 return method;
285 }
286 }
287
288 return null;
289 }
290
291 /**
292 * Returns all the inner classes defined in this class. If no inner classes
293 * are defined, then an array of length zero is returned.
294 */
295 public ClassFile[] getInnerClasses() {
296 if (mInnerClasses == null) {
297 return new ClassFile[0];
298 }
299
300 ClassFile[] innerClasses = new ClassFile[mInnerClasses.size()];
301 return (ClassFile[])mInnerClasses.toArray(innerClasses);
302 }
303
304 /**
305 * Returns true if this ClassFile represents an inner class.
306 */
307 public boolean isInnerClass() {
308 return mOuterClass != null;
309 }
310
311 /**
312 * If this ClassFile represents a non-anonymous inner class, returns its
313 * short inner class name.
314 */
315 public String getInnerClassName() {
316 return mInnerClassName;
317 }
318
319 /**
320 * Returns null if this ClassFile does not represent an inner class.
321 *
322 * @see #isInnerClass()
323 */
324 public ClassFile getOuterClass() {
325 return mOuterClass;
326 }
327
328 /**
329 * Returns a value indicating how deeply nested an inner class is with
330 * respect to its outermost enclosing class. For top level classes, 0
331 * is returned. For first level inner classes, 1 is returned, etc.
332 */
333 public int getClassDepth() {
334 int depth = 0;
335
336 ClassFile outer = mOuterClass;
337 while (outer != null) {
338 depth++;
339 outer = outer.mOuterClass;
340 }
341
342 return depth;
343 }
344
345 /**
346 * Returns the source file of this class file or null if not set.
347 */
348 public String getSourceFile() {
349 if (mSource == null) {
350 return null;
351 }
352 else {
353 return mSource.getFileName();
354 }
355 }
356
357 public boolean isSynthetic() {
358 for (int i = mAttributes.size(); --i >= 0; ) {
359 Object obj = mAttributes.get(i);
360 if (obj instanceof SyntheticAttr) {
361 return true;
362 }
363 }
364 return false;
365 }
366
367 public boolean isDeprecated() {
368 for (int i = mAttributes.size(); --i >= 0; ) {
369 Object obj = mAttributes.get(i);
370 if (obj instanceof DeprecatedAttr) {
371 return true;
372 }
373 }
374 return false;
375 }
376
377 /**
378 * Provides access to the ClassFile's ContantPool.
379 *
380 * @return The constant pool for this class file.
381 */
382 public ConstantPool getConstantPool() {
383 return mCp;
384 }
385
386 /**
387 * Add an interface that this class implements.
388 *
389 * @param interfaceName Full interface name.
390 */
391 public void addInterface(String interfaceName) {
392 if (!mInterfaceSet.contains(interfaceName)) {
393 mInterfaces.add(ConstantClassInfo.make(mCp, interfaceName));
394 mInterfaceSet.add(interfaceName);
395 }
396 }
397
398 /**
399 * Add an interface that this class implements.
400 */
401 public void addInterface(Class i) {
402 addInterface(i.getName());
403 }
404
405 /**
406 * Add a field to this class.
407 */
408 public FieldInfo addField(AccessFlags flags,
409 String fieldName,
410 TypeDescriptor type) {
411 FieldInfo fi = new FieldInfo(this, flags, fieldName, type);
412 mFields.add(fi);
413 return fi;
414 }
415
416 /**
417 * Add a method to this class.
418 *
419 * @param ret Is null if method returns void.
420 * @param params May be null if method accepts no parameters.
421 */
422 public MethodInfo addMethod(AccessFlags flags,
423 String methodName,
424 TypeDescriptor ret,
425 TypeDescriptor[] params) {
426 MethodDescriptor md = new MethodDescriptor(ret, params);
427 return addMethod(flags, methodName, md);
428 }
429
430 /**
431 * Add a method to this class.
432 */
433 public MethodInfo addMethod(AccessFlags flags,
434 String methodName,
435 MethodDescriptor md) {
436 MethodInfo mi = new MethodInfo(this, flags, methodName, md);
437 mMethods.add(mi);
438 return mi;
439 }
440
441 /**
442 * Add a method to this class. This method is handy for implementing
443 * methods defined by a pre-existing interface.
444 */
445 public MethodInfo addMethod(Method method) {
446 AccessFlags flags = new AccessFlags(method.getModifiers());
447 flags.setAbstract(false);
448
449 TypeDescriptor ret = new TypeDescriptor(method.getReturnType());
450
451 Class[] paramClasses = method.getParameterTypes();
452 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
453 for (int i=0; i<params.length; i++) {
454 params[i] = new TypeDescriptor(paramClasses[i]);
455 }
456
457 MethodInfo mi = addMethod(flags, method.getName(), ret, params);
458
459 // exception stuff...
460 Class[] exceptions = method.getExceptionTypes();
461 for (int i=0; i<exceptions.length; i++) {
462 mi.addException(exceptions[i].getName());
463 }
464
465 return mi;
466 }
467
468 /**
469 * Add a constructor to this class.
470 *
471 * @param params May be null if constructor accepts no parameters.
472 */
473 public MethodInfo addConstructor(AccessFlags flags,
474 TypeDescriptor[] params) {
475 MethodDescriptor md = new MethodDescriptor(null, params);
476 MethodInfo mi = new MethodInfo(this, flags, "<init>", md);
477 mMethods.add(mi);
478 return mi;
479 }
480
481 /**
482 * Add a static initializer to this class.
483 */
484 public MethodInfo addInitializer() {
485 MethodDescriptor md = new MethodDescriptor(null, null);
486 AccessFlags af = new AccessFlags();
487 af.setStatic(true);
488 MethodInfo mi = new MethodInfo(this, af, "<clinit>", md);
489 mMethods.add(mi);
490 return mi;
491 }
492
493 /**
494 * Add an inner class to this class. By default, inner classes are private
495 * static.
496 *
497 * @param innerClassName Optional short inner class name.
498 */
499 public ClassFile addInnerClass(String innerClassName) {
500 return addInnerClass(innerClassName, (String)null);
501 }
502
503 /**
504 * Add an inner class to this class. By default, inner classes are private
505 * static.
506 *
507 * @param innerClassName Optional short inner class name.
508 * @param superClass Super class.
509 */
510 public ClassFile addInnerClass(String innerClassName, Class superClass) {
511 return addInnerClass(innerClassName, superClass.getName());
512 }
513
514 /**
515 * Add an inner class to this class. By default, inner classes are private
516 * static.
517 *
518 * @param innerClassName Optional short inner class name.
519 * @param superClassName Full super class name.
520 * @param isStatic True specifies a static inner class.
521 */
522 public ClassFile addInnerClass(String innerClassName,
523 String superClassName) {
524 String fullInnerClassName;
525 if (innerClassName == null) {
526 fullInnerClassName =
527 mClassName + '$' + (++mAnonymousInnerClassCount);
528 }
529 else {
530 fullInnerClassName = mClassName + '$' + innerClassName;
531 }
532
533 ClassFile inner = new ClassFile(fullInnerClassName, superClassName);
534 AccessFlags access = inner.getAccessFlags();
535 access.setPrivate(true);
536 access.setStatic(true);
537 inner.mInnerClassName = innerClassName;
538 inner.mOuterClass = this;
539
540 if (mInnerClasses == null) {
541 mInnerClasses = new ArrayList();
542 }
543
544 mInnerClasses.add(inner);
545
546 // Record the inner class in this, the outer class.
547 if (mInnerClassesAttr == null) {
548 addAttribute(new InnerClassesAttr(mCp));
549 }
550
551 mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName,
552 innerClassName, access);
553
554 // Record the inner class in itself.
555 inner.addAttribute(new InnerClassesAttr(inner.getConstantPool()));
556 inner.mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName,
557 innerClassName, access);
558
559 return inner;
560 }
561
562 /**
563 * Set the source file of this class file by adding a source file
564 * attribute. The source doesn't actually have to be a file,
565 * but the virtual machine spec names the attribute "SourceFile_attribute".
566 */
567 public void setSourceFile(String fileName) {
568 addAttribute(new SourceFileAttr(mCp, fileName));
569 }
570
571 /**
572 * Mark this class as being synthetic by adding a special attribute.
573 */
574 public void markSynthetic() {
575 addAttribute(new SyntheticAttr(mCp));
576 }
577
578 /**
579 * Mark this class as being deprecated by adding a special attribute.
580 */
581 public void markDeprecated() {
582 addAttribute(new DeprecatedAttr(mCp));
583 }
584
585 /**
586 * Add an attribute to this class.
587 */
588 public void addAttribute(Attribute attr) {
589 if (attr instanceof SourceFileAttr) {
590 if (mSource != null) {
591 mAttributes.remove(mSource);
592 }
593 mSource = (SourceFileAttr)attr;
594 }
595 else if (attr instanceof InnerClassesAttr) {
596 if (mInnerClassesAttr != null) {
597 mAttributes.remove(mInnerClassesAttr);
598 }
599 mInnerClassesAttr = (InnerClassesAttr)attr;
600 }
601
602 mAttributes.add(attr);
603 }
604
605 public Attribute[] getAttributes() {
606 Attribute[] attrs = new Attribute[mAttributes.size()];
607 return (Attribute[])mAttributes.toArray(attrs);
608 }
609
610 /**
611 * Sets the version to use when writing the generated ClassFile. Currently,
612 * only version 45, 3 is supported, and is set by default.
613 *
614 * @exception IllegalArgumentException when the version isn't supported
615 */
616 public void setVersion(int major, int minor)
617 throws IllegalArgumentException {
618
619 if (major != JDK1_1_MAJOR_VERSION ||
620 minor != JDK1_1_MINOR_VERSION) {
621
622 throw new IllegalArgumentException("Version " + major + ", " +
623 minor + " is not supported");
624 }
625
626 mMajorVersion = major;
627 mMinorVersion = minor;
628 }
629
630 /**
631 * Writes the ClassFile to the given OutputStream. When finished, the
632 * stream is flushed, but not closed.
633 */
634 public void writeTo(OutputStream out) throws IOException {
635 if (!(out instanceof DataOutput)) {
636 out = new DataOutputStream(out);
637 }
638
639 writeTo((DataOutput)out);
640
641 out.flush();
642 }
643
644 /**
645 * Writes the ClassFile to the given DataOutput.
646 */
647 public void writeTo(DataOutput dout) throws IOException {
648 dout.writeInt(MAGIC);
649 dout.writeShort(mMinorVersion);
650 dout.writeShort(mMajorVersion);
651
652 mCp.writeTo(dout);
653
654 int modifier = mAccessFlags.getModifier();
655 dout.writeShort(modifier | Modifier.SYNCHRONIZED);
656
657 dout.writeShort(mThisClass.getIndex());
658 if (mSuperClass != null) {
659 dout.writeShort(mSuperClass.getIndex());
660 }
661 else {
662 dout.writeShort(0);
663 }
664
665 int size = mInterfaces.size();
666 if (size > 65535) {
667 throw new RuntimeException
668 ("Interfaces count cannot exceed 65535: " + size);
669 }
670 dout.writeShort(size);
671 for (int i=0; i<size; i++) {
672 int index = ((ConstantInfo)mInterfaces.get(i)).getIndex();
673 dout.writeShort(index);
674 }
675
676 size = mFields.size();
677 if (size > 65535) {
678 throw new RuntimeException
679 ("Field count cannot exceed 65535: " + size);
680 }
681 dout.writeShort(size);
682 for (int i=0; i<size; i++) {
683 FieldInfo field = (FieldInfo)mFields.get(i);
684 field.writeTo(dout);
685 }
686
687 size = mMethods.size();
688 if (size > 65535) {
689 throw new RuntimeException
690 ("Method count cannot exceed 65535: " + size);
691 }
692 dout.writeShort(size);
693 for (int i=0; i<size; i++) {
694 MethodInfo method = (MethodInfo)mMethods.get(i);
695 method.writeTo(dout);
696 }
697
698 size = mAttributes.size();
699 if (size > 65535) {
700 throw new RuntimeException
701 ("Attribute count cannot exceed 65535: " + size);
702 }
703 dout.writeShort(size);
704 for (int i=0; i<size; i++) {
705 Attribute attr = (Attribute)mAttributes.get(i);
706 attr.writeTo(dout);
707 }
708 }
709
710 /**
711 * Reads a ClassFile from the given InputStream. With this method, inner
712 * classes cannot be loaded, and custom attributes cannot be defined.
713 *
714 * @param in source of class file data
715 * @throws IOException for I/O error or if classfile is invalid.
716 * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
717 * of range.
718 * @throws ClassCastException if a constant pool index references the
719 * wrong type.
720 */
721 public static ClassFile readFrom(InputStream in) throws IOException {
722 return readFrom(in, null, null);
723 }
724
725 /**
726 * Reads a ClassFile from the given DataInput. With this method, inner
727 * classes cannot be loaded, and custom attributes cannot be defined.
728 *
729 * @param din source of class file data
730 * @throws IOException for I/O error or if classfile is invalid.
731 * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
732 * of range.
733 * @throws ClassCastException if a constant pool index references the
734 * wrong type.
735 */
736 public static ClassFile readFrom(DataInput din) throws IOException {
737 return readFrom(din, null, null);
738 }
739
740 /**
741 * Reads a ClassFile from the given InputStream. A
742 * {@link ClassFileDataLoader} may be provided, which allows inner class
743 * definitions to be loaded. Also, an {@link AttributeFactory} may be
744 * provided, which allows non-standard attributes to be read. All
745 * remaining unknown attribute types are captured, but are not decoded.
746 *
747 * @param in source of class file data
748 * @param loader optional loader for reading inner class definitions
749 * @param attrFactory optional factory for reading custom attributes
750 * @throws IOException for I/O error or if classfile is invalid.
751 * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
752 * of range.
753 * @throws ClassCastException if a constant pool index references the
754 * wrong type.
755 */
756 public static ClassFile readFrom(InputStream in,
757 ClassFileDataLoader loader,
758 AttributeFactory attrFactory)
759 throws IOException
760 {
761 if (!(in instanceof DataInput)) {
762 in = new DataInputStream(in);
763 }
764 return readFrom((DataInput)in, loader, attrFactory);
765 }
766
767 /**
768 * Reads a ClassFile from the given DataInput. A
769 * {@link ClassFileDataLoader} may be provided, which allows inner class
770 * definitions to be loaded. Also, an {@link AttributeFactory} may be
771 * provided, which allows non-standard attributes to be read. All
772 * remaining unknown attribute types are captured, but are not decoded.
773 *
774 * @param din source of class file data
775 * @param loader optional loader for reading inner class definitions
776 * @param attrFactory optional factory for reading custom attributes
777 * @throws IOException for I/O error or if classfile is invalid.
778 * @throws ArrayIndexOutOfBoundsException if a constant pool index is out
779 * of range.
780 * @throws ClassCastException if a constant pool index references the
781 * wrong type.
782 */
783 public static ClassFile readFrom(DataInput din,
784 ClassFileDataLoader loader,
785 AttributeFactory attrFactory)
786 throws IOException
787 {
788 return readFrom(din, loader, attrFactory, new HashMap(11), null);
789 }
790
791 /**
792 * @param loadedClassFiles Maps name to ClassFiles for classes already
793 * loaded. This prevents infinite loop: inner loads outer loads inner...
794 */
795 private static ClassFile readFrom(DataInput din,
796 ClassFileDataLoader loader,
797 AttributeFactory attrFactory,
798 Map loadedClassFiles,
799 ClassFile outerClass)
800 throws IOException
801 {
802 int magic = din.readInt();
803 if (magic != MAGIC) {
804 throw new IOException("Incorrect magic number: 0x" +
805 Integer.toHexString(magic));
806 }
807
808 int minor = din.readUnsignedShort();
809 /*
810 if (minor != JDK1_1_MINOR_VERSION) {
811 throw new IOException("Minor version " + minor +
812 " not supported, version " +
813 JDK1_1_MINOR_VERSION + " is.");
814 }
815 */
816
817 int major = din.readUnsignedShort();
818 /*
819 if (major != JDK1_1_MAJOR_VERSION) {
820 throw new IOException("Major version " + major +
821 "not supported, version " +
822 JDK1_1_MAJOR_VERSION + " is.");
823 }
824 */
825
826 ConstantPool cp = ConstantPool.readFrom(din);
827 AccessFlags accessFlags = new AccessFlags(din.readUnsignedShort());
828 accessFlags.setSynchronized(false);
829
830 int index = din.readUnsignedShort();
831 ConstantClassInfo thisClass = (ConstantClassInfo)cp.getConstant(index);
832
833 index = din.readUnsignedShort();
834 ConstantClassInfo superClass = null;
835 if (index > 0) {
836 superClass = (ConstantClassInfo)cp.getConstant(index);
837 }
838
839 ClassFile cf =
840 new ClassFile(cp, accessFlags, thisClass, superClass, outerClass);
841 loadedClassFiles.put(cf.getClassName(), cf);
842
843 // Read interfaces.
844 int size = din.readUnsignedShort();
845 for (int i=0; i<size; i++) {
846 index = din.readUnsignedShort();
847 ConstantClassInfo info = (ConstantClassInfo)cp.getConstant(index);
848 cf.addInterface(info.getClassName());
849 }
850
851 // Read fields.
852 size = din.readUnsignedShort();
853 for (int i=0; i<size; i++) {
854 cf.mFields.add(FieldInfo.readFrom(cf, din, attrFactory));
855 }
856
857 // Read methods.
858 size = din.readUnsignedShort();
859 for (int i=0; i<size; i++) {
860 cf.mMethods.add(MethodInfo.readFrom(cf, din, attrFactory));
861 }
862
863 // Read attributes.
864 size = din.readUnsignedShort();
865 for (int i=0; i<size; i++) {
866 Attribute attr = Attribute.readFrom(cp, din, attrFactory);
867 cf.addAttribute(attr);
868 if (attr instanceof InnerClassesAttr) {
869 cf.mInnerClassesAttr = (InnerClassesAttr)attr;
870 }
871 }
872
873 // Load inner and outer classes.
874 if (cf.mInnerClassesAttr != null && loader != null) {
875 InnerClassesAttr.Info[] infos =
876 cf.mInnerClassesAttr.getInnerClassesInfo();
877 for (int i=0; i<infos.length; i++) {
878 InnerClassesAttr.Info info = infos[i];
879
880 if (thisClass.equals(info.getInnerClass())) {
881 // This class is an inner class.
882 if (info.getInnerClassName() != null) {
883 cf.mInnerClassName = info.getInnerClassName();
884 }
885 ConstantClassInfo outer = info.getOuterClass();
886 if (cf.mOuterClass == null && outer != null) {
887 cf.mOuterClass = readOuterClass
888 (outer, loader, attrFactory, loadedClassFiles);
889 }
890 AccessFlags innerFlags = info.getAccessFlags();
891 accessFlags.setStatic(innerFlags.isStatic());
892 accessFlags.setPrivate(innerFlags.isPrivate());
893 accessFlags.setProtected(innerFlags.isProtected());
894 accessFlags.setPublic(innerFlags.isPublic());
895 }
896 else if (thisClass.equals(info.getOuterClass())) {
897 // This class is an outer class.
898 ConstantClassInfo inner = info.getInnerClass();
899 if (inner != null) {
900 ClassFile innerClass = readInnerClass
901 (inner, loader, attrFactory, loadedClassFiles, cf);
902
903 if (innerClass != null) {
904 if (innerClass.getInnerClassName() == null) {
905 innerClass.mInnerClassName =
906 info.getInnerClassName();
907 }
908 if (cf.mInnerClasses == null) {
909 cf.mInnerClasses = new ArrayList();
910 }
911 cf.mInnerClasses.add(innerClass);
912 }
913 }
914 }
915 }
916 }
917
918 return cf;
919 }
920
921 private static ClassFile readOuterClass(ConstantClassInfo outer,
922 ClassFileDataLoader loader,
923 AttributeFactory attrFactory,
924 Map loadedClassFiles)
925 throws IOException
926 {
927 String name = outer.getClassName();
928
929 ClassFile outerClass = (ClassFile)loadedClassFiles.get(name);
930 if (outerClass != null) {
931 return outerClass;
932 }
933
934 InputStream in = loader.getClassData(name);
935 if (in == null) {
936 return null;
937 }
938
939 if (!(in instanceof DataInput)) {
940 in = new DataInputStream(in);
941 }
942
943 return readFrom
944 ((DataInput)in, loader, attrFactory, loadedClassFiles, null);
945 }
946
947 private static ClassFile readInnerClass(ConstantClassInfo inner,
948 ClassFileDataLoader loader,
949 AttributeFactory attrFactory,
950 Map loadedClassFiles,
951 ClassFile outerClass)
952 throws IOException
953 {
954 String name = inner.getClassName();
955
956 ClassFile innerClass = (ClassFile)loadedClassFiles.get(name);
957 if (innerClass != null) {
958 return innerClass;
959 }
960
961 InputStream in = loader.getClassData(name);
962 if (in == null) {
963 return null;
964 }
965
966 if (!(in instanceof DataInput)) {
967 in = new DataInputStream(in);
968 }
969
970 return readFrom
971 ((DataInput)in, loader, attrFactory, loadedClassFiles, outerClass);
972 }
973 }