View Javadoc

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