KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > classfile > engine > ClassParser


1 /*
2  * FindBugs - Find Bugs in Java programs
3  * Copyright (C) 2006, University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.classfile.engine;
21
22 import java.io.DataInputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.TreeSet JavaDoc;
25
26 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
27 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
28 import edu.umd.cs.findbugs.classfile.IClassConstants;
29 import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
30 import edu.umd.cs.findbugs.classfile.InvalidClassFileFormatException;
31 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
32 import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
33 import edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo;
34 import edu.umd.cs.findbugs.util.ClassName;
35
36 /**
37  * Parse a class to extract symbolic information.
38  * see <a HREF=http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html">
39    http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html </a>
40  *
41  * @author David Hovemeyer
42  */

43 public class ClassParser {
44
45     static class Constant {
46         int tag;
47         Object JavaDoc[] data;
48         
49         Constant(int tag, Object JavaDoc[] data) {
50             this.tag = tag;
51             this.data = data;
52         }
53     }
54     
55     private DataInputStream JavaDoc in;
56     private ClassDescriptor expectedClassDescriptor;
57     private ICodeBaseEntry codeBaseEntry;
58     private Constant[] constantPool;
59     
60     /**
61      * Constructor.
62      *
63      * @param in the DataInputStream to read class data from
64      * @param expectedClassDescriptor ClassDescriptor expected: null if unknown
65      * @param codeBaseEntry codebase entry class is loaded from
66      */

67     public ClassParser(
68             DataInputStream JavaDoc in,
69             ClassDescriptor expectedClassDescriptor,
70             ICodeBaseEntry codeBaseEntry) {
71         this.in = in;
72         this.expectedClassDescriptor = expectedClassDescriptor;
73         this.codeBaseEntry = codeBaseEntry;
74     }
75     
76     /**
77      * Parse the class data into a ClassNameAndSuperclassInfo object containing
78      * (some of) the class's symbolic information.
79      *
80      * @param classInfo a ClassNameAndSuperclassInfo object to be filled in with (some of)
81      * the class's symbolic information
82      * @throws InvalidClassFileFormatException
83      */

84     public void parse(ClassNameAndSuperclassInfo classInfo) throws InvalidClassFileFormatException {
85         try {
86             int magic = in.readInt();
87             int major_version = in.readUnsignedShort();
88             int minor_version = in.readUnsignedShort();
89             int constant_pool_count = in.readUnsignedShort();
90
91             constantPool = new Constant[constant_pool_count];
92             for (int i = 1; i < constantPool.length; i++) {
93                 constantPool[i] = readConstant();
94                 if (constantPool[i].tag == IClassConstants.CONSTANT_Double ||
95                         constantPool[i].tag == IClassConstants.CONSTANT_Long) {
96                     // Double and Long constants take up two constant pool entries
97
++i;
98                 }
99             }
100             
101             int access_flags = in.readUnsignedShort();
102
103             int this_class = in.readUnsignedShort();
104             ClassDescriptor thisClassDescriptor =
105                 getClassDescriptor(this_class);
106
107             int super_class = in.readUnsignedShort();
108             ClassDescriptor superClassDescriptor =
109                 getClassDescriptor(super_class);
110
111             int interfaces_count = in.readUnsignedShort();
112             if (interfaces_count < 0) {
113                 throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
114             }
115             ClassDescriptor[] interfaceDescriptorList = new ClassDescriptor[interfaces_count];
116             for (int i = 0; i < interfaceDescriptorList.length; i++) {
117                 interfaceDescriptorList[i] = getClassDescriptor(in.readUnsignedShort());
118             }
119
120             classInfo.setClassDescriptor(thisClassDescriptor);
121             classInfo.setSuperclassDescriptor(superClassDescriptor);
122             classInfo.setInterfaceDescriptorList(interfaceDescriptorList);
123             classInfo.setCodeBaseEntry(codeBaseEntry);
124             classInfo.setAccessFlags(access_flags);
125         } catch (IOException JavaDoc e) {
126             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry, e);
127         }
128     }
129
130     /**
131      * Parse the class data into a ClassInfo object containing
132      * (some of) the class's symbolic information.
133      *
134      * @param classInfo a ClassInfo object to be filled in with (some of)
135      * the class's symbolic information
136      * @throws InvalidClassFileFormatException
137      */

138     public void parse(ClassInfo classInfo) throws InvalidClassFileFormatException {
139         parse((ClassNameAndSuperclassInfo) classInfo);
140
141         try {
142             int fields_count = in.readUnsignedShort();
143             if (fields_count < 0 ) {
144                 throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
145             }
146             FieldDescriptor[] fieldDescriptorList = new FieldDescriptor[fields_count];
147             for (int i = 0; i < fields_count; i++) {
148                 fieldDescriptorList[i] = readField(classInfo.getClassDescriptor());
149             }
150             
151             int methods_count = in.readUnsignedShort();
152             if (methods_count < 0) {
153                 throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
154             }
155             MethodDescriptor[] methodDescriptorList = new MethodDescriptor[methods_count];
156             for (int i = 0; i < methods_count; i++) {
157                 methodDescriptorList[i] = readMethod(classInfo.getClassDescriptor());
158             }
159             
160             // Extract all references to other classes,
161
// both CONSTANT_Class entries and also referenced method
162
// signatures.
163
ClassDescriptor[] referencedClassDescriptorList = extractReferencedClasses();
164             
165             classInfo.setFieldDescriptorList(fieldDescriptorList);
166             classInfo.setMethodDescriptorList(methodDescriptorList);
167             classInfo.setReferencedClassDescriptorList(referencedClassDescriptorList);
168         } catch (IOException JavaDoc e) {
169             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry, e);
170         }
171         
172     }
173
174     /**
175      * Extract references to other classes.
176      *
177      * @return array of ClassDescriptors of referenced classes
178      * @throws InvalidClassFileFormatException
179      */

180     private ClassDescriptor[] extractReferencedClasses() throws InvalidClassFileFormatException {
181         TreeSet JavaDoc<ClassDescriptor> referencedClassSet = new TreeSet JavaDoc<ClassDescriptor>();
182         for (Constant constant : constantPool) {
183             if (constant == null) {
184                 continue;
185             }
186             if (constant.tag == IClassConstants.CONSTANT_Class) {
187                 String JavaDoc className = getUtf8String((Integer JavaDoc)constant.data[0]);
188                 if (className.indexOf('[') >= 0) {
189                     extractReferencedClassesFromSignature(referencedClassSet, className);
190                 } else if (ClassName.isValidClassName(className)) {
191                     referencedClassSet.add(new ClassDescriptor(className));
192                 }
193             } else if (constant.tag == IClassConstants.CONSTANT_Methodref
194                     || constant.tag == IClassConstants.CONSTANT_Fieldref
195                     || constant.tag == IClassConstants.CONSTANT_InterfaceMethodref) {
196                 // Get the target class name
197
String JavaDoc className = getClassName((Integer JavaDoc) constant.data[0]);
198                 extractReferencedClassesFromSignature(referencedClassSet, className);
199                 
200                 // Parse signature to extract class names
201
String JavaDoc signature = getSignatureFromNameAndType((Integer JavaDoc)constant.data[1]);
202                 extractReferencedClassesFromSignature(referencedClassSet, signature);
203             }
204         }
205         ClassDescriptor[] referencedClassDescriptorList =
206             referencedClassSet.toArray(new ClassDescriptor[referencedClassSet.size()]);
207         return referencedClassDescriptorList;
208     }
209
210     /**
211      * @param referencedClassSet
212      * @param signature
213      */

214     private void extractReferencedClassesFromSignature(TreeSet JavaDoc<ClassDescriptor> referencedClassSet, String JavaDoc signature) {
215         while (signature.length() > 0) {
216             int start = signature.indexOf('L');
217             if (start < 0) {
218                 break;
219             }
220             signature = signature.substring(start);
221             int end = signature.indexOf(';');
222             if (end < 0) {
223                 break;
224             }
225             String JavaDoc className = signature.substring(1, end);
226             if (ClassName.isValidClassName(className)) {
227                 referencedClassSet.add(new ClassDescriptor(className));
228             }
229             signature = signature.substring(end + 1);
230         }
231     }
232
233     // 8: UTF-8 string
234
// I: int
235
// F: float
236
// L: long
237
// D: double
238
// i: 2-byte constant pool index
239
private static final String JavaDoc[] CONSTANT_FORMAT_MAP = {
240         null,
241         "8", // 1: CONSTANT_Utf8
242
null,
243         "I", // 3: CONSTANT_Integer
244
"F", // 4: CONSTANT_Float
245
"L", // 5: CONSTANT_Long
246
"D", // 6: CONSTANT_Double
247
"i", // 7: CONSTANT_Class
248
"i", // 8: CONSTANT_String
249
"ii", // 9: CONSTANT_Fieldref
250
"ii", // 10: CONSTANT_Methodref
251
"ii", // 11: CONSTANT_InterfaceMethodref
252
"ii", // 12: CONSTANT_NameAndType
253
};
254
255     /**
256      * Read a constant from the constant pool.
257      *
258      * @return a Constant
259      * @throws InvalidClassFileFormatException
260      * @throws IOException
261      */

262     private Constant readConstant()
263             throws InvalidClassFileFormatException, IOException JavaDoc {
264         int tag = in.readUnsignedByte();
265         if (tag < 0 || tag >= CONSTANT_FORMAT_MAP.length || CONSTANT_FORMAT_MAP[tag] == null) {
266             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
267         }
268         String JavaDoc format = CONSTANT_FORMAT_MAP[tag];
269         Object JavaDoc[] data = new Object JavaDoc[format.length()];
270         for (int i = 0; i < format.length(); i++) {
271             char spec = format.charAt(i);
272             switch (spec) {
273             case '8':
274                 data[i] = in.readUTF();
275                 break;
276             case 'I':
277                 data[i] = (Integer JavaDoc)in.readInt();
278                 break;
279             case 'F':
280                 data[i] = new Float JavaDoc(in.readFloat());
281                 break;
282             case 'L':
283                 data[i] = (Long JavaDoc)in.readLong();
284                 break;
285             case 'D':
286                 data[i] = new Double JavaDoc(in.readDouble());
287                 break;
288             case 'i':
289                 data[i] = (Integer JavaDoc)in.readUnsignedShort();
290                 break;
291             default: throw new IllegalStateException JavaDoc();
292             }
293         }
294         
295         return new Constant(tag, data);
296     }
297
298     /**
299      * Get a class name from a CONSTANT_Class.
300      * Note that this may be an array (e.g., "[Ljava/lang/String;").
301      *
302      * @param index index of the constant
303      * @return the class name
304      * @throws InvalidClassFileFormatException
305      */

306     private String JavaDoc getClassName(int index) throws InvalidClassFileFormatException {
307         if (index == 0) {
308             return null;
309         }
310         
311         checkConstantPoolIndex(index);
312         Constant constant = constantPool[index];
313         checkConstantTag(constant, IClassConstants.CONSTANT_Class);
314         
315         int refIndex = ((Integer JavaDoc)constant.data[0]).intValue();
316         String JavaDoc stringValue = getUtf8String(refIndex);
317
318         return stringValue;
319     }
320     
321     /**
322      * Get the ClassDescriptor of a class referenced in the constant pool.
323      *
324      * @param index index of the referenced class in the constant pool
325      * @return the ClassDescriptor of the referenced calss
326      * @throws InvalidClassFileFormatException
327      */

328     private ClassDescriptor getClassDescriptor(int index) throws InvalidClassFileFormatException {
329         String JavaDoc className = getClassName(index);
330         return className != null ? new ClassDescriptor(className) : null;
331     }
332
333     /**
334      * Get the UTF-8 string constant at given constant pool index.
335      *
336      * @param refIndex the constant pool index
337      * @return the String at that index
338      * @throws InvalidClassFileFormatException
339      */

340     private String JavaDoc getUtf8String(int refIndex) throws InvalidClassFileFormatException {
341         checkConstantPoolIndex(refIndex);
342         Constant refConstant = constantPool[refIndex];
343         checkConstantTag(refConstant, IClassConstants.CONSTANT_Utf8);
344         return (String JavaDoc) refConstant.data[0];
345     }
346
347     /**
348      * Check that a constant pool index is valid.
349      *
350      * @param expectedClassDescriptor class descriptor
351      * @param constantPool the constant pool
352      * @param index the index to check
353      * @throws InvalidClassFileFormatException if the index is not valid
354      */

355     private void checkConstantPoolIndex(int index) throws InvalidClassFileFormatException {
356         if (index < 0 || index >= constantPool.length || constantPool[index] == null) {
357             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
358         }
359     }
360
361     /**
362      * Check that a constant has the expected tag.
363      *
364      * @param constant the constant to check
365      * @param expectedTag the expected constant tag
366      * @throws InvalidClassFileFormatException if the constant's tag does not match the expected tag
367      */

368     private void checkConstantTag(Constant constant, int expectedTag)
369             throws InvalidClassFileFormatException {
370         if (constant.tag != expectedTag) {
371             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
372         }
373     }
374     
375     interface FieldOrMethodDescriptorCreator<E> {
376         public E create(String JavaDoc className, String JavaDoc name, String JavaDoc signature, int accessFlags);
377     }
378
379     /**
380      * Read field_info, return FieldDescriptor.
381      *
382      * @param thisClassDescriptor the ClassDescriptor of this class (being parsed)
383      * @return the FieldDescriptor
384      * @throws IOException
385      * @throws InvalidClassFileFormatException
386      */

387     private FieldDescriptor readField(ClassDescriptor thisClassDescriptor)
388             throws IOException JavaDoc, InvalidClassFileFormatException {
389         return readFieldOrMethod(thisClassDescriptor, new FieldOrMethodDescriptorCreator<FieldDescriptor>() {
390             /* (non-Javadoc)
391              * @see edu.umd.cs.findbugs.classfile.engine.ClassParser.FieldOrMethodDescriptorCreator#create(java.lang.String, java.lang.String, java.lang.String, int)
392              */

393             public FieldDescriptor create(String JavaDoc className, String JavaDoc name, String JavaDoc signature, int accessFlags) {
394                 return new FieldDescriptor(className, name, signature, (accessFlags & IClassConstants.ACC_STATIC) != 0);
395             }
396         });
397     }
398
399     /**
400      * Read method_info, read method descriptor.
401      *
402      * @param thisClassDescriptor
403      * @return
404      * @throws IOException
405      * @throws InvalidClassFileFormatException
406      */

407     private MethodDescriptor readMethod(ClassDescriptor thisClassDescriptor)
408             throws InvalidClassFileFormatException, IOException JavaDoc {
409         return readFieldOrMethod(thisClassDescriptor, new FieldOrMethodDescriptorCreator<MethodDescriptor>(){
410             /* (non-Javadoc)
411              * @see edu.umd.cs.findbugs.classfile.engine.ClassParser.FieldOrMethodDescriptorCreator#create(java.lang.String, java.lang.String, java.lang.String, int)
412              */

413             public MethodDescriptor create(String JavaDoc className, String JavaDoc name, String JavaDoc signature, int accessFlags) {
414                 return new MethodDescriptor(className, name, signature, (accessFlags & IClassConstants.ACC_STATIC) != 0);
415             }
416         });
417     }
418
419     /**
420      * Read field_info or method_info.
421      * They have the same format.
422      *
423      * @param <E> descriptor type to return
424      * @param thisClassDescriptor class descriptor of class being parsed
425      * @param creator callback to create the FieldDescriptor or MethodDescriptor
426      * @return the parsed descriptor
427      * @throws IOException
428      * @throws InvalidClassFileFormatException
429      */

430     private<E> E readFieldOrMethod(
431             ClassDescriptor thisClassDescriptor, FieldOrMethodDescriptorCreator<E> creator)
432             throws IOException JavaDoc, InvalidClassFileFormatException {
433         int access_flags = in.readUnsignedShort();
434         int name_index = in.readUnsignedShort();
435         int descriptor_index = in.readUnsignedShort();
436         int attributes_count = in.readUnsignedShort();
437     
438         String JavaDoc name = getUtf8String(name_index);
439         String JavaDoc signature = getUtf8String(descriptor_index);
440         if (attributes_count < 0) {
441             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
442         }
443         for (int i = 0; i < attributes_count; i++) {
444             readAttribute();
445         }
446
447         return creator.create(
448                 thisClassDescriptor.getClassName(), name, signature, access_flags);
449     }
450
451     /**
452      * Read an attribute.
453      *
454      * @throws IOException
455      * @throws InvalidClassFileFormatException
456      */

457     private void readAttribute() throws IOException JavaDoc, InvalidClassFileFormatException {
458         int attribute_name_index = in.readUnsignedShort();
459         int attribute_length = in.readInt();
460         if (attribute_length < 0) {
461             throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
462         }
463         byte[] buf = new byte[attribute_length];
464         in.readFully(buf);
465     }
466
467     /**
468      * Get the signature from a CONSTANT_NameAndType.
469      *
470      * @param index the index of the CONSTANT_NameAndType
471      * @return the signature
472      * @throws InvalidClassFileFormatException
473      */

474     private String JavaDoc getSignatureFromNameAndType(int index) throws InvalidClassFileFormatException {
475         checkConstantPoolIndex(index);
476         Constant constant = constantPool[index];
477         checkConstantTag(constant, IClassConstants.CONSTANT_NameAndType);
478         return getUtf8String((Integer JavaDoc) constant.data[1]);
479     }
480 }
481
Popular Tags