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 file_name;
  52     private String zip_file;
  53     private int class_name_index;
  54     private int superclass_name_index;
  55     private int major; // Compiler version
  56     private int minor; // Compiler version
  57     private int access_flags; // Access rights of parsed class
  58     private int[] interfaces; // Names of implemented interfaces
  59     private ConstantPool constant_pool; // 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 is_zip; // 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 file_name File name
  72      */
  73     public ClassParser(final InputStream inputStream, final String file_name) {
  74         this.file_name = file_name;
  75         fileOwned = false;
  76         final String clazz = inputStream.getClass().getName(); // Not a very clean solution ...
  77         is_zip = 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 file_name file name
  89      */
  90     public ClassParser(final String file_name) {
  91         is_zip = false;
  92         this.file_name = file_name;
  93         fileOwned = true;
  94     }
  95 
  96 
  97     /** Parses class from given .class file in a ZIP-archive
  98      *
  99      * @param zip_file zip file name
 100      * @param file_name file name
 101      */
 102     public ClassParser(final String zip_file, final String file_name) {
 103         is_zip = true;
 104         fileOwned = true;
 105         this.zip_file = zip_file;
 106         this.file_name = file_name;
 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      */
 121     public JavaClass parse() throws IOException, ClassFormatException {
 122         ZipFile zip = null;
 123         try {
 124             if (fileOwned) {
 125                 if (is_zip) {
 126                     zip = new ZipFile(zip_file);
 127                     final ZipEntry entry = zip.getEntry(file_name);
 128 
 129                     if (entry == null) {
 130                         throw new IOException("File " + file_name + " 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                             file_name), 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(!(is_zip && (buf.length == 1))) {
 169             //      System.err.println("WARNING: Trailing garbage at end of " + file_name);
 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(class_name_index, superclass_name_index, file_name, major, minor,
 194                 access_flags, constant_pool, interfaces, fields, methods, attributes, is_zip
 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      */
 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, constant_pool);
 210         }
 211     }
 212 
 213 
 214     /**
 215      * Reads information about the class and its super class.
 216      * @throws  IOException
 217      * @throws  ClassFormatException
 218      */
 219     private void readClassInfo() throws IOException, ClassFormatException {
 220         access_flags = dataInputStream.readUnsignedShort();
 221         /* Interfaces are implicitely abstract, the flag should be set
 222          * according to the JVM specification.
 223          */
 224         if ((access_flags & Const.ACC_INTERFACE) != 0) {
 225             access_flags |= Const.ACC_ABSTRACT;
 226         }
 227         if (((access_flags & Const.ACC_ABSTRACT) != 0)
 228                 && ((access_flags & Const.ACC_FINAL) != 0)) {
 229             throw new ClassFormatException("Class " + file_name + " can't be both final and abstract");
 230         }
 231         class_name_index = dataInputStream.readUnsignedShort();
 232         superclass_name_index = dataInputStream.readUnsignedShort();
 233     }
 234 
 235 
 236     /**
 237      * Reads constant pool entries.
 238      * @throws  IOException
 239      * @throws  ClassFormatException
 240      */
 241     private void readConstantPool() throws IOException, ClassFormatException {
 242         constant_pool = 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      */
 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, constant_pool);
 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      */
 267     private void readID() throws IOException, ClassFormatException {
 268         if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) {
 269             throw new ClassFormatException(file_name + " 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      */
 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      */
 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, constant_pool);
 298         }
 299     }
 300 
 301 
 302     /**
 303      * Reads major and minor version of compiler which created the file.
 304      * @throws  IOException
 305      * @throws  ClassFormatException
 306      */
 307     private void readVersion() throws IOException, ClassFormatException {
 308         minor = dataInputStream.readUnsignedShort();
 309         major = dataInputStream.readUnsignedShort();
 310     }
 311 }