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 }