Mercurial > hg > blitz_condensed
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 } |