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