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:41:37 pserver Exp $
00021  * @author  <A HREF="http://www.inf.fu-berlin.de/~dahm">M. Dahm</A>
00022  */
00023 public final class ClassParser {
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   private boolean         is_zip; // Loaded from zip file
00036 
00037   private static final int BUFSIZE = 8192;
00038 
00039   /**
00040    * Parse class from the given stream.
00041    *
00042    * @param file Input stream
00043    * @param file_name File name
00044    */
00045   public ClassParser(InputStream file, String file_name) {
00046     this.file_name = file_name;
00047 
00048     String clazz = file.getClass().getName(); // Not a very clean solution ...
00049     is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
00050 
00051     if(file instanceof DataInputStream) // Is already a data stream
00052       this.file = (DataInputStream)file;
00053     else
00054       this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
00055   }  
00056   /** Parse class from given .class file.
00057    *
00058    * @param file_name file name
00059    * @throw IOException
00060    */
00061   public ClassParser(String file_name) throws IOException
00062   {    
00063     is_zip = false;
00064     this.file_name = file_name;
00065     file = new DataInputStream(new BufferedInputStream
00066                    (new FileInputStream(file_name), BUFSIZE));
00067   }  
00068   /** Parse class from given .class file in a ZIP-archive
00069    *
00070    * @param file_name file name
00071    * @throw IOException
00072    */
00073   public ClassParser(String zip_file, String file_name) throws IOException
00074   {    
00075     is_zip = true;
00076     zip = new ZipFile(zip_file);
00077     ZipEntry entry = zip.getEntry(file_name);
00078            
00079     this.file_name = file_name;
00080 
00081     file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
00082                                BUFSIZE));
00083   }  
00084   /**
00085    * Parse the given Java class file and return an object that represents
00086    * the contained data, i.e., constants, methods, fields and commands.
00087    * A <em>ClassFormatError</em> is raised, if the file is not a valid
00088    * .class file. (This does not include verification of the byte code as it
00089    * is performed by the java interpreter).
00090    *
00091    * @return Class object representing the parsed class file
00092    * @throw  IOException
00093    * @throw  ClassFormatError
00094    */  
00095   public JavaClass parse() throws IOException, ClassFormatError
00096   {
00097     /****************** Read headers ********************************/
00098     // Check magic tag of class file
00099     readID();
00100 
00101     // Get compiler version
00102     readVersion();
00103 
00104     /****************** Read constant pool and related **************/
00105     // Read constant pool entries
00106     readConstantPool();
00107     
00108     // Get class information
00109     readClassInfo();
00110 
00111     // Get interface information, i.e., implemented interfaces
00112     readInterfaces();
00113 
00114     /****************** Read class fields and methods ***************/ 
00115     // Read class fields, i.e., the variables of the class
00116     readFields();
00117 
00118     // Read class methods, i.e., the functions in the class
00119     readMethods();
00120 
00121     // Read class attributes
00122     readAttributes();
00123 
00124     // Check for unknown variables
00125     //Unknown[] u = Unknown.getUnknownAttributes();
00126     //for(int i=0; i < u.length; i++)
00127     //  System.err.println("WARNING: " + u[i]);
00128 
00129     // Everything should have been read now
00130     //      if(file.available() > 0) {
00131     //        int bytes = file.available();
00132     //        byte[] buf = new byte[bytes];
00133     //        file.read(buf);
00134     
00135     //        if(!(is_zip && (buf.length == 1))) {
00136     //      System.err.println("WARNING: Trailing garbage at end of " + file_name);
00137     //      System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
00138     //        }
00139     //      }
00140 
00141     // Read everything of interest, so close the file
00142     file.close();
00143     if(zip != null)
00144       zip.close();
00145 
00146     // Return the information we have gathered in a new object
00147     return new JavaClass(class_name_index, superclass_name_index, 
00148              file_name, major, minor, access_flags,
00149              constant_pool, interfaces, fields,
00150              methods, attributes, is_zip? JavaClass.ZIP : JavaClass.FILE);
00151   }  
00152   /**
00153    * Read information about the attributes of the attributes of the class.
00154    * @throw  IOException
00155    * @throw  ClassFormatError
00156    */
00157   private final void readAttributes() throws IOException, ClassFormatError
00158   {
00159     int attributes_count;
00160 
00161     attributes_count = file.readUnsignedShort();
00162     attributes       = new Attribute[attributes_count];
00163 
00164     for(int i=0; i < attributes_count; i++)
00165       attributes[i] = Attribute.readAttribute(file, constant_pool);
00166   }  
00167   /**
00168    * Read information about the class and its super class.
00169    * @throw  IOException
00170    * @throw  ClassFormatError
00171    */
00172   private final void readClassInfo() throws IOException, ClassFormatError
00173   {
00174     access_flags = file.readUnsignedShort();
00175 
00176     /* Interfaces are implicitely abstract, the flag should be set
00177      * according to the JVM specification.
00178      */
00179     if((access_flags & Constants.ACC_INTERFACE) != 0)
00180       access_flags |= Constants.ACC_ABSTRACT;
00181 
00182     if(((access_flags & Constants.ACC_ABSTRACT) != 0) && 
00183        ((access_flags & Constants.ACC_FINAL)    != 0 ))
00184       throw new ClassFormatError("Class can't be both final and abstract");
00185 
00186     class_name_index      = file.readUnsignedShort();
00187     superclass_name_index = file.readUnsignedShort();
00188   }  
00189   /**
00190    * Read constant pool entries.
00191    * @throw  IOException
00192    * @throw  ClassFormatError
00193    */
00194   private final void readConstantPool() throws IOException, ClassFormatError
00195   {
00196     constant_pool = new ConstantPool(file);
00197   }  
00198   /**
00199    * Read information about the fields of the class, i.e., its variables.
00200    * @throw  IOException
00201    * @throw  ClassFormatError
00202    */
00203   private final void readFields() throws IOException, ClassFormatError
00204   {
00205     int fields_count;
00206 
00207     fields_count = file.readUnsignedShort();
00208     fields       = new Field[fields_count];
00209 
00210     for(int i=0; i < fields_count; i++)
00211       fields[i] = new Field(file, constant_pool);
00212   }  
00213   /******************** Private utility methods **********************/
00214 
00215   /**
00216    * Check whether the header of the file is ok.
00217    * Of course, this has to be the first action on successive file reads.
00218    * @throw  IOException
00219    * @throw  ClassFormatError
00220    */
00221   private final void readID() throws IOException, ClassFormatError
00222   {
00223     int magic = 0xCAFEBABE;
00224 
00225     if(file.readInt() != magic)
00226       throw new ClassFormatError(file_name + " is not a Java .class file");
00227   }  
00228   /**
00229    * Read information about the interfaces implemented by this class.
00230    * @throw  IOException
00231    * @throw  ClassFormatError
00232    */
00233   private final void readInterfaces() throws IOException, ClassFormatError
00234   {
00235     int interfaces_count;
00236 
00237     interfaces_count = file.readUnsignedShort();
00238     interfaces       = new int[interfaces_count];
00239 
00240     for(int i=0; i < interfaces_count; i++)
00241       interfaces[i] = file.readUnsignedShort();
00242   }  
00243   /**
00244    * Read information about the methods of the class.
00245    * @throw  IOException
00246    * @throw  ClassFormatError
00247    */
00248   private final void readMethods() throws IOException, ClassFormatError
00249   {
00250     int methods_count;
00251 
00252     methods_count = file.readUnsignedShort();
00253     methods       = new Method[methods_count];
00254 
00255     for(int i=0; i < methods_count; i++)
00256       methods[i] = new Method(file, constant_pool);
00257   }  
00258   /**
00259    * Read major and minor version of compiler which created the file.
00260    * @throw  IOException
00261    * @throw  ClassFormatError
00262    */
00263   private final void readVersion() throws IOException, ClassFormatError
00264   {
00265     minor = file.readUnsignedShort();
00266     major = file.readUnsignedShort();
00267   }  
00268 }

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