KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > polyglot > types > reflect > ClassFile


1 package polyglot.types.reflect;
2
3 import polyglot.main.Report;
4 import polyglot.types.*;
5 import polyglot.util.*;
6 import java.io.*;
7 import java.util.*;
8
9 /**
10  * ClassFile basically represents a Java classfile as it is found on
11  * disk. The classfile is modeled according to the Java Virtual Machine
12  * Specification. Methods are provided to edit the classfile at a very
13  * low level.
14  *
15  * @see polyglot.types.reflect Attribute
16  * @see polyglot.types.reflect Constant
17  * @see polyglot.types.reflect Field
18  * @see polyglot.types.reflect Method
19  *
20  * @author Nate Nystrom
21  * (<a HREF="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
22  */

23 public class ClassFile implements LazyClassInitializer {
24     Constant[] constants; // The constant pool
25
int modifiers; // This class's modifer bit field
26
int thisClass;
27     int superClass;
28     int[] interfaces;
29     Field[] fields;
30     Method[] methods;
31     Attribute[] attrs;
32     InnerClasses innerClasses;
33     File classFileSource;
34
35     static Collection verbose = ClassFileLoader.verbose;
36   
37     /**
38      * Constructor. This constructor parses the class file from the byte array
39      *
40      * @param code
41      * A byte array containing the class data
42      */

43     public ClassFile(File classFileSource, byte[] code) {
44         this.classFileSource = classFileSource;
45
46         try {
47             ByteArrayInputStream bin = new ByteArrayInputStream(code);
48             DataInputStream in = new DataInputStream(bin);
49             read(in);
50             in.close();
51             bin.close();
52         }
53         catch (IOException e) {
54             throw new InternalCompilerError("I/O exception on ByteArrayInputStream");
55         }
56     }
57
58     public boolean fromClassFile() {
59       return true;
60     }
61
62     JLCInfo getJLCInfo(String JavaDoc ts) {
63       // Check if already set.
64
JLCInfo jlc = (JLCInfo) jlcInfo.get(ts);
65
66       if (jlc != null) {
67         return jlc;
68       }
69
70       jlc = new JLCInfo();
71       jlcInfo.put(ts, jlc);
72
73       try {
74         int mask = 0;
75
76         for (int i = 0; i < fields.length; i++) {
77           if (fields[i].name().equals("jlc$SourceLastModified$" + ts)) {
78             jlc.sourceLastModified = fields[i].getLong();
79             mask |= 1;
80           }
81           else if (fields[i].name().equals("jlc$CompilerVersion$" + ts)) {
82             jlc.compilerVersion = fields[i].getString();
83             mask |= 2;
84           }
85           else if (fields[i].name().equals("jlc$ClassType$" + ts)) {
86             jlc.encodedClassType = fields[i].getString();
87             mask |= 4;
88           }
89         }
90
91         if (mask != 7) {
92           // Not all the information is there. Reset to default.
93
jlc.sourceLastModified = 0;
94           jlc.compilerVersion = null;
95           jlc.encodedClassType = null;
96         }
97       }
98       catch (SemanticException e) {
99         jlc.sourceLastModified = 0;
100         jlc.compilerVersion = null;
101         jlc.encodedClassType = null;
102       }
103
104       return jlc;
105     }
106
107     /**
108      * Get the encoded source modified time.
109      */

110     public long sourceLastModified(String JavaDoc ts) {
111       JLCInfo jlc = getJLCInfo(ts);
112       return jlc.sourceLastModified;
113     }
114
115     public long rawSourceLastModified() {
116       return classFileSource.lastModified();
117     }
118
119     /**
120      * Get the encoded compiler version used to compile the source.
121      */

122     public String JavaDoc compilerVersion(String JavaDoc ts) {
123       JLCInfo jlc = getJLCInfo(ts);
124       return jlc.compilerVersion;
125     }
126
127     /**
128      * Get the encoded class type.
129      */

130     public String JavaDoc encodedClassType(String JavaDoc ts) {
131       JLCInfo jlc = getJLCInfo(ts);
132       return jlc.encodedClassType;
133     }
134
135     Map jlcInfo = new HashMap();
136
137     /**
138      * Read the class file.
139      */

140     void read(DataInputStream in) throws IOException {
141         // Read in file contents from stream
142
readHeader(in);
143         readConstantPool(in);
144         readAccessFlags(in);
145         readClassInfo(in);
146         readFields(in);
147         readMethods(in);
148         readAttributes(in);
149     }
150
151     /**
152      * Extract the class type from the class file.
153      */

154     public ParsedClassType type(TypeSystem ts) throws SemanticException {
155         ParsedClassType ct = createType(ts);
156
157         if (ts.equals(ct, ts.Object())) {
158             ct.superType(null);
159         }
160         else {
161             String JavaDoc superName = classNameCP(superClass);
162
163             if (superName != null) {
164                 ct.superType(typeForName(ts, superName));
165             }
166             else {
167                 ct.superType(ts.Object());
168             }
169         }
170
171         return ct;
172     }
173
174     /**
175      * Initialize <code>ct</code>'s member classes.
176      */

177     public void initMemberClasses(ParsedClassType ct) {
178         if (innerClasses == null) {
179             return;
180         }
181
182         TypeSystem ts = ct.typeSystem();
183
184         for (int i = 0; i < innerClasses.classes.length; i++) {
185             InnerClasses.Info c = innerClasses.classes[i];
186
187             if (c.outerClassIndex == thisClass && c.classIndex != 0) {
188                 String JavaDoc name = classNameCP(c.classIndex);
189
190                 int index = name.lastIndexOf('$');
191
192                 // Skip local and anonymous classes.
193
if (index >= 0 && Character.isDigit(name.charAt(index+1))) {
194                     continue;
195                 }
196
197                 // A member class of this class
198
ClassType t = quietTypeForName(ts, name);
199
200                 if (t.isMember()) {
201             if (Report.should_report(verbose, 3))
202                         Report.report(3, "adding member " + t + " to " + ct);
203
204                     ct.addMemberClass(t);
205                     
206                     // HACK: set the access flags of the member class
207
// using the modifier bits of the InnerClass attribute.
208
if (t instanceof ParsedClassType) {
209                         ParsedClassType pt = (ParsedClassType) t;
210                         pt.flags(ts.flagsForBits(c.modifiers));
211                     }
212                 }
213                 else {
214                     throw new InternalCompilerError(name + " should be a member class.");
215                 }
216             }
217         }
218     }
219
220     /**
221      * Initialize <code>ct</code>'s interfaces.
222      */

223     public void initInterfaces(ParsedClassType ct) {
224         TypeSystem ts = ct.typeSystem();
225
226         for (int i = 0; i < interfaces.length; i++) {
227             String JavaDoc name = classNameCP(interfaces[i]);
228             ct.addInterface(quietTypeForName(ts, name));
229         }
230     }
231
232     /**
233      * Initialize <code>ct</code>'s fields.
234      */

235     public void initFields(ParsedClassType ct) {
236         TypeSystem ts = ct.typeSystem();
237
238         // Add the "class" field.
239
LazyClassInitializer init = ts.defaultClassInitializer();
240         init.initFields(ct);
241   
242         for (int i = 0; i < fields.length; i++) {
243             if (! fields[i].name().startsWith("jlc$") &&
244                 ! fields[i].isSynthetic()) {
245                 FieldInstance fi = fields[i].fieldInstance(ts, ct);
246         if (Report.should_report(verbose, 3))
247             Report.report(3, "adding " + fi + " to " + ct);
248                 ct.addField(fi);
249             }
250         }
251     }
252
253     /**
254      * Initialize <code>ct</code>'s methods.
255      */

256     public void initMethods(ParsedClassType ct) {
257         TypeSystem ts = ct.typeSystem();
258
259         for (int i = 0; i < methods.length; i++) {
260             if (! methods[i].name().equals("<init>") &&
261                 ! methods[i].name().equals("<clinit>") &&
262                 ! methods[i].isSynthetic()) {
263                 MethodInstance mi = methods[i].methodInstance(ts, ct);
264         if (Report.should_report(verbose,3))
265             Report.report(3, "adding " + mi + " to " + ct);
266                 ct.addMethod(mi);
267             }
268         }
269     }
270
271     /**
272      * Initialize <code>ct</code>'s constructors.
273      */

274     public void initConstructors(ParsedClassType ct) {
275         TypeSystem ts = ct.typeSystem();
276
277         for (int i = 0; i < methods.length; i++) {
278             if (methods[i].name().equals("<init>") &&
279                 ! methods[i].isSynthetic()) {
280                 ConstructorInstance ci =
281                     methods[i].constructorInstance(ts, ct, fields);
282         if (Report.should_report(verbose,3))
283             Report.report(3, "adding " + ci + " to " + ct);
284                 ct.addConstructor(ci);
285             }
286         }
287     }
288
289     /**
290      * Create an array of <code>t</code>.
291      */

292     Type arrayOf(Type t, int dims) {
293         if (dims == 0) {
294             return t;
295         }
296         else {
297             return t.typeSystem().arrayOf(t, dims);
298         }
299     }
300
301     /**
302      * Convert a descriptor string into a list of types.
303      */

304     List typeListForString(TypeSystem ts, String JavaDoc str) {
305         List types = new ArrayList();
306
307         for (int i = 0; i < str.length(); i++) {
308             int dims = 0;
309
310             while (str.charAt(i) == '[') {
311                 dims++;
312                 i++;
313             }
314
315             switch (str.charAt(i)) {
316                 case 'Z': types.add(arrayOf(ts.Boolean(), dims));
317                           break;
318                 case 'B': types.add(arrayOf(ts.Byte(), dims));
319                           break;
320                 case 'S': types.add(arrayOf(ts.Short(), dims));
321                           break;
322                 case 'C': types.add(arrayOf(ts.Char(), dims));
323                           break;
324                 case 'I': types.add(arrayOf(ts.Int(), dims));
325                           break;
326                 case 'J': types.add(arrayOf(ts.Long(), dims));
327                           break;
328                 case 'F': types.add(arrayOf(ts.Float(), dims));
329                           break;
330                 case 'D': types.add(arrayOf(ts.Double(), dims));
331                           break;
332                 case 'V': types.add(arrayOf(ts.Void(), dims));
333                           break;
334                 case 'L': {
335                     int start = ++i;
336                     while (i < str.length()) {
337                         if (str.charAt(i) == ';') {
338                             String JavaDoc s = str.substring(start, i);
339                             s = s.replace('/', '.');
340                             types.add(arrayOf(quietTypeForName(ts, s), dims));
341                             break;
342                         }
343
344                         i++;
345                     }
346                 }
347             }
348         }
349
350     if (Report.should_report(verbose, 4))
351         Report.report(4, "parsed \"" + str + "\" -> " + types);
352
353         return types;
354     }
355
356     /**
357      * Convert a descriptor string into a type.
358      */

359     Type typeForString(TypeSystem ts, String JavaDoc str) {
360         List l = typeListForString(ts, str);
361
362         if (l.size() == 1) {
363             return (Type) l.get(0);
364         }
365
366         throw new InternalCompilerError("Bad type string: \"" + str + "\"");
367     }
368
369     /**
370      * Convert a String into a type. Throws an InternalCompilerError
371      * if this cannot be done.
372      */

373     ClassType quietTypeForName(TypeSystem ts, String JavaDoc name) {
374     if (Report.should_report(verbose,2))
375         Report.report(2, "resolving " + name);
376
377         try {
378             return (ClassType) ts.systemResolver().find(name);
379         }
380         catch (SemanticException e) {
381             throw new InternalCompilerError("could not load " + name);
382         }
383     }
384
385     /**
386      * Convert a String into a type. Throws a SemanticException if this
387      * cannot be done.
388      */

389     ClassType typeForName(TypeSystem ts, String JavaDoc name) throws SemanticException {
390     if (Report.should_report(verbose,2))
391         Report.report(2, "resolving " + name);
392         return (ClassType) ts.systemResolver().find(name);
393     }
394
395     /**
396      * Create the type for this class file.
397      */

398     ParsedClassType createType(TypeSystem ts) throws SemanticException {
399         // The name is of the form "p.q.C$I$J".
400
String JavaDoc name = classNameCP(thisClass);
401
402     if (Report.should_report(verbose, 2))
403         Report.report(2, "creating ClassType for " + name);
404
405         // Create the ClassType.
406
ParsedClassType ct = ts.createClassType(this);
407
408         ct.flags(ts.flagsForBits(modifiers));
409         ct.position(position());
410
411         // Add unresolved class into the cache to avoid circular resolving.
412
((CachingResolver) ts.systemResolver()).install(name, ct);
413
414         // This is the "p.q" part.
415
String JavaDoc packageName = StringUtil.getPackageComponent(name);
416
417         // Set the ClassType's package.
418
if (! packageName.equals("")) {
419             ct.package_(ts.packageForName(packageName));
420         }
421
422         // This is the "C$I$J" part.
423
String JavaDoc className = StringUtil.getShortNameComponent(name);
424
425         String JavaDoc outerName; // This will be "p.q.C$I"
426
String JavaDoc innerName; // This will be "J"
427

428         outerName = name;
429         innerName = null;
430
431         while (true) {
432             int dollar = outerName.lastIndexOf('$');
433
434             if (dollar >= 0) {
435                 outerName = name.substring(0, dollar);
436                 innerName = name.substring(dollar+1);
437             }
438             else {
439                 outerName = name;
440                 innerName = null;
441                 break;
442             }
443
444             // Try loading the outer class.
445
// This will recursively load its outer class, if any.
446
try {
447                 if (Report.should_report(verbose,2))
448                     Report.report(2, "resolving " + outerName + " for " + name);
449                 ct.outer(typeForName(ts, outerName));
450                 break;
451             }
452             catch (SemanticException e) {
453                 // Failed. The class probably has a '$' in its name.
454
if (Report.should_report(verbose,3))
455                     Report.report(2, "error resolving " + outerName);
456             }
457         }
458
459         ClassType.Kind kind = ClassType.TOP_LEVEL;
460
461         if (innerName != null) {
462             // A nested class. Parse the class name to determine what kind.
463
StringTokenizer st = new StringTokenizer(className, "$");
464
465             while (st.hasMoreTokens()) {
466                 String JavaDoc s = st.nextToken();
467
468                 if (Character.isDigit(s.charAt(0))) {
469                     // Example: C$1
470
kind = ClassType.ANONYMOUS;
471                 }
472                 else if (kind == ClassType.ANONYMOUS) {
473                     // Example: C$1$D
474
kind = ClassType.LOCAL;
475                 }
476                 else {
477                     // Example: C$D
478
kind = ClassType.MEMBER;
479                 }
480             }
481         }
482
483     if (Report.should_report(verbose, 3))
484         Report.report(3, name + " is " + kind);
485
486         ct.kind(kind);
487
488         if (ct.isTopLevel()) {
489             ct.name(className);
490         }
491         else if (ct.isMember() || ct.isLocal()) {
492             ct.name(innerName);
493         }
494
495
496         return ct;
497     }
498
499     /**
500      * Create a position for the class file.
501      */

502     public Position position() {
503         return new Position(name() + ".class");
504     }
505
506   /**
507    * Get the class name at the given constant pool index.
508    */

509   String JavaDoc classNameCP(int index) {
510     Constant c = constants[index];
511
512     if (c != null && c.tag() == Constant.CLASS) {
513       Integer JavaDoc nameIndex = (Integer JavaDoc) c.value();
514       if (nameIndex != null) {
515     c = constants[nameIndex.intValue()];
516     if (c.tag() == Constant.UTF8) {
517       String JavaDoc s = (String JavaDoc) c.value();
518           return s.replace('/', '.');
519     }
520       }
521     }
522
523     return null;
524   }
525
526   /**
527    * Get the name of the class, including the package name.
528    *
529    * @return
530    * The name of the class.
531    */

532   public String JavaDoc name() {
533     Constant c = constants[thisClass];
534     if (c.tag() == Constant.CLASS) {
535       Integer JavaDoc nameIndex = (Integer JavaDoc) c.value();
536       if (nameIndex != null) {
537     c = constants[nameIndex.intValue()];
538     if (c.tag() == Constant.UTF8) {
539       return (String JavaDoc) c.value();
540     }
541       }
542     }
543     
544     throw new ClassFormatError JavaDoc("Couldn't find class name in file");
545   }
546   
547   /**
548    * Read a constant from the constant pool.
549    *
550    * @param in
551    * The stream from which to read.
552    * @return
553    * The constant.
554    * @exception IOException
555    * If an error occurs while reading.
556    */

557   Constant readConstant(DataInputStream in)
558        throws IOException
559   {
560     int tag = in.readUnsignedByte();
561     Object JavaDoc value;
562     
563     switch (tag)
564       {
565       case Constant.CLASS:
566       case Constant.STRING:
567     value = new Integer JavaDoc(in.readUnsignedShort());
568     break;
569       case Constant.FIELD_REF:
570       case Constant.METHOD_REF:
571       case Constant.INTERFACE_METHOD_REF:
572       case Constant.NAME_AND_TYPE:
573     value = new int[2];
574
575     ((int[]) value)[0] = in.readUnsignedShort();
576     ((int[]) value)[1] = in.readUnsignedShort();
577     break;
578       case Constant.INTEGER:
579     value = new Integer JavaDoc(in.readInt());
580     break;
581       case Constant.FLOAT:
582     value = new Float JavaDoc(in.readFloat());
583     break;
584       case Constant.LONG:
585     // Longs take up 2 constant pool entries.
586
value = new Long JavaDoc(in.readLong());
587     break;
588       case Constant.DOUBLE:
589     // Doubles take up 2 constant pool entries.
590
value = new Double JavaDoc(in.readDouble());
591     break;
592       case Constant.UTF8:
593     value = in.readUTF();
594     break;
595       default:
596     throw new ClassFormatError JavaDoc("Invalid constant tag: " + tag);
597       }
598     
599     return new Constant(tag, value);
600   }
601   
602   /**
603    * Read the class file header.
604    *
605    * @param in
606    * The stream from which to read.
607    * @exception IOException
608    * If an error occurs while reading.
609    */

610   void readHeader(DataInputStream in)
611        throws IOException
612   {
613     int magic = in.readInt();
614     
615     if (magic != 0xCAFEBABE) {
616       throw new ClassFormatError JavaDoc("Bad magic number.");
617     }
618     
619     int major = in.readUnsignedShort();
620     int minor = in.readUnsignedShort();
621   }
622   
623   /**
624    * Read the class's constant pool. Constants in the constant pool
625    * are modeled by an array of <tt>reflect.Constant</tt>/
626    *
627    * @param in
628    * The stream from which to read.
629    * @exception IOException
630    * If an error occurs while reading.
631    *
632    * @see Constant
633    * @see #constants
634    */

635   void readConstantPool(DataInputStream in)
636        throws IOException
637   {
638     int count = in.readUnsignedShort();
639     
640     constants = new Constant[count];
641     
642     // The first constant is reserved for internal use by the JVM.
643
constants[0] = null;
644     
645     // Read the constants.
646
for (int i = 1; i < count; i++) {
647       constants[i] = readConstant(in);
648       
649       switch (constants[i].tag()) {
650     case Constant.LONG:
651     case Constant.DOUBLE:
652       // Longs and doubles take up 2 constant pool entries.
653
constants[++i] = null;
654       break;
655       }
656     }
657   }
658   
659   /**
660    * Read the class's access flags.
661    *
662    * @param in
663    * The stream from which to read.
664    * @exception IOException
665    * If an error occurs while reading.
666    */

667   void readAccessFlags(DataInputStream in)
668        throws IOException
669   {
670     modifiers = in.readUnsignedShort();
671   }
672   
673   /**
674    * Read the class's name, superclass, and interfaces.
675    *
676    * @param in
677    * The stream from which to read.
678    * @exception IOException
679    * If an error occurs while reading.
680    */

681   void readClassInfo(DataInputStream in)
682        throws IOException
683   {
684     int index;
685     
686     thisClass = in.readUnsignedShort();
687     superClass = in.readUnsignedShort();
688     
689     int numInterfaces = in.readUnsignedShort();
690     
691     interfaces = new int[numInterfaces];
692     
693     for (int i = 0; i < numInterfaces; i++) {
694       interfaces[i] = in.readUnsignedShort();
695     }
696   }
697   
698   /**
699    * Read the class's fields.
700    *
701    * @param in
702    * The stream from which to read.
703    * @exception IOException
704    * If an error occurs while reading.
705    */

706   void readFields(DataInputStream in)
707        throws IOException
708   {
709     int numFields = in.readUnsignedShort();
710     
711     fields = new Field[numFields];
712     
713     for (int i = 0; i < numFields; i++) {
714       fields[i] = new Field(in, this);
715     }
716   }
717   
718   /**
719    * Read the class's methods.
720    *
721    * @param in
722    * The stream from which to read.
723    * @exception IOException
724    * If an error occurs while reading.
725    */

726   void readMethods(DataInputStream in)
727        throws IOException
728   {
729     int numMethods = in.readUnsignedShort();
730     
731     methods = new Method[numMethods];
732     
733     for (int i = 0; i < numMethods; i++) {
734       methods[i] = new Method(in, this);
735     }
736   }
737   
738   /**
739    * Read the class's attributes. Since none of the attributes
740    * are required, just read the length of each attribute and
741    * skip that many bytes.
742    *
743    * @param in
744    * The stream from which to read.
745    * @exception IOException
746    * If an error occurs while reading.
747    */

748   void readAttributes(DataInputStream in)
749        throws IOException
750   {
751     int numAttributes = in.readUnsignedShort();
752     
753     attrs = new Attribute[numAttributes];
754     
755     for (int i = 0; i < numAttributes; i++) {
756       int nameIndex = in.readUnsignedShort();
757       int length = in.readInt();
758       if ("InnerClasses".equals(constants[nameIndex].value())) {
759           innerClasses = new InnerClasses(in, nameIndex, length);
760           attrs[i] = innerClasses;
761       }
762       else {
763           long n = in.skip(length);
764           if (n != length) {
765               throw new EOFException();
766           }
767       }
768     }
769   }
770 }
771
Popular Tags