Source code: org/apache/bcel/classfile/ClassParser.java
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 }