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