KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > alt > jiapi > file > ClassFile


1 package alt.jiapi.file;
2
3 import java.io.ByteArrayInputStream JavaDoc;
4 import java.io.ByteArrayOutputStream JavaDoc;
5 import java.io.DataInputStream JavaDoc;
6 import java.io.DataOutputStream JavaDoc;
7 import java.io.FileInputStream JavaDoc;
8 import java.io.InputStream JavaDoc;
9 import java.io.IOException JavaDoc;
10 import java.io.EOFException JavaDoc;
11
12 import java.util.Iterator JavaDoc;
13 import java.util.LinkedList JavaDoc;
14 import java.util.List JavaDoc;
15
16 /**
17  * ClassFile is a low level representation of Java class file.
18  *
19  * @author Mika Riekkinen
20  */

21 public class ClassFile {
22     private static Configuration config = new Configuration();
23
24     private ConstantPool constantPool;
25     private List JavaDoc interfaces = new LinkedList JavaDoc();
26     private List JavaDoc fields = new LinkedList JavaDoc();
27     private List JavaDoc methods = new LinkedList JavaDoc();
28     private List JavaDoc attributes = new LinkedList JavaDoc();
29
30     // Values of the ACCESS_FLAGS
31
/**
32      * Public access.
33      */

34     public static final int ACC_PUBLIC = 0x0001;
35     /**
36      * Final.
37      */

38     public static final int ACC_FINAL = 0x0010;
39     /**
40      * Super bit.
41      */

42     public static final int ACC_SUPER = 0x0020;
43     /**
44      * Class is an interface
45      */

46     public static final int ACC_INTERFACE = 0x0200;
47     /**
48      * Class is abstract
49      */

50     public static final int ACC_ABSTRACT = 0x0400;
51
52     // u1 ~ byte
53
// u2 ~ short
54
// u4 ~ int
55
private int magic_number;
56     private short minor_version;
57     private short major_version;
58     private short access_flags;
59     private short this_class;
60     private short super_class;
61
62     /**
63      * Used for testing purposes.
64      *
65      * @param args args[0] is a path to java class file
66      */

67     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
68         ClassFile cf = ClassFile.parse(args[0]);
69
70         System.out.println(cf.getMagicNumber());
71         System.out.println(cf.getMajorVersion() + " " + cf.getMinorVersion());
72         System.out.println(cf.getAccessFlags());
73
74         System.out.println(cf.getConstantPool());
75     }
76
77     /**
78      * Constructor to build ClassFile from scratch.
79      *
80      * @param className fully qualified name of the class.
81      */

82     public ClassFile(String JavaDoc className) {
83         String JavaDoc name = className.replace('.', '/');
84         this.magic_number = 0xcafebabe;
85         this.minor_version = 0; // jdk1.2 minor_version
86
this.major_version = 46; // jdk1.2 major_version
87
this.access_flags = ACC_PUBLIC;
88
89         this.constantPool = new ConstantPool();
90         this.this_class = constantPool.addClassInfo(name).getEntryIndex();
91         this.super_class = constantPool.addClassInfo("java.lang.Object").getEntryIndex();
92
93         this.interfaces = new LinkedList JavaDoc();
94         this.methods = new LinkedList JavaDoc();
95         this.fields = new LinkedList JavaDoc();
96         this.attributes = new LinkedList JavaDoc();
97     }
98
99     /**
100      * Constructor used, when parsing ClassFile from InputStream
101      */

102     private ClassFile() {
103     }
104
105
106     /**
107      * Parse given file and create an instance of ClassFile from it.
108      *
109      * @param fileName name of the file, that is read.
110      * @return an instance of ClassFile, that conforms to java virtual
111      * machine specification classfile format.
112      * @exception ParseException is thrown, if classfile parser cannot
113      * understand parsed stream.
114      * @exception IOException is thrown, if there was problems in reading the
115      * stream.
116      */

117     public static ClassFile parse(String JavaDoc fileName) throws ParseException, IOException JavaDoc {
118         return parse(new FileInputStream JavaDoc(fileName));
119     }
120
121
122     /**
123      * Parse InputStream and create an instance of ClassFile from stream.
124      *
125      * @param is InputStream
126      * @return an instance of ClassFile, that conforms to java virtual
127      * machine specification classfile format.
128      * @exception ParseException is thrown, if classfile parser cannot
129      * understand parsed stream.
130      * @exception IOException is thrown, if there was problems in reading the
131      * stream.
132      */

133     public static ClassFile parse(InputStream JavaDoc is) throws ParseException, IOException JavaDoc {
134         InputStream JavaDoc input = null;
135
136         // Workaround for bug in ZipFileInputStream:
137
// We could read input stream to byte buffer,
138
// and create DataInputStream that points to this
139
// byte buffer.
140

141         if (config.getBoolean("alt.jiapi.file.use-ZipFileInputStream-bug-workaround")) { // ZipFileInputStream fix
142
//System.out.println(">> using workaround");
143
ByteArrayOutputStream JavaDoc bos = new
144                 ByteArrayOutputStream JavaDoc(is.available());
145
146             int i = 0;
147             while((i = is.read()) != -1) {
148                 bos.write(i);
149             }
150
151             input = new ByteArrayInputStream JavaDoc(bos.toByteArray());
152         }
153         else {
154             input = is;
155         }
156
157
158
159         DataInputStream JavaDoc dis = new DataInputStream JavaDoc(input);
160         ClassFile cf = null;
161         try {
162             cf = new ClassFile();
163             cf.parseClassFile(dis);
164
165             if (dis.available() != 0) {
166                 System.out.println(is.available() + ":::" + dis.available() + ":" + is);
167                 System.out.println("" + dis.readByte());
168                 if (false) {
169                     // This os most likely a bug in ZipFileInputStream.
170
// It reports some bytes left in available(),
171
// but reading one byte results in EOFException
172
throw new ParseException("Could not fully read class file. " + dis.available() + " bytes remain to be read", cf);
173                 }
174             }
175         }
176         catch(EOFException JavaDoc eof) {
177 // eof.printStackTrace();
178
System.out.println(">>Got EOFException: " + eof + ","+is.available()+
179                                ", " + cf.getClassName());
180         }
181         catch(IOException JavaDoc ioe) {
182             System.out.println("Got IOException: " + ioe + "," +is.available()+
183                                ", " + cf.getClassName());
184             throw new ParseException(ioe.getMessage(), cf);
185         }
186         finally {
187             dis.close();
188         }
189
190         return cf;
191     }
192
193
194     /**
195      * Gets all the class level attributes of this ClassFile.
196      *
197      * @return a List of Attributes
198      */

199     public List JavaDoc getAttributes() {
200         return attributes;
201     }
202
203
204     /**
205      * Gets the constant pool of this ClassFile.
206      *
207      * @return ConstantPool
208      */

209     public ConstantPool getConstantPool() {
210         return constantPool;
211     }
212
213
214     /**
215      * Adds a new interface, that class represented by this ClassFile
216      * implements.
217      *
218      * @param fully qualified name of the interface to implement
219      */

220     public void addInterface(String JavaDoc name) {
221         String JavaDoc iType = name.replace('.', '/');
222         short nameIndex = constantPool.addUtf8Info(iType).getEntryIndex();
223
224         // Add Class_info
225
short cInfo = constantPool.addClassInfo(nameIndex);
226
227         interfaces.add(new Interface(constantPool, cInfo));
228     }
229
230
231
232     /**
233      * Gets the magic number
234      *
235      * @return magic number
236      */

237     public int getMagicNumber() {
238         return magic_number; // u4
239
}
240
241     /**
242      * Gets the minor version of the class file
243      *
244      * @return Minor version
245      */

246     public short getMinorVersion() {
247     return minor_version; // u2
248
}
249
250     /**
251      * Gets the major version of the class file
252      *
253      * @return Major version
254      */

255     public short getMajorVersion() {
256         return major_version; // u2
257
}
258
259     /**
260      * Gets all the Fields of this class file
261      *
262      * @return a List of Fields
263      */

264     public List JavaDoc getFields() {
265         return fields;
266     }
267
268     /**
269      * Gets all the Interfaces of this class file
270      *
271      * @return a List of Interfaces
272      */

273     public List JavaDoc getInterfaces() {
274         return interfaces;
275     }
276
277     /**
278      * Gets all the Methods of this class file
279      *
280      * @return a List of Methods
281      */

282     public List JavaDoc getMethods() {
283         return methods;
284     }
285
286
287     /**
288      * Gets the name of the class represented by this ClassFile.
289      *
290      * @return Name of the class
291      */

292     public String JavaDoc getClassName() {
293         return constantPool.getClassName(this_class);
294     }
295
296     /**
297      * Gets the name of the superclass of the class represented by this
298      * ClassFile.
299      *
300      * @return Name of the superclass, or null if this ClassFile represents
301      * java.lang.Object
302      */

303     public String JavaDoc getSuperclassName() {
304         if (super_class != 0) {
305             return constantPool.getClassName(super_class);
306         }
307
308         return null;
309     }
310
311
312     /**
313      * Gets the access_flags of this class file
314      *
315      * @return access flags.
316      * @see #ACC_PUBLIC
317      * @see #ACC_FINAL
318      * @see #ACC_SUPER
319      * @see #ACC_INTERFACE
320      * @see #ACC_ABSTRACT
321      */

322     public short getAccessFlags() {
323         return access_flags; // u2
324
}
325
326     public void setAccessFlags(short access_flags) {
327         this.access_flags = access_flags;
328     }
329
330
331     /**
332      * Gets the index in constant-pool, that holds a class-info
333      * for super class of this class. This method returns 0,
334      * if this ClassFile represents java.lang.Object.
335      * Note, that indexing in constant-pool starts from 1.
336      *
337      * @return index into constant pool
338      */

339     public short getSuperClassIndex() {
340         return super_class;
341     }
342
343     /**
344      * Gets the index in constant-pool, that holds a class-info
345      * of this class.
346      *
347      * @return index into constant pool
348      */

349     public short getThisClassIndex() {
350         return this_class;
351     }
352
353
354     /**
355      * Converts this ClassFile into bytes.
356      * No checking against the class file format specification
357      * is made.
358      *
359      * @return bytes representing a Java class.
360      */

361     public byte[] toBytes() {
362         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
363         DataOutputStream JavaDoc dos = new DataOutputStream JavaDoc(baos);
364         try {
365             dos.writeInt(magic_number);
366             dos.writeShort(minor_version);
367             dos.writeShort(major_version);
368
369             writeConstantPool(dos);
370
371             dos.writeShort(access_flags);
372             dos.writeShort(this_class);
373             dos.writeShort(super_class);
374
375             writeInterfaces(dos);
376             writeFields(dos);
377             writeMethods(dos);
378
379             writeAttributes(dos, attributes);
380         }
381         catch(IOException JavaDoc ioe) {
382             throw new ParseException(ioe.getMessage(), this); // Should not happen
383
}
384
385         return baos.toByteArray();
386     }
387
388
389     private void parseClassFile(DataInputStream JavaDoc dis) throws ParseException, IOException JavaDoc {
390         this.magic_number = dis.readInt();
391         this.minor_version = dis.readShort();//dis.readUnsignedShort();
392
this.major_version = dis.readShort();//dis.readUnsignedShort();
393

394 // if (major_version != 46) {
395
// throw new ParseException("reading of classfile version " +
396
// major_version + "." + minor_version +
397
// " is not supported", this);
398
// }
399

400         parseConstantPool(dis);
401
402         this.access_flags = dis.readShort();//dis.readUnsignedShort();
403

404         this.this_class = dis.readShort();
405         this.super_class = dis.readShort();
406
407 // System.out.println(constantPool);
408
// System.out.println(this_class);
409
// System.out.println(super_class);
410

411         readInterfaces(dis);
412         readFields(dis);
413         readMethods(dis);
414
415         this.attributes = readAttributes(dis);
416     }
417
418
419     void addInterface(short constantClassIndex) {
420         interfaces.add(new Interface(constantPool, constantClassIndex));
421     }
422
423     void addField(short access_flags, short name_index,
424                          short descriptor_index, List JavaDoc attributes) {
425         fields.add(new Field(constantPool, access_flags, name_index,
426                              descriptor_index, attributes));
427     }
428
429     void addMethod(short access_flags, short name_index,
430                    short descriptor_index, List JavaDoc attributes) {
431         methods.add(new Method(constantPool, access_flags, name_index,
432                                descriptor_index, attributes));
433     }
434
435
436     private void readInterfaces(DataInputStream JavaDoc dis) throws IOException JavaDoc {
437         short iCount = dis.readShort();
438         Interface[] interfaces = new Interface[iCount];
439
440         // Each record is CONSTANT_Class_Info
441
for (int i = 0; i < iCount; i++) {
442             short constantClassIndex = dis.readShort();
443             addInterface(constantClassIndex);
444         }
445     }
446
447     private void readFields(DataInputStream JavaDoc dis) throws IOException JavaDoc {
448         short fCount = dis.readShort();
449         Field[] fields = new Field[fCount];
450
451         for (int i = 0; i < fCount; i++) {
452             short access_flags = dis.readShort();
453             short name_index = dis.readShort();
454             short descriptor_index = dis.readShort();
455
456             List JavaDoc attributes = readAttributes(dis);
457
458             addField(access_flags, name_index, descriptor_index, attributes);
459         }
460     }
461
462
463     private void readMethods(DataInputStream JavaDoc dis) throws IOException JavaDoc {
464         short mCount = dis.readShort();
465         Method[] methods = new Method[mCount];
466
467         for (int i = 0; i < mCount; i++) {
468             short access_flags = dis.readShort();
469             short name_index = dis.readShort();
470             short descriptor_index = dis.readShort();
471
472             List JavaDoc attributes = readAttributes(dis);
473
474             addMethod(access_flags, name_index, descriptor_index, attributes);
475         }
476     }
477
478
479     /**
480      * First 'short' read is expected to contain attribute count.
481      */

482     List JavaDoc readAttributes(DataInputStream JavaDoc dis) throws IOException JavaDoc {
483         short attr_count = dis.readShort();
484         LinkedList JavaDoc attrs = new LinkedList JavaDoc();
485
486         // attribute_info
487
for (int i = 0; i < attr_count; i++) {
488             short attribute_name_index = dis.readShort();
489             int attribute_length = dis.readInt();
490
491             String JavaDoc attrName = getUtf8(attribute_name_index);
492             Attribute a = null;
493             
494             if ("Code".equals(attrName)) {
495                 a = new CodeAttribute(constantPool, attribute_name_index, dis);
496             }
497             else if ("Exceptions".equals(attrName)) {
498                 a = new ExceptionsAttribute(attribute_name_index, dis);
499             }
500             else if ("ConstantValue".equals(attrName)) {
501                 a = new ConstantValueAttribute(attribute_name_index, dis);
502             }
503             else if ("InnerClasses".equals(attrName)) {
504                 a = new InnerClassesAttribute(attribute_name_index, dis);
505             }
506             else if ("Synthetic".equals(attrName)) {
507                 a = new SyntheticAttribute(attribute_name_index, dis);
508             }
509             else if ("SourceFile".equals(attrName)) {
510                 a = new SourceFileAttribute(attribute_name_index, dis);
511             }
512             else if ("Deprecated".equals(attrName)) {
513                 a = new DeprecatedAttribute(attribute_name_index, dis);
514             }
515             else {
516                 // For used defined attributes, or for unknown attributes
517
a = new Attribute(attribute_name_index, attribute_length, dis);
518             }
519
520             a.setConstantPool(constantPool);
521
522             attrs.add(a);
523         }
524
525         return attrs;
526     }
527
528
529
530     private void parseConstantPool(DataInputStream JavaDoc dis) throws IOException JavaDoc {
531         short constantPoolCount = dis.readShort();
532         constantPool = new ConstantPool(constantPoolCount - 1);
533
534         for (int i = 0; i < constantPoolCount - 1; i++) {
535             byte tag = dis.readByte();
536
537             switch(tag) {
538             case ConstantPool.CONSTANT_Class:
539                 constantPool.addClassInfo(dis.readShort());
540                 break;
541             case ConstantPool.CONSTANT_Fieldref:
542                 constantPool.addFieldRefInfo(dis.readShort(), dis.readShort());
543                 break;
544             case ConstantPool.CONSTANT_Methodref:
545                 constantPool.addMethodRefInfo(dis.readShort(),dis.readShort());
546                 break;
547             case ConstantPool.CONSTANT_InterfaceMethodref:
548                 constantPool.addInterfaceMethodRefInfo(dis.readShort(),
549                                                        dis.readShort());
550                 break;
551             case ConstantPool.CONSTANT_String:
552                 constantPool.addString_info(dis.readShort());
553                 break;
554             case ConstantPool.CONSTANT_Integer:
555                 constantPool.addInteger_info(dis.readInt());
556                 break;
557             case ConstantPool.CONSTANT_Float:
558                 constantPool.addFloat_info(dis.readInt());
559                 break;
560             case ConstantPool.CONSTANT_Long:
561                 constantPool.addLong_info(dis.readInt(), dis.readInt());
562                 i++; // long_info takes two entries
563
break;
564             case ConstantPool.CONSTANT_Double:
565                 constantPool.addDouble_info(dis.readInt(), dis.readInt());
566                 i++; // double_info takes two entries
567
break;
568             case ConstantPool.CONSTANT_NameAndType:
569 // short s1 = dis.readShort();
570
// short s2 = dis.readShort();
571
// System.out.println("nti: " + constantPool.get(s1) + ", " +
572
// constantPool.get(s2 - 1));
573
// constantPool.addNameAndTypeInfo(s1, (short)(s2 - 1));
574

575                  constantPool.addNameAndTypeInfo(dis.readShort(),
576                                                  dis.readShort());
577                 break;
578             case ConstantPool.CONSTANT_Utf8:
579                 short length = dis.readShort();
580                 byte[] byteArray = new byte[length];
581                 for(int j = 0; j < byteArray.length; j++) {
582                     byteArray[j] = dis.readByte();
583                     if (byteArray[j] == 0 || byteArray[j] >= 0xf0) {
584                         // @see VMSpec, 4.4.7
585
System.out.println(" " + Integer.toHexString(byteArray[j]));
586                     }
587                 }
588
589                 constantPool.addUtf8_info(byteArray);
590                 break;
591             default:
592                 throw new ParseException(constantPool +
593                                          "\nInvalid constant pool tag: " +tag,
594                                          this);
595             }
596         }
597     }
598
599
600     private void writeConstantPool(DataOutputStream JavaDoc dos) throws IOException JavaDoc {
601         List JavaDoc cp = constantPool.getList();
602
603         dos.writeShort(cp.size() + 1); // Size of Constant pool
604

605         Iterator JavaDoc i = cp.iterator();
606         while(i.hasNext()) {
607             ConstantPool.Entry e = (ConstantPool.Entry)i.next();
608
609             if (e instanceof ConstantPool.NullEntry) {
610                 continue;
611             }
612
613             if (e.getTag() == 0) {
614                 System.out.println("ERROR: invalid constant pool tag: 0");
615             }
616
617             dos.writeByte(e.getTag());
618             e.writeData(dos);
619         }
620     }
621
622     private void writeInterfaces(DataOutputStream JavaDoc dos) throws IOException JavaDoc {
623         dos.writeShort(interfaces.size());
624
625         Iterator JavaDoc i = interfaces.iterator();
626         while(i.hasNext()) {
627             Interface iFace = (Interface)i.next();
628             //case CONSTANT_Class:
629
dos.writeShort(iFace.getConstantClassIndex());
630         }
631     }
632
633     private void writeFields(DataOutputStream JavaDoc dos) throws IOException JavaDoc {
634         dos.writeShort(fields.size()); // Field Count
635

636         Iterator JavaDoc i = fields.iterator();
637         while(i.hasNext()) {
638             Field f = (Field)i.next();
639             //case CONSTANT_Class:
640
dos.writeShort(f.getAccessFlags());
641             dos.writeShort(f.getNameIndex());
642             dos.writeShort(f.getDescriptorIndex());
643
644             writeAttributes(dos, f.getAttributes());
645         }
646     }
647
648     private void writeMethods(DataOutputStream JavaDoc dos) throws IOException JavaDoc {
649         dos.writeShort(methods.size()); // method Count
650

651         Iterator JavaDoc i = methods.iterator();
652         while(i.hasNext()) {
653             Method m = (Method)i.next();
654             //case CONSTANT_Class:
655
dos.writeShort(m.getAccessFlags());
656             dos.writeShort(m.getNameIndex());
657             dos.writeShort(m.getDescriptorIndex());
658
659             writeAttributes(dos, m.getAttributes());
660         }
661     }
662
663     private void writeAttributes(DataOutputStream JavaDoc dos, List JavaDoc attrs) throws IOException JavaDoc {
664         dos.writeShort(attrs.size());
665
666         Iterator JavaDoc i = attrs.iterator();
667         while(i.hasNext()) {
668             Attribute a = (Attribute)i.next();
669
670             dos.writeShort(a.getAttributeNameIndex());
671
672             byte[] info = a.getBytes();
673             
674             dos.writeInt(info.length);
675             for (int j = 0; j < info.length; j++) {
676                 dos.writeByte(info[j]);
677             }
678         }
679     }
680
681
682     private String JavaDoc getUtf8(short index) {
683         ConstantPool.Utf8Info utf8 =
684             (ConstantPool.Utf8Info)constantPool.get(index);
685
686         String JavaDoc s = new String JavaDoc(utf8.getBytes());
687
688         return s;
689     }
690 }
691
Popular Tags