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 25 import com.sun.org.apache.bcel.internal.Constants; 26 import java.io.*; 27 import java.util.zip.*; 28 29 /** 30 * Wrapper class that parses a given Java .class file. The method <A 31 * href ="#parse">parse</A> returns a <A href ="JavaClass.html"> 32 * JavaClass</A> object on success. When an I/O error or an 33 * inconsistency occurs an appropiate exception is propagated back to 34 * the caller. 35 * 36 * The structure and the names comply, except for a few conveniences, 37 * exactly with the <A href="ftp://java.sun.com/docs/specs/vmspec.ps"> 38 * JVM specification 1.0</a>. See this paper for 39 * further details about the structure of a bytecode file. 40 * 41 * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> 42 */ 43 public final class ClassParser { 44 private DataInputStream file; 45 private ZipFile zip; 46 private String file_name; 47 private int class_name_index, superclass_name_index; 48 private int major, minor; // Compiler version 49 private int access_flags; // Access rights of parsed class 50 private int[] interfaces; // Names of implemented interfaces 51 private ConstantPool constant_pool; // collection of constants 52 private Field[] fields; // class fields, i.e., its variables 53 private Method[] methods; // methods defined in the class 54 private Attribute[] attributes; // attributes defined in the class 55 private boolean is_zip; // Loaded from zip file 56 57 private static final int BUFSIZE = 8192; 58 59 /** 60 * Parse class from the given stream. 61 * 62 * @param file Input stream 63 * @param file_name File name 64 */ 65 public ClassParser(InputStream file, String file_name) { 66 this.file_name = file_name; 67 68 String clazz = file.getClass().getName(); // Not a very clean solution ... 69 is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar."); 70 71 if(file instanceof DataInputStream) // Is already a data stream 72 this.file = (DataInputStream)file; 73 else 74 this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE)); 75 } 76 77 /** Parse class from given .class file. 78 * 79 * @param file_name file name 80 * @throws IOException 81 */ 82 public ClassParser(String file_name) throws IOException 83 { 84 is_zip = false; 85 this.file_name = file_name; 86 file = new DataInputStream(new BufferedInputStream 87 (new FileInputStream(file_name), BUFSIZE)); 88 } 89 90 /** Parse class from given .class file in a ZIP-archive 91 * 92 * @param file_name file name 93 * @throws IOException 94 */ 95 public ClassParser(String zip_file, String file_name) throws IOException 96 { 97 is_zip = true; 98 zip = new ZipFile(zip_file); 99 ZipEntry entry = zip.getEntry(file_name); 100 101 this.file_name = file_name; 102 103 file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry), 104 BUFSIZE)); 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 */ 118 public JavaClass parse() throws IOException, ClassFormatException 119 { 120 /****************** Read headers ********************************/ 121 // Check magic tag of class file 122 readID(); 123 124 // Get compiler version 125 readVersion(); 126 127 /****************** Read constant pool and related **************/ 128 // Read constant pool entries 129 readConstantPool(); 130 131 // Get class information 132 readClassInfo(); 133 134 // Get interface information, i.e., implemented interfaces 135 readInterfaces(); 136 137 /****************** Read class fields and methods ***************/ 138 // Read class fields, i.e., the variables of the class 139 readFields(); 140 141 // Read class methods, i.e., the functions in the class 142 readMethods(); 143 144 // Read class attributes 145 readAttributes(); 146 147 // Check for unknown variables 148 //Unknown[] u = Unknown.getUnknownAttributes(); 149 //for(int i=0; i < u.length; i++) 150 // System.err.println("WARNING: " + u[i]); 151 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 158 // if(!(is_zip && (buf.length == 1))) { 159 // System.err.println("WARNING: Trailing garbage at end of " + file_name); 160 // System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf)); 161 // } 162 // } 163 164 // Read everything of interest, so close the file 165 file.close(); 166 if(zip != null) 167 zip.close(); 168 169 // Return the information we have gathered in a new object 170 return new JavaClass(class_name_index, superclass_name_index, 171 file_name, major, minor, access_flags, 172 constant_pool, interfaces, fields, 173 methods, attributes, is_zip? JavaClass.ZIP : JavaClass.FILE); 174 } 175 176 /** 177 * Read information about the attributes of the class. 178 * @throws IOException 179 * @throws ClassFormatException 180 */ 181 private final void readAttributes() throws IOException, ClassFormatException 182 { 183 int attributes_count; 184 185 attributes_count = file.readUnsignedShort(); 186 attributes = new Attribute[attributes_count]; 187 188 for(int i=0; i < attributes_count; i++) 189 attributes[i] = Attribute.readAttribute(file, constant_pool); 190 } 191 192 /** 193 * Read information about the class and its super class. 194 * @throws IOException 195 * @throws ClassFormatException 196 */ 197 private final void readClassInfo() throws IOException, ClassFormatException 198 { 199 access_flags = file.readUnsignedShort(); 200 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 * Read constant pool entries. 216 * @throws IOException 217 * @throws ClassFormatException 218 */ 219 private final void readConstantPool() throws IOException, ClassFormatException 220 { 221 constant_pool = new ConstantPool(file); 222 } 223 224 /** 225 * Read information about the fields of the class, i.e., its variables. 226 * @throws IOException 227 * @throws ClassFormatException 228 */ 229 private final void readFields() throws IOException, ClassFormatException 230 { 231 int fields_count; 232 233 fields_count = file.readUnsignedShort(); 234 fields = new Field[fields_count]; 235 236 for(int i=0; i < fields_count; i++) 237 fields[i] = new Field(file, constant_pool); 238 } 239 240 /******************** Private utility methods **********************/ 241 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 { 250 int magic = 0xCAFEBABE; 251 252 if(file.readInt() != magic) 253 throw new ClassFormatException(file_name + " is not a Java .class file"); 254 } 255 /** 256 * Read information about the interfaces implemented by this class. 257 * @throws IOException 258 * @throws ClassFormatException 259 */ 260 private final void readInterfaces() throws IOException, ClassFormatException 261 { 262 int interfaces_count; 263 264 interfaces_count = file.readUnsignedShort(); 265 interfaces = new int[interfaces_count]; 266 267 for(int i=0; i < interfaces_count; i++) 268 interfaces[i] = file.readUnsignedShort(); 269 } 270 /** 271 * Read information about the methods of the class. 272 * @throws IOException 273 * @throws ClassFormatException 274 */ 275 private final void readMethods() throws IOException, ClassFormatException 276 { 277 int methods_count; 278 279 methods_count = file.readUnsignedShort(); 280 methods = new Method[methods_count]; 281 282 for(int i=0; i < methods_count; i++) 283 methods[i] = new Method(file, constant_pool); 284 } 285 /** 286 * Read major and minor version of compiler which created the file. 287 * @throws IOException 288 * @throws ClassFormatException 289 */ 290 private final void readVersion() throws IOException, ClassFormatException 291 { 292 minor = file.readUnsignedShort(); 293 major = file.readUnsignedShort(); 294 } 295 }