KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > jclasslib > structures > ClassFile


1 /*
2     This library is free software; you can redistribute it and/or
3     modify it under the terms of the GNU General Public
4     License as published by the Free Software Foundation; either
5     version 2 of the license, or (at your option) any later version.
6 */

7
8 package org.gjt.jclasslib.structures;
9
10 import org.gjt.jclasslib.io.Log;
11 import org.gjt.jclasslib.structures.constants.ConstantLargeNumeric;
12 import org.gjt.jclasslib.structures.constants.ConstantUtf8Info;
13
14 import java.io.*;
15 import java.util.HashMap JavaDoc;
16
17 /**
18  * The class file structure in which all other structures are hooked up.
19  *
20  * @author <a HREF="mailto:jclasslib@ej-technologies.com">Ingo Kegel</a>, <a HREF="mailto:vitor.carreira@gmail.com">Vitor Carreira</a>
21  * @version $Revision: 1.10 $ $Date: 2004/12/28 13:04:32 $
22  */

23 public class ClassFile extends AbstractStructureWithAttributes {
24
25     /**
26      * Set this JVM System property to true to skip reading of constant pool
27      * entries. This is not advisable, since most sunsequent operations on the
28      * class file structure will fail.
29      */

30     public static final String JavaDoc SYSTEM_PROPERTY_SKIP_CONSTANT_POOL = "jclasslib.io.skipConstantPool";
31
32     private static final int MAGIC_NUMBER = 0xcafebabe;
33
34     private final boolean skipConstantPool;
35
36     private int minorVersion;
37     private int majorVersion;
38     private CPInfo[] constantPool;
39     private HashMap JavaDoc constantPoolEntryToIndex = new HashMap JavaDoc();
40     private int accessFlags;
41     private int thisClass;
42     private int superClass;
43     private int[] interfaces;
44     private FieldInfo[] fields;
45     private MethodInfo[] methods;
46
47
48     /**
49      * Constructor.
50      */

51     public ClassFile() {
52         skipConstantPool = Boolean.getBoolean(SYSTEM_PROPERTY_SKIP_CONSTANT_POOL);
53         setClassFile(this);
54     }
55
56     /**
57      * Get the minor version of the class file format.
58      *
59      * @return the minor version
60      */

61     public int getMinorVersion() {
62         return minorVersion;
63     }
64
65     /**
66      * Set the minor version of the class file format.
67      *
68      * @param minorVersion the minor version
69      */

70     public void setMinorVersion(int minorVersion) {
71         this.minorVersion = minorVersion;
72     }
73
74     /**
75      * Get the major version of the class file format.
76      *
77      * @return the major version
78      */

79     public int getMajorVersion() {
80         return majorVersion;
81     }
82
83     /**
84      * Set the major version of the class file format.
85      *
86      * @param majorVersion the major version
87      */

88     public void setMajorVersion(int majorVersion) {
89         this.majorVersion = majorVersion;
90     }
91
92     /**
93      * Get the array with all constant pool entries.
94      *
95      * @return the array
96      */

97     public CPInfo[] getConstantPool() {
98         return constantPool;
99     }
100
101     /**
102      * Get the index of an equivalent constant pool entry.
103      *
104      * @param cpInfo the constant pool entry
105      * @return the index, -1 if no equivalent constant pool entry can be found
106      */

107     public int getConstantPoolIndex(CPInfo cpInfo) {
108         Integer JavaDoc index = (Integer JavaDoc)constantPoolEntryToIndex.get(cpInfo);
109         if (index != null) {
110             return index.intValue();
111         } else {
112             return -1;
113         }
114     }
115
116     /**
117      * Set the array with all constant pool entries. An internal hash map
118      * will need to be recalulated. If you add to the end of the constant
119      * pool, use <tt>enlargeConstantPool</tt>.
120      *
121      * @param constantPool the array
122      */

123     public void setConstantPool(CPInfo[] constantPool) {
124         this.constantPool = constantPool;
125         for (int i = 0; i < constantPool.length; i++) {
126             constantPoolEntryToIndex.put(constantPool[i], new Integer JavaDoc(i));
127         }
128     }
129
130     /**
131      * Set the array with all constant pool entries where the new array
132      * of constant pool entries starts with the old constant pool. If
133      * you delete entries, use <tt>setConstantPool</tt>.
134      *
135      * @param enlargedConstantPool the array
136      */

137     public void enlargeConstantPool(CPInfo[] enlargedConstantPool) {
138         int startIndex = constantPool == null ? 0 : constantPool.length;
139         this.constantPool = enlargedConstantPool;
140         for (int i = startIndex; i < constantPool.length; i++) {
141             if (constantPool[i] != null) {
142                 constantPoolEntryToIndex.put(constantPool[i], new Integer JavaDoc(i));
143             }
144         }
145     }
146
147     /**
148      * Register the constant pool entry at a given index, so that it can
149      * be found through the <tt>getConstantPoolIndex</tt> method.
150      *
151      * @param index the index
152      */

153     public void registerConstantPoolEntry(int index) {
154         constantPoolEntryToIndex.put(constantPool[index], new Integer JavaDoc(index));
155     }
156
157     /**
158      * Unregister the constant pool entry at a given index, so that it can
159      * no longer be found through the <tt>getConstantPoolIndex</tt> method.
160      *
161      * @param index the index
162      */

163     public void unregisterConstantPoolEntry(int index) {
164         constantPoolEntryToIndex.remove(constantPool[index]);
165     }
166
167     /**
168      * Get the access flags of this class.
169      *
170      * @return the access flags
171      */

172     public int getAccessFlags() {
173         return accessFlags;
174     }
175
176     /**
177      * Set the access flags of this class.
178      *
179      * @param accessFlags the access flags
180      */

181     public void setAccessFlags(int accessFlags) {
182         this.accessFlags = accessFlags;
183     }
184
185     /**
186      * Get the constant pool index of this class.
187      *
188      * @return the index
189      */

190     public int getThisClass() {
191         return thisClass;
192     }
193
194     /**
195      * Set the constant pool index of this class.
196      *
197      * @param thisClass the index
198      */

199     public void setThisClass(int thisClass) {
200         this.thisClass = thisClass;
201     }
202
203     /**
204      * Get the name of this class.
205      *
206      * @return the name
207      * @throws InvalidByteCodeException
208      */

209     public String JavaDoc getThisClassName() throws InvalidByteCodeException {
210         return getConstantPoolEntryName(getThisClass());
211     }
212
213     /**
214      * Get the constant pool index of the super class of this class.
215      *
216      * @return the index
217      */

218     public int getSuperClass() {
219         return superClass;
220     }
221
222     /**
223      * Set the constant pool index of the super class of this class.
224      *
225      * @param superClass the index
226      */

227     public void setSuperClass(int superClass) {
228         this.superClass = superClass;
229     }
230
231     /**
232      * Get the name of the super class.
233      *
234      * @return the name
235      * @throws InvalidByteCodeException
236      */

237     public String JavaDoc getSuperClassName() throws InvalidByteCodeException {
238         return getConstantPoolEntryName(getSuperClass());
239     }
240
241     /**
242      * Get the array with the constant pool entries of all interfaces.
243      *
244      * @return the array
245      */

246     public int[] getInterfaces() {
247         return interfaces;
248     }
249
250     /**
251      * Set the array with the constant pool entries of all interfaces.
252      *
253      * @param interfaces the array
254      */

255     public void setInterfaces(int[] interfaces) {
256         this.interfaces = interfaces;
257     }
258
259     /**
260      * Get the array with the <tt>FieldInfo</tt> structures for the fields of this class.
261      *
262      * @return the array
263      */

264     public FieldInfo[] getFields() {
265         return fields;
266     }
267
268     /**
269      * Set the array with the <tt>FieldInfo</tt> structures for the fields of this class.
270      *
271      * @param fields the array
272      */

273     public void setFields(FieldInfo[] fields) {
274         this.fields = fields;
275     }
276
277     /**
278      * Get the array with the <tt>MethodInfo</tt> structures for the methods of this class.
279      *
280      * @return the array
281      */

282     public MethodInfo[] getMethods() {
283         return methods;
284     }
285
286     /**
287      * Set the array with the <tt>MethodInfo</tt> structures for the methods of this class.
288      *
289      * @param methods the array
290      */

291     public void setMethods(MethodInfo[] methods) {
292         this.methods = methods;
293     }
294
295     /**
296      * Get the the access flags of this class as a hex string.
297      *
298      * @return the hex string
299      */

300     public String JavaDoc getFormattedAccessFlags() {
301         return printAccessFlags(accessFlags);
302     }
303
304     /**
305      * Get the verbose description of the access flags of this class.
306      *
307      * @return the description
308      */

309     public String JavaDoc getAccessFlagsVerbose() {
310         return printAccessFlagsVerbose(accessFlags);
311     }
312
313     /**
314      * Get the <tt>ConstantUtf8Info</tt> constant pool entry at the specified index.
315      *
316      * @param index the index
317      * @return the constant pool entry
318      * @throws InvalidByteCodeException if the entry is not a <tt>ConstantUtf8Info</tt>
319      */

320     public ConstantUtf8Info getConstantPoolUtf8Entry(int index)
321             throws InvalidByteCodeException {
322
323         return (ConstantUtf8Info)getConstantPoolEntry(index, ConstantUtf8Info.class);
324     }
325
326     /**
327      * Get the constant pool entry at the specified index and cast it to a specified class.
328      *
329      * @param index the index
330      * @param entryClass the required subtype of <tt>CPInfo</tt>
331      * @return the constant pool entry
332      * @throws InvalidByteCodeException if the entry is of a different class than expected
333      */

334     public CPInfo getConstantPoolEntry(int index, Class JavaDoc entryClass)
335             throws InvalidByteCodeException {
336
337         if (!checkValidConstantPoolIndex(index)) {
338             return null;
339         }
340
341         CPInfo cpInfo = constantPool[index];
342
343         if (cpInfo == null) {
344             return null;
345         }
346
347         if (entryClass.isAssignableFrom(cpInfo.getClass())) {
348             return cpInfo;
349         } else {
350             throw new InvalidByteCodeException("constant pool entry at " + index +
351                     " is not assignable to " +
352                     entryClass.getName());
353         }
354     }
355
356     /**
357      * Get an approximate verbose description of the content of the constant pool entry
358      * at the specified index.
359      *
360      * @param index the index
361      * @return the description
362      * @throws InvalidByteCodeException if the entry is invalid
363      */

364     public String JavaDoc getConstantPoolEntryName(int index)
365             throws InvalidByteCodeException {
366
367         if (!checkValidConstantPoolIndex(index)) {
368             return null;
369         }
370
371         CPInfo cpInfo = constantPool[index];
372         if (cpInfo == null) {
373             return "invalid constant pool index";
374         } else {
375             return cpInfo.getVerbose();
376         }
377     }
378
379     /**
380      * Get the index of a field for given field name and signature.
381      *
382      * @param name the field name.
383      * @param descriptor the signature.
384      * @return the index or <tt>-1</tt> if not found.
385      * @throws InvalidByteCodeException
386      */

387     public int getFieldIndex(String JavaDoc name, String JavaDoc descriptor) throws InvalidByteCodeException {
388
389         for (int i = 0; i < fields.length; i++) {
390             FieldInfo field = fields[i];
391             if (field.getName().equals(name) && field.getDescriptor().equals(descriptor)) {
392                 return i;
393             }
394         }
395         return -1;
396     }
397
398     /**
399      * Get the <tt>FieldInfo</tt> for given field name and signature.
400      *
401      * @param name the field name.
402      * @param descriptor the signature.
403      * @return the <tt>FieldInfo</tt> or <tt>null</tt> if not found.
404      * @throws InvalidByteCodeException
405      */

406     public FieldInfo getField(String JavaDoc name, String JavaDoc descriptor) throws InvalidByteCodeException {
407
408         int index = getFieldIndex(name, descriptor);
409         if (index < 0) {
410             return null;
411         } else {
412             return fields[index];
413         }
414     }
415
416     /**
417      * Get the index of a method for given method name and signature.
418      *
419      * @param name the method name.
420      * @param descriptor the signature.
421      * @return the index or <tt>-1</tt> if not found.
422      * @throws InvalidByteCodeException
423      */

424     public int getMethodIndex(String JavaDoc name, String JavaDoc descriptor) throws InvalidByteCodeException {
425
426         for (int i = 0; i < methods.length; i++) {
427             MethodInfo method = methods[i];
428             if (method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {
429                 return i;
430             }
431         }
432         return -1;
433     }
434
435     /**
436      * Get the <tt>MethodInfo</tt> for given method name and signature.
437      *
438      * @param name the method name.
439      * @param descriptor the signature.
440      * @return the <tt>MethodInfo</tt> or <tt>null</tt> if not found.
441      * @throws InvalidByteCodeException
442      */

443     public MethodInfo getMethod(String JavaDoc name, String JavaDoc descriptor) throws InvalidByteCodeException {
444
445         int index = getMethodIndex(name, descriptor);
446         if (index < 0) {
447             return null;
448         } else {
449             return methods[index];
450         }
451     }
452
453     public void read(DataInput in)
454             throws InvalidByteCodeException, IOException {
455
456         readMagicNumber(in);
457         readVersion(in);
458         readConstantPool(in);
459         readAccessFlags(in);
460         readThisClass(in);
461         readSuperClass(in);
462         readInterfaces(in);
463         readFields(in);
464         readMethods(in);
465         readAttributes(in);
466     }
467
468     public void write(DataOutput in)
469             throws InvalidByteCodeException, IOException {
470
471         writeMagicNumber(in);
472         writeVersion(in);
473         writeConstantPool(in);
474         writeAccessFlags(in);
475         writeThisClass(in);
476         writeSuperClass(in);
477         writeInterfaces(in);
478         writeFields(in);
479         writeMethods(in);
480         writeAttributes(in);
481
482     }
483
484     private boolean checkValidConstantPoolIndex(int index) {
485
486         if (index < 1 || index >= constantPool.length) {
487             return false;
488         }
489         return true;
490
491     }
492
493     private void readMagicNumber(DataInput in)
494             throws InvalidByteCodeException, IOException {
495
496         int magicNumber = in.readInt();
497         if (magicNumber != MAGIC_NUMBER) {
498             throw new InvalidByteCodeException("Invalid magic number 0x" +
499                     Integer.toHexString(magicNumber) +
500                     " instead of 0x" +
501                     Integer.toHexString(MAGIC_NUMBER));
502         }
503
504         if (debug) debug("read magic number");
505     }
506
507     private void writeMagicNumber(DataOutput out) throws IOException {
508
509         out.writeInt(MAGIC_NUMBER);
510         if (debug) debug("wrote magic number");
511     }
512
513     private void readVersion(DataInput in) throws IOException {
514
515         minorVersion = in.readUnsignedShort();
516         if (debug) debug("read minor version " + minorVersion);
517
518         majorVersion = in.readUnsignedShort();
519         if (debug) debug("read major version " + majorVersion);
520
521         checkMajorVersion(majorVersion);
522     }
523
524     private void writeVersion(DataOutput out) throws IOException {
525
526         out.writeShort(minorVersion);
527         if (debug) debug("wrote minor version " + minorVersion);
528
529         out.writeShort(majorVersion);
530         if (debug) debug("wrote major version " + majorVersion);
531
532         checkMajorVersion(majorVersion);
533     }
534
535     private void readConstantPool(DataInput in)
536             throws InvalidByteCodeException, IOException {
537
538         constantPoolEntryToIndex.clear();
539         int constantPoolCount = in.readUnsignedShort();
540         if (debug) debug("read constant pool count " + constantPoolCount);
541
542         constantPool = new CPInfo[constantPoolCount];
543
544         // constantPool has effective length constantPoolCount - 1
545
// constantPool[0] defaults to null
546
for (int i = 1; i < constantPoolCount; i++) {
547             if (skipConstantPool) {
548                 // see below for i++
549
i += CPInfo.skip(in);
550             } else {
551                 // create CPInfos via factory method since the actual type
552
// of the constant is not yet known
553
if (debug) debug("reading constant pool entry " + i);
554                 constantPool[i] = CPInfo.create(in, this);
555                 constantPoolEntryToIndex.put(constantPool[i], new Integer JavaDoc(i));
556                 if (constantPool[i] instanceof ConstantLargeNumeric) {
557                     // CONSTANT_Double_info and CONSTANT_Long_info take 2 constant
558
// pool entries, the second entry is unusable (design mistake)
559
i++;
560                 }
561             }
562         }
563     }
564
565     private void writeConstantPool(DataOutput out)
566             throws InvalidByteCodeException, IOException {
567
568         int lastFreeIndex;
569         for (lastFreeIndex = getLength(constantPool) - 1;
570              lastFreeIndex >= 0 && constantPool[lastFreeIndex] == null;
571              lastFreeIndex--) {
572         }
573
574         out.writeShort(lastFreeIndex + 1);
575         if (debug) debug("wrote constant pool count " + (lastFreeIndex + 1));
576
577         // constantPool[0] defaults to null and is not written into the class file
578
for (int i = 1; i <= lastFreeIndex; i++) {
579             if (constantPool[i] == null) {
580                 throw new InvalidByteCodeException("constant pool entry " + i + " is null");
581             }
582             if (debug) debug("writing constant pool entry " + i);
583             constantPool[i].write(out);
584             if (constantPool[i] instanceof ConstantLargeNumeric) {
585                 // CONSTANT_Double_info and CONSTANT_Long_info take 2 constant
586
// pool entries, the second entry is unusable (design mistake)
587
i++;
588             }
589         }
590     }
591
592     private void readAccessFlags(DataInput in) throws IOException {
593
594         accessFlags = in.readUnsignedShort();
595         if (debug) debug("read access flags " + printAccessFlags(accessFlags));
596     }
597
598     private void writeAccessFlags(DataOutput out) throws IOException {
599
600         out.writeShort(accessFlags);
601         if (debug) debug("wrote access flags " + printAccessFlags(accessFlags));
602     }
603
604     private void readThisClass(DataInput in) throws IOException {
605
606         thisClass = in.readUnsignedShort();
607         if (debug) debug("read this_class index " + thisClass);
608     }
609
610     private void writeThisClass(DataOutput out) throws IOException {
611
612         out.writeShort(thisClass);
613         if (debug) debug("wrote this_class index " + thisClass);
614     }
615
616     private void readSuperClass(DataInput in) throws IOException {
617
618         superClass = in.readUnsignedShort();
619         if (debug) debug("read super_class index " + superClass);
620     }
621
622     private void writeSuperClass(DataOutput out) throws IOException {
623
624         out.writeShort(superClass);
625         if (debug) debug("wrote super_class index " + superClass);
626     }
627
628     private void readInterfaces(DataInput in) throws IOException {
629
630         int interfacesCount = in.readUnsignedShort();
631         if (debug) debug("read interfaces count " + interfacesCount);
632
633         interfaces = new int[interfacesCount];
634
635         for (int i = 0; i < interfacesCount; i++) {
636             interfaces[i] = in.readUnsignedShort();
637             if (debug) debug("read interface index " + interfaces[i]);
638         }
639
640     }
641
642     private void writeInterfaces(DataOutput out) throws IOException {
643
644         int interfacesCount = getLength(interfaces);
645
646         out.writeShort(interfacesCount);
647         if (debug) debug("wrote interfaces count " + interfacesCount);
648
649         for (int i = 0; i < interfacesCount; i++) {
650             out.writeShort(interfaces[i]);
651             if (debug) debug("wrote interface index " + interfaces[i]);
652         }
653
654     }
655
656     private void readFields(DataInput in)
657             throws InvalidByteCodeException, IOException {
658
659         int fieldsCount = in.readUnsignedShort();
660         if (debug) debug("read fields count " + fieldsCount);
661
662         fields = new FieldInfo[fieldsCount];
663
664         for (int i = 0; i < fieldsCount; i++) {
665             fields[i] = FieldInfo.create(in, this);
666         }
667
668     }
669
670     private void writeFields(DataOutput out)
671             throws InvalidByteCodeException, IOException {
672
673         int fieldsCount = getLength(fields);
674
675         out.writeShort(fieldsCount);
676         if (debug) debug("wrote fields count " + fieldsCount);
677
678         for (int i = 0; i < fieldsCount; i++) {
679             if (fields[i] == null) {
680                 throw new InvalidByteCodeException("field " + i + " is null");
681             }
682             fields[i].write(out);
683         }
684
685     }
686
687     private void readMethods(DataInput in)
688             throws InvalidByteCodeException, IOException {
689
690         int methodsCount = in.readUnsignedShort();
691         if (debug) debug("read methods count " + methodsCount);
692
693         methods = new MethodInfo[methodsCount];
694
695         for (int i = 0; i < methodsCount; i++) {
696             methods[i] = MethodInfo.create(in, this);
697         }
698
699     }
700
701     private void writeMethods(DataOutput out)
702             throws InvalidByteCodeException, IOException {
703
704         int methodsCount = getLength(methods);
705
706         out.writeShort(methodsCount);
707         if (debug) debug("wrote methods count " + methodsCount);
708
709         for (int i = 0; i < methodsCount; i++) {
710             if (methods[i] == null) {
711                 throw new InvalidByteCodeException("method " + i + " is null");
712             }
713             methods[i].write(out);
714         }
715
716     }
717
718     protected void readAttributes(DataInput in)
719             throws InvalidByteCodeException, IOException {
720
721         super.readAttributes(in);
722         if (debug) debug("read " + getLength(attributes) + " attributes for the ClassFile structure");
723     }
724
725     protected void writeAttributes(DataOutput out)
726             throws InvalidByteCodeException, IOException {
727
728         super.writeAttributes(out);
729         if (debug) debug("wrote " + getLength(attributes) + " attributes for the ClassFile structure");
730     }
731
732     private void checkMajorVersion(int majorVersion) {
733
734         if (majorVersion < 45 || majorVersion > 49) {
735             Log.warning("major version should be between 45 and 49 for JDK <= 1.5");
736         }
737
738     }
739
740     protected String JavaDoc printAccessFlagsVerbose(int accessFlags) {
741         return printAccessFlagsVerbose(AccessFlags.CLASS_ACCESS_FLAGS, AccessFlags.CLASS_ACCESS_FLAGS_VERBOSE, accessFlags);
742     }
743 }
744
Popular Tags