1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
51 private int access_flags;
52 private int[] interfaces;
53 private ConstantPool constant_pool;
54 private Field[] fields;
55 private Method[] methods;
56 private Attribute[] attributes;
57 private boolean is_zip;
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();
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
131 readID();
132
133 readVersion();
134 /******************* Read constant pool and related **************/
135
136 readConstantPool();
137
138 readClassInfo();
139
140 readInterfaces();
141 /******************* Read class fields and methods ***************/
142
143 readFields();
144
145 readMethods();
146
147 readAttributes();
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 } finally {
163
164 if (fileOwned) {
165 file.close();
166 if (zip != null) {
167 zip.close();
168 }
169 }
170 }
171
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
202
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 }