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