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