View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package org.apache.bcel.classfile;
18  
19  import java.io.BufferedInputStream;
20  import java.io.DataInputStream;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.zip.ZipEntry;
25  import java.util.zip.ZipFile;
26  import org.apache.bcel.Constants;
27  
28  /***
29   * Wrapper class that parses a given Java .class file. The method <A
30   * href ="#parse">parse</A> returns a <A href ="JavaClass.html">
31   * JavaClass</A> object on success. When an I/O error or an
32   * inconsistency occurs an appropiate exception is propagated back to
33   * the caller.
34   *
35   * The structure and the names comply, except for a few conveniences,
36   * exactly with the <A href="ftp://java.sun.com/docs/specs/vmspec.ps">
37   * JVM specification 1.0</a>. See this paper for
38   * further details about the structure of a bytecode file.
39   *
40   * @version $Id: ClassParser.java 386056 2006-03-15 11:31:56Z tcurdt $
41   * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> 
42   */
43  public final class ClassParser {
44  
45      private DataInputStream file;
46      private boolean fileOwned;
47      private String file_name;
48      private String zip_file;
49      private int class_name_index, superclass_name_index;
50      private int major, minor; // Compiler version
51      private int access_flags; // Access rights of parsed class
52      private int[] interfaces; // Names of implemented interfaces
53      private ConstantPool constant_pool; // collection of constants
54      private Field[] fields; // class fields, i.e., its variables
55      private Method[] methods; // methods defined in the class
56      private Attribute[] attributes; // attributes defined in the class
57      private boolean is_zip; // Loaded from zip file
58      private static final int BUFSIZE = 8192;
59  
60  
61      /***
62       * Parse class from the given stream.
63       *
64       * @param file Input stream
65       * @param file_name File name
66       */
67      public ClassParser(InputStream file, String file_name) {
68          this.file_name = file_name;
69          fileOwned = false;
70          String clazz = file.getClass().getName(); // Not a very clean solution ...
71          is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
72          if (file instanceof DataInputStream) {
73              this.file = (DataInputStream) file;
74          } else {
75              this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
76          }
77      }
78  
79  
80      /*** Parse class from given .class file.
81       *
82       * @param file_name file name
83       */
84      public ClassParser(String file_name) throws IOException {
85          is_zip = false;
86          this.file_name = file_name;
87          fileOwned = true;
88      }
89  
90  
91      /*** Parse class from given .class file in a ZIP-archive
92       *
93       * @param zip_file zip file name
94       * @param file_name file name
95       */
96      public ClassParser(String zip_file, String file_name) {
97          is_zip = true;
98          fileOwned = true;
99          this.zip_file = zip_file;
100         this.file_name = file_name;
101     }
102 
103 
104     /***
105      * Parse the given Java class file and return an object that represents
106      * the contained data, i.e., constants, methods, fields and commands.
107      * A <em>ClassFormatException</em> is raised, if the file is not a valid
108      * .class file. (This does not include verification of the byte code as it
109      * is performed by the java interpreter).
110      *
111      * @return Class object representing the parsed class file
112      * @throws  IOException
113      * @throws  ClassFormatException
114      */
115     public JavaClass parse() throws IOException, ClassFormatException {
116         ZipFile zip = null;
117         try {
118             if (fileOwned) {
119                 if (is_zip) {
120                     zip = new ZipFile(zip_file);
121                     ZipEntry entry = zip.getEntry(file_name);
122                     file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
123                             BUFSIZE));
124                 } else {
125                     file = new DataInputStream(new BufferedInputStream(new FileInputStream(
126                             file_name), BUFSIZE));
127                 }
128             }
129             /******************* Read headers ********************************/
130             // Check magic tag of class file
131             readID();
132             // Get compiler version
133             readVersion();
134             /******************* Read constant pool and related **************/
135             // Read constant pool entries
136             readConstantPool();
137             // Get class information
138             readClassInfo();
139             // Get interface information, i.e., implemented interfaces
140             readInterfaces();
141             /******************* Read class fields and methods ***************/
142             // Read class fields, i.e., the variables of the class
143             readFields();
144             // Read class methods, i.e., the functions in the class
145             readMethods();
146             // Read class attributes
147             readAttributes();
148             // Check for unknown variables
149             //Unknown[] u = Unknown.getUnknownAttributes();
150             //for(int i=0; i < u.length; i++)
151             //  System.err.println("WARNING: " + u[i]);
152             // Everything should have been read now
153             //      if(file.available() > 0) {
154             //        int bytes = file.available();
155             //        byte[] buf = new byte[bytes];
156             //        file.read(buf);
157             //        if(!(is_zip && (buf.length == 1))) {
158             //  	System.err.println("WARNING: Trailing garbage at end of " + file_name);
159             //  	System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
160             //        }
161             //      }
162         } finally {
163             // Read everything of interest, so close the file
164             if (fileOwned) {
165                 file.close();
166                 if (zip != null) {
167                     zip.close();
168                 }
169             }
170         }
171         // Return the information we have gathered in a new object
172         return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
173                 access_flags, constant_pool, interfaces, fields, methods, attributes, is_zip
174                         ? JavaClass.ZIP
175                         : JavaClass.FILE);
176     }
177 
178 
179     /***
180      * Read information about the attributes of the class.
181      * @throws  IOException
182      * @throws  ClassFormatException
183      */
184     private final void readAttributes() throws IOException, ClassFormatException {
185         int attributes_count;
186         attributes_count = file.readUnsignedShort();
187         attributes = new Attribute[attributes_count];
188         for (int i = 0; i < attributes_count; i++) {
189             attributes[i] = Attribute.readAttribute(file, constant_pool);
190         }
191     }
192 
193 
194     /***
195      * Read information about the class and its super class.
196      * @throws  IOException
197      * @throws  ClassFormatException
198      */
199     private final void readClassInfo() throws IOException, ClassFormatException {
200         access_flags = file.readUnsignedShort();
201         /* Interfaces are implicitely abstract, the flag should be set
202          * according to the JVM specification.
203          */
204         if ((access_flags & Constants.ACC_INTERFACE) != 0) {
205             access_flags |= Constants.ACC_ABSTRACT;
206         }
207         if (((access_flags & Constants.ACC_ABSTRACT) != 0)
208                 && ((access_flags & Constants.ACC_FINAL) != 0)) {
209             throw new ClassFormatException("Class can't be both final and abstract");
210         }
211         class_name_index = file.readUnsignedShort();
212         superclass_name_index = file.readUnsignedShort();
213     }
214 
215 
216     /***
217      * Read constant pool entries.
218      * @throws  IOException
219      * @throws  ClassFormatException
220      */
221     private final void readConstantPool() throws IOException, ClassFormatException {
222         constant_pool = new ConstantPool(file);
223     }
224 
225 
226     /***
227      * Read information about the fields of the class, i.e., its variables.
228      * @throws  IOException
229      * @throws  ClassFormatException
230      */
231     private final void readFields() throws IOException, ClassFormatException {
232         int fields_count;
233         fields_count = file.readUnsignedShort();
234         fields = new Field[fields_count];
235         for (int i = 0; i < fields_count; i++) {
236             fields[i] = new Field(file, constant_pool);
237         }
238     }
239 
240 
241     /********************* Private utility methods **********************/
242     /***
243      * Check whether the header of the file is ok.
244      * Of course, this has to be the first action on successive file reads.
245      * @throws  IOException
246      * @throws  ClassFormatException
247      */
248     private final void readID() throws IOException, ClassFormatException {
249         int magic = 0xCAFEBABE;
250         if (file.readInt() != magic) {
251             throw new ClassFormatException(file_name + " is not a Java .class file");
252         }
253     }
254 
255 
256     /***
257      * Read information about the interfaces implemented by this class.
258      * @throws  IOException
259      * @throws  ClassFormatException
260      */
261     private final void readInterfaces() throws IOException, ClassFormatException {
262         int interfaces_count;
263         interfaces_count = file.readUnsignedShort();
264         interfaces = new int[interfaces_count];
265         for (int i = 0; i < interfaces_count; i++) {
266             interfaces[i] = file.readUnsignedShort();
267         }
268     }
269 
270 
271     /***
272      * Read information about the methods of the class.
273      * @throws  IOException
274      * @throws  ClassFormatException
275      */
276     private final void readMethods() throws IOException, ClassFormatException {
277         int methods_count;
278         methods_count = file.readUnsignedShort();
279         methods = new Method[methods_count];
280         for (int i = 0; i < methods_count; i++) {
281             methods[i] = new Method(file, constant_pool);
282         }
283     }
284 
285 
286     /***
287      * Read major and minor version of compiler which created the file.
288      * @throws  IOException
289      * @throws  ClassFormatException
290      */
291     private final void readVersion() throws IOException, ClassFormatException {
292         minor = file.readUnsignedShort();
293         major = file.readUnsignedShort();
294     }
295 }