Main Page   Packages   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

ClassParser.java

00001 package de.fub.bytecode.classfile;
00002 
00003 import  de.fub.bytecode.Constants;
00004 import  java.io.*;
00005 import  java.util.zip.*;
00006 
00007 /**
00008  * Wrapper class that parses a given Java .class file. The method
00009  * <A href ="#parse">parse</A> returns a
00010  * <A href ="de.fub.bytecode.classfile.JavaClass.html">
00011  * JavaClass</A> object on success. When an I/O error or an
00012  * inconsistency occurs an appropiate exception is propagated back
00013  * to the caller.
00014  *
00015  * The structure and the names comply, except for a few conveniences,
00016  * exactly with the <A href="ftp://java.sun.com/docs/specs/vmspec.ps">
00017  * JVM specification 1.0</a>. See this paper for
00018  * further details about the structure of a bytecode file.
00019  *
00020  * @version $Id: ClassParser.java,v 1.1.1.1 2002/01/24 03:44:00 pserver Exp $
00021  * @author  <A HREF="http://www.inf.fu-berlin.de/~dahm">M. Dahm</A>
00022  */
00023 public final class ClassParser implements Constants {
00024   private DataInputStream file;
00025   private ZipFile         zip;
00026   private String          file_name;
00027   private int             class_name_index, superclass_name_index;
00028   private int             major, minor; // Compiler version
00029   private int             access_flags; // Access rights of parsed class
00030   private int[]           interfaces; // Names of implemented interfaces
00031   private ConstantPool    constant_pool; // collection of constants
00032   private Field[]         fields; // class fields, i.e. its variables
00033   private Method[]        methods; // methods defined in the class
00034   private Attribute[]     attributes; // attributes defined in the class
00035 
00036   private static final int BUFSIZE = 8192;
00037 
00038   /**
00039    * Parse class from the given stream.
00040    *
00041    * @param file Input stream
00042    * @param file_name File name
00043    */
00044   public ClassParser(InputStream file, String file_name) {
00045     this.file_name = file_name;
00046 
00047     if(file instanceof DataInputStream) // Is already a data stream
00048       this.file = (DataInputStream)file;
00049     else
00050       this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
00051   }  
00052   /** Parse class from given .class file.
00053    *
00054    * @param file_name file name
00055    * @throw IOException
00056    */
00057   public ClassParser(String file_name) throws IOException
00058   {    
00059     this.file_name = file_name;
00060     file = new DataInputStream(new BufferedInputStream
00061                    (new FileInputStream(file_name), BUFSIZE));
00062   }  
00063   /** Parse class from given .class file in a ZIP-archive
00064    *
00065    * @param file_name file name
00066    * @throw IOException
00067    */
00068   public ClassParser(String zip_file, String file_name) throws IOException
00069   {    
00070     zip = new ZipFile(zip_file);
00071     ZipEntry entry = zip.getEntry(file_name);
00072            
00073     this.file_name = file_name;
00074 
00075     file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
00076                                BUFSIZE));
00077   }  
00078   /**
00079    * Parse the given Java class file and return an object that represents
00080    * the contained data, i.e. constants, methods, fields and commands.
00081    * A <em>ClassFormatError</em> is raised, if the file is not a valid
00082    * .class file. (This does not include verification of the byte code as it
00083    * is performed by the java interpreter).
00084    *
00085    * @return Class object representing the parsed class file
00086    * @throw  IOException
00087    * @throw  ClassFormatError
00088    */  
00089   public JavaClass parse() throws IOException, ClassFormatError
00090   {
00091     /****************** Read headers ********************************/
00092     // Check magic tag of class file
00093     readID();
00094 
00095     // Get compiler version
00096     readVersion();
00097 
00098     /****************** Read constant pool and related **************/
00099     // Read constant pool entries
00100     readConstantPool();
00101     
00102     // Get class information
00103     readClassInfo();
00104 
00105     // Get interface information, i.e. implemented interfaces
00106     readInterfaces();
00107 
00108     /****************** Read class fields and methods ***************/ 
00109     // Read class fields, i.e. the variables of the class
00110     readFields();
00111 
00112     // Read class methods, i.e. the functions in the class
00113     readMethods();
00114 
00115     // Read class attributes
00116     readAttributes();
00117 
00118     // Check for unknown variables
00119     //Unknown[] u = Unknown.getUnknownAttributes();
00120     //for(int i=0; i < u.length; i++)
00121     //  System.err.println("WARNING: " + u[i]);
00122 
00123     // Everything should have been read now
00124     if(file.available() > 0) {
00125       int bytes = file.available();
00126       byte[] buf = new byte[bytes];
00127       file.read(buf);
00128 
00129 //      System.err.println("WARNING: Trailing garbage at end of " + file_name);
00130 //      System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
00131     }
00132 
00133     // Read everything of interest, so close the file
00134     file.close();
00135     if(zip != null)
00136       zip.close();
00137 
00138     // Return the information we have gathered in a new object
00139     return new JavaClass(class_name_index, superclass_name_index, 
00140              file_name, major, minor, access_flags,
00141              constant_pool, interfaces, fields,
00142              methods, attributes);
00143   }  
00144   /**
00145    * Read information about the attributes of the attributes of the class.
00146    * @throw  IOException
00147    * @throw  ClassFormatError
00148    */
00149   private final void readAttributes() throws IOException, ClassFormatError
00150   {
00151     int attributes_count;
00152 
00153     attributes_count = file.readUnsignedShort();
00154     attributes       = new Attribute[attributes_count];
00155 
00156     for(int i=0; i < attributes_count; i++)
00157       attributes[i] = Attribute.readAttribute(file, constant_pool);
00158   }  
00159   /**
00160    * Read information about the class and its super class.
00161    * @throw  IOException
00162    * @throw  ClassFormatError
00163    */
00164   private final void readClassInfo() throws IOException, ClassFormatError
00165   {
00166     access_flags = file.readUnsignedShort();
00167 
00168     /* Interfaces are implicitely abstract, the flag should be set
00169      * according to the JVM specification.
00170      */
00171     if((access_flags & ACC_INTERFACE) != 0)
00172       access_flags |= ACC_ABSTRACT;
00173 
00174     if(((access_flags & ACC_ABSTRACT) != 0) && 
00175        ((access_flags & ACC_FINAL)    != 0 ))
00176       throw new ClassFormatError("Class can't be both final and abstract");
00177 
00178     class_name_index      = file.readUnsignedShort();
00179     superclass_name_index = file.readUnsignedShort();
00180   }  
00181   /**
00182    * Read constant pool entries.
00183    * @throw  IOException
00184    * @throw  ClassFormatError
00185    */
00186   private final void readConstantPool() throws IOException, ClassFormatError
00187   {
00188     constant_pool = new ConstantPool(file);
00189   }  
00190   /**
00191    * Read information about the fields of the class, i.e. its variables.
00192    * @throw  IOException
00193    * @throw  ClassFormatError
00194    */
00195   private final void readFields() throws IOException, ClassFormatError
00196   {
00197     int fields_count;
00198 
00199     fields_count = file.readUnsignedShort();
00200     fields       = new Field[fields_count];
00201 
00202     for(int i=0; i < fields_count; i++)
00203       fields[i] = new Field(file, constant_pool);
00204   }  
00205   // No getXXX/setXXX methods, wouldn't make too much sense
00206 
00207   /******************** Private utility methods **********************/
00208 
00209   /**
00210    * Check whether the header of the file is ok.
00211    * Of course, this has to be the first action on successive file reads.
00212    * @throw  IOException
00213    * @throw  ClassFormatError
00214    */
00215   private final void readID() throws IOException, ClassFormatError
00216   {
00217     int magic = 0xCAFEBABE;
00218 
00219     if(file.readInt() != magic)
00220       throw new ClassFormatError(file_name + " is not a Java .class file");
00221   }  
00222   /**
00223    * Read information about the interfaces implemented by this class.
00224    * @throw  IOException
00225    * @throw  ClassFormatError
00226    */
00227   private final void readInterfaces() throws IOException, ClassFormatError
00228   {
00229     int interfaces_count;
00230 
00231     interfaces_count = file.readUnsignedShort();
00232     interfaces       = new int[interfaces_count];
00233 
00234     for(int i=0; i < interfaces_count; i++)
00235       interfaces[i] = file.readUnsignedShort();
00236   }  
00237   /**
00238    * Read information about the methods of the class.
00239    * @throw  IOException
00240    * @throw  ClassFormatError
00241    */
00242   private final void readMethods() throws IOException, ClassFormatError
00243   {
00244     int methods_count;
00245 
00246     methods_count = file.readUnsignedShort();
00247     methods       = new Method[methods_count];
00248 
00249     for(int i=0; i < methods_count; i++)
00250       methods[i] = new Method(file, constant_pool);
00251   }  
00252   /**
00253    * Read major and minor version of compiler which created the file.
00254    * @throw  IOException
00255    * @throw  ClassFormatError
00256    */
00257   private final void readVersion() throws IOException, ClassFormatError
00258   {
00259     minor = file.readUnsignedShort();
00260     major = file.readUnsignedShort();
00261 
00262     if(!((major == MAJOR_1_1) && (minor == MINOR_1_1)) &&
00263        !((major == MAJOR_1_2) && (minor >= MINOR_1_2)))
00264       System.err.println("Possibly incompatible version: " + major + "." + minor);
00265   }  
00266 }

Generated at Thu Feb 7 06:42:27 2002 for Bandera by doxygen1.2.10 written by Dimitri van Heesch, © 1997-2001