001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018package org.apache.bcel.classfile; 019 020import java.io.BufferedInputStream; 021import java.io.DataInputStream; 022import java.io.FileInputStream; 023import java.io.IOException; 024import java.io.InputStream; 025import java.util.zip.ZipEntry; 026import java.util.zip.ZipFile; 027 028import org.apache.bcel.Const; 029 030/** 031 * Wrapper class that parses a given Java .class file. The method <A 032 * href ="#parse">parse</A> returns a <A href ="JavaClass.html"> 033 * JavaClass</A> object on success. When an I/O error or an 034 * inconsistency occurs an appropiate exception is propagated back to 035 * the caller. 036 * 037 * The structure and the names comply, except for a few conveniences, 038 * exactly with the <A href="http://docs.oracle.com/javase/specs/"> 039 * JVM specification 1.0</a>. See this paper for 040 * further details about the structure of a bytecode file. 041 * 042 */ 043public final class ClassParser { 044 045 private DataInputStream dataInputStream; 046 private final boolean fileOwned; 047 private final String file_name; 048 private String zip_file; 049 private int class_name_index; 050 private int superclass_name_index; 051 private int major; // Compiler version 052 private int minor; // Compiler version 053 private int access_flags; // Access rights of parsed class 054 private int[] interfaces; // Names of implemented interfaces 055 private ConstantPool constant_pool; // collection of constants 056 private Field[] fields; // class fields, i.e., its variables 057 private Method[] methods; // methods defined in the class 058 private Attribute[] attributes; // attributes defined in the class 059 private final boolean is_zip; // Loaded from zip file 060 private static final int BUFSIZE = 8192; 061 062 063 /** 064 * Parses class from the given stream. 065 * 066 * @param inputStream Input stream 067 * @param file_name File name 068 */ 069 public ClassParser(final InputStream inputStream, final String file_name) { 070 this.file_name = file_name; 071 fileOwned = false; 072 final String clazz = inputStream.getClass().getName(); // Not a very clean solution ... 073 is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar."); 074 if (inputStream instanceof DataInputStream) { 075 this.dataInputStream = (DataInputStream) inputStream; 076 } else { 077 this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE)); 078 } 079 } 080 081 082 /** Parses class from given .class file. 083 * 084 * @param file_name file name 085 */ 086 public ClassParser(final String file_name) { 087 is_zip = false; 088 this.file_name = file_name; 089 fileOwned = true; 090 } 091 092 093 /** Parses class from given .class file in a ZIP-archive 094 * 095 * @param zip_file zip file name 096 * @param file_name file name 097 */ 098 public ClassParser(final String zip_file, final String file_name) { 099 is_zip = true; 100 fileOwned = true; 101 this.zip_file = zip_file; 102 this.file_name = file_name; 103 } 104 105 106 /** 107 * Parses the given Java class file and return an object that represents 108 * the contained data, i.e., constants, methods, fields and commands. 109 * A <em>ClassFormatException</em> is raised, if the file is not a valid 110 * .class file. (This does not include verification of the byte code as it 111 * is performed by the java interpreter). 112 * 113 * @return Class object representing the parsed class file 114 * @throws IOException 115 * @throws ClassFormatException 116 */ 117 public JavaClass parse() throws IOException, ClassFormatException { 118 ZipFile zip = null; 119 try { 120 if (fileOwned) { 121 if (is_zip) { 122 zip = new ZipFile(zip_file); 123 final ZipEntry entry = zip.getEntry(file_name); 124 125 if (entry == null) { 126 throw new IOException("File " + file_name + " not found"); 127 } 128 129 dataInputStream = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry), 130 BUFSIZE)); 131 } else { 132 dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream( 133 file_name), BUFSIZE)); 134 } 135 } 136 /****************** Read headers ********************************/ 137 // Check magic tag of class file 138 readID(); 139 // Get compiler version 140 readVersion(); 141 /****************** Read constant pool and related **************/ 142 // Read constant pool entries 143 readConstantPool(); 144 // Get class information 145 readClassInfo(); 146 // Get interface information, i.e., implemented interfaces 147 readInterfaces(); 148 /****************** Read class fields and methods ***************/ 149 // Read class fields, i.e., the variables of the class 150 readFields(); 151 // Read class methods, i.e., the functions in the class 152 readMethods(); 153 // Read class attributes 154 readAttributes(); 155 // Check for unknown variables 156 //Unknown[] u = Unknown.getUnknownAttributes(); 157 //for (int i=0; i < u.length; i++) 158 // System.err.println("WARNING: " + u[i]); 159 // Everything should have been read now 160 // if(file.available() > 0) { 161 // int bytes = file.available(); 162 // byte[] buf = new byte[bytes]; 163 // file.read(buf); 164 // if(!(is_zip && (buf.length == 1))) { 165 // System.err.println("WARNING: Trailing garbage at end of " + file_name); 166 // System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf)); 167 // } 168 // } 169 } finally { 170 // Read everything of interest, so close the file 171 if (fileOwned) { 172 try { 173 if (dataInputStream != null) { 174 dataInputStream.close(); 175 } 176 } catch (final IOException ioe) { 177 //ignore close exceptions 178 } 179 } 180 try { 181 if (zip != null) { 182 zip.close(); 183 } 184 } catch (final IOException ioe) { 185 //ignore close exceptions 186 } 187 } 188 // Return the information we have gathered in a new object 189 return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor, 190 access_flags, constant_pool, interfaces, fields, methods, attributes, is_zip 191 ? JavaClass.ZIP 192 : JavaClass.FILE); 193 } 194 195 196 /** 197 * Reads information about the attributes of the class. 198 * @throws IOException 199 * @throws ClassFormatException 200 */ 201 private void readAttributes() throws IOException, ClassFormatException { 202 final int attributes_count = dataInputStream.readUnsignedShort(); 203 attributes = new Attribute[attributes_count]; 204 for (int i = 0; i < attributes_count; i++) { 205 attributes[i] = Attribute.readAttribute(dataInputStream, constant_pool); 206 } 207 } 208 209 210 /** 211 * Reads information about the class and its super class. 212 * @throws IOException 213 * @throws ClassFormatException 214 */ 215 private void readClassInfo() throws IOException, ClassFormatException { 216 access_flags = dataInputStream.readUnsignedShort(); 217 /* Interfaces are implicitely abstract, the flag should be set 218 * according to the JVM specification. 219 */ 220 if ((access_flags & Const.ACC_INTERFACE) != 0) { 221 access_flags |= Const.ACC_ABSTRACT; 222 } 223 if (((access_flags & Const.ACC_ABSTRACT) != 0) 224 && ((access_flags & Const.ACC_FINAL) != 0)) { 225 throw new ClassFormatException("Class " + file_name + " can't be both final and abstract"); 226 } 227 class_name_index = dataInputStream.readUnsignedShort(); 228 superclass_name_index = dataInputStream.readUnsignedShort(); 229 } 230 231 232 /** 233 * Reads constant pool entries. 234 * @throws IOException 235 * @throws ClassFormatException 236 */ 237 private void readConstantPool() throws IOException, ClassFormatException { 238 constant_pool = new ConstantPool(dataInputStream); 239 } 240 241 242 /** 243 * Reads information about the fields of the class, i.e., its variables. 244 * @throws IOException 245 * @throws ClassFormatException 246 */ 247 private void readFields() throws IOException, ClassFormatException { 248 final int fields_count = dataInputStream.readUnsignedShort(); 249 fields = new Field[fields_count]; 250 for (int i = 0; i < fields_count; i++) { 251 fields[i] = new Field(dataInputStream, constant_pool); 252 } 253 } 254 255 256 /******************** Private utility methods **********************/ 257 /** 258 * Checks whether the header of the file is ok. 259 * Of course, this has to be the first action on successive file reads. 260 * @throws IOException 261 * @throws ClassFormatException 262 */ 263 private void readID() throws IOException, ClassFormatException { 264 if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) { 265 throw new ClassFormatException(file_name + " is not a Java .class file"); 266 } 267 } 268 269 270 /** 271 * Reads information about the interfaces implemented by this class. 272 * @throws IOException 273 * @throws ClassFormatException 274 */ 275 private void readInterfaces() throws IOException, ClassFormatException { 276 final int interfaces_count = dataInputStream.readUnsignedShort(); 277 interfaces = new int[interfaces_count]; 278 for (int i = 0; i < interfaces_count; i++) { 279 interfaces[i] = dataInputStream.readUnsignedShort(); 280 } 281 } 282 283 284 /** 285 * Reads information about the methods of the class. 286 * @throws IOException 287 * @throws ClassFormatException 288 */ 289 private void readMethods() throws IOException, ClassFormatException { 290 final int methods_count = dataInputStream.readUnsignedShort(); 291 methods = new Method[methods_count]; 292 for (int i = 0; i < methods_count; i++) { 293 methods[i] = new Method(dataInputStream, constant_pool); 294 } 295 } 296 297 298 /** 299 * Reads major and minor version of compiler which created the file. 300 * @throws IOException 301 * @throws ClassFormatException 302 */ 303 private void readVersion() throws IOException, ClassFormatException { 304 minor = dataInputStream.readUnsignedShort(); 305 major = dataInputStream.readUnsignedShort(); 306 } 307}