jaxp/src/java.xml/share/classes/com/sun/org/apache/bcel/internal/classfile/ClassParser.java
changeset 46174 5611d2529b49
parent 44797 8b3b3b911b8a
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/bcel/internal/classfile/ClassParser.java	Tue Aug 08 22:52:41 2017 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/bcel/internal/classfile/ClassParser.java	Sun Aug 13 21:10:40 2017 -0700
@@ -21,10 +21,15 @@
 
 package com.sun.org.apache.bcel.internal.classfile;
 
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
-import  com.sun.org.apache.bcel.internal.Constants;
-import  java.io.*;
-import  java.util.zip.*;
+import com.sun.org.apache.bcel.internal.Const;
 
 /**
  * Wrapper class that parses a given Java .class file. The method <A
@@ -34,262 +39,274 @@
  * the caller.
  *
  * The structure and the names comply, except for a few conveniences,
- * exactly with the <A href="ftp://java.sun.com/docs/specs/vmspec.ps">
+ * exactly with the <A href="http://docs.oracle.com/javase/specs/">
  * JVM specification 1.0</a>. See this paper for
  * further details about the structure of a bytecode file.
  *
- * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
+ * @version $Id: ClassParser.java 1749603 2016-06-21 20:50:19Z ggregory $
  */
 public final class ClassParser {
-  private DataInputStream file;
-  private ZipFile         zip;
-  private String          file_name;
-  private int             class_name_index, superclass_name_index;
-  private int             major, minor; // Compiler version
-  private int             access_flags; // Access rights of parsed class
-  private int[]           interfaces; // Names of implemented interfaces
-  private ConstantPool    constant_pool; // collection of constants
-  private Field[]         fields; // class fields, i.e., its variables
-  private Method[]        methods; // methods defined in the class
-  private Attribute[]     attributes; // attributes defined in the class
-  private boolean         is_zip; // Loaded from zip file
 
-  private static final int BUFSIZE = 8192;
+    private DataInputStream dataInputStream;
+    private final boolean fileOwned;
+    private final String file_name;
+    private String zip_file;
+    private int class_name_index;
+    private int superclass_name_index;
+    private int major; // Compiler version
+    private int minor; // Compiler version
+    private int access_flags; // Access rights of parsed class
+    private int[] interfaces; // Names of implemented interfaces
+    private ConstantPool constant_pool; // collection of constants
+    private Field[] fields; // class fields, i.e., its variables
+    private Method[] methods; // methods defined in the class
+    private Attribute[] attributes; // attributes defined in the class
+    private final boolean is_zip; // Loaded from zip file
+    private static final int BUFSIZE = 8192;
 
-  /**
-   * Parse class from the given stream.
-   *
-   * @param file Input stream
-   * @param file_name File name
-   */
-  public ClassParser(InputStream file, String file_name) {
-    this.file_name = file_name;
-
-    String clazz = file.getClass().getName(); // Not a very clean solution ...
-    is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
 
-    if(file instanceof DataInputStream) // Is already a data stream
-      this.file = (DataInputStream)file;
-    else
-      this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
-  }
+    /**
+     * Parse class from the given stream.
+     *
+     * @param inputStream Input stream
+     * @param file_name File name
+     */
+    public ClassParser(final InputStream inputStream, final String file_name) {
+        this.file_name = file_name;
+        fileOwned = false;
+        final String clazz = inputStream.getClass().getName(); // Not a very clean solution ...
+        is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
+        if (inputStream instanceof DataInputStream) {
+            this.dataInputStream = (DataInputStream) inputStream;
+        } else {
+            this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE));
+        }
+    }
 
-  /** Parse class from given .class file.
-   *
-   * @param file_name file name
-   * @throws IOException
-   */
-  public ClassParser(String file_name) throws IOException
-  {
-    is_zip = false;
-    this.file_name = file_name;
-    file = new DataInputStream(new BufferedInputStream
-                               (new FileInputStream(file_name), BUFSIZE));
-  }
-
-  /** Parse class from given .class file in a ZIP-archive
-   *
-   * @param file_name file name
-   * @throws IOException
-   */
-  public ClassParser(String zip_file, String file_name) throws IOException
-  {
-    is_zip = true;
-    zip = new ZipFile(zip_file);
-    ZipEntry entry = zip.getEntry(file_name);
-
-    this.file_name = file_name;
 
-    file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
-                                                       BUFSIZE));
-  }
+    /** Parse class from given .class file.
+     *
+     * @param file_name file name
+     */
+    public ClassParser(final String file_name) {
+        is_zip = false;
+        this.file_name = file_name;
+        fileOwned = true;
+    }
+
 
-  /**
-   * Parse the given Java class file and return an object that represents
-   * the contained data, i.e., constants, methods, fields and commands.
-   * A <em>ClassFormatException</em> is raised, if the file is not a valid
-   * .class file. (This does not include verification of the byte code as it
-   * is performed by the java interpreter).
-   *
-   * @return Class object representing the parsed class file
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  public JavaClass parse() throws IOException, ClassFormatException
-  {
-    /****************** Read headers ********************************/
-    // Check magic tag of class file
-    readID();
-
-    // Get compiler version
-    readVersion();
-
-    /****************** Read constant pool and related **************/
-    // Read constant pool entries
-    readConstantPool();
-
-    // Get class information
-    readClassInfo();
+    /** Parse class from given .class file in a ZIP-archive
+     *
+     * @param zip_file zip file name
+     * @param file_name file name
+     */
+    public ClassParser(final String zip_file, final String file_name) {
+        is_zip = true;
+        fileOwned = true;
+        this.zip_file = zip_file;
+        this.file_name = file_name;
+    }
 
-    // Get interface information, i.e., implemented interfaces
-    readInterfaces();
-
-    /****************** Read class fields and methods ***************/
-    // Read class fields, i.e., the variables of the class
-    readFields();
-
-    // Read class methods, i.e., the functions in the class
-    readMethods();
-
-    // Read class attributes
-    readAttributes();
 
-    // Check for unknown variables
-    //Unknown[] u = Unknown.getUnknownAttributes();
-    //for(int i=0; i < u.length; i++)
-    //  System.err.println("WARNING: " + u[i]);
-
-    // Everything should have been read now
-    //      if(file.available() > 0) {
-    //        int bytes = file.available();
-    //        byte[] buf = new byte[bytes];
-    //        file.read(buf);
+    /**
+     * Parse the given Java class file and return an object that represents
+     * the contained data, i.e., constants, methods, fields and commands.
+     * A <em>ClassFormatException</em> is raised, if the file is not a valid
+     * .class file. (This does not include verification of the byte code as it
+     * is performed by the java interpreter).
+     *
+     * @return Class object representing the parsed class file
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    public JavaClass parse() throws IOException, ClassFormatException {
+        ZipFile zip = null;
+        try {
+            if (fileOwned) {
+                if (is_zip) {
+                    zip = new ZipFile(zip_file);
+                    final ZipEntry entry = zip.getEntry(file_name);
 
-    //        if(!(is_zip && (buf.length == 1))) {
-    //          System.err.println("WARNING: Trailing garbage at end of " + file_name);
-    //          System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
-    //        }
-    //      }
-
-    // Read everything of interest, so close the file
-    file.close();
-    if(zip != null)
-      zip.close();
+                    if (entry == null) {
+                        throw new IOException("File " + file_name + " not found");
+                    }
 
-    // Return the information we have gathered in a new object
-    return new JavaClass(class_name_index, superclass_name_index,
-                         file_name, major, minor, access_flags,
-                         constant_pool, interfaces, fields,
-                         methods, attributes, is_zip? JavaClass.ZIP : JavaClass.FILE);
-  }
-
-  /**
-   * Read information about the attributes of the class.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readAttributes() throws IOException, ClassFormatException
-  {
-    int attributes_count;
-
-    attributes_count = file.readUnsignedShort();
-    attributes       = new Attribute[attributes_count];
-
-    for(int i=0; i < attributes_count; i++)
-      attributes[i] = Attribute.readAttribute(file, constant_pool);
-  }
-
-  /**
-   * Read information about the class and its super class.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readClassInfo() throws IOException, ClassFormatException
-  {
-    access_flags = file.readUnsignedShort();
+                    dataInputStream = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
+                            BUFSIZE));
+                } else {
+                    dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(
+                            file_name), BUFSIZE));
+                }
+            }
+            /****************** Read headers ********************************/
+            // Check magic tag of class file
+            readID();
+            // Get compiler version
+            readVersion();
+            /****************** Read constant pool and related **************/
+            // Read constant pool entries
+            readConstantPool();
+            // Get class information
+            readClassInfo();
+            // Get interface information, i.e., implemented interfaces
+            readInterfaces();
+            /****************** Read class fields and methods ***************/
+            // Read class fields, i.e., the variables of the class
+            readFields();
+            // Read class methods, i.e., the functions in the class
+            readMethods();
+            // Read class attributes
+            readAttributes();
+            // Check for unknown variables
+            //Unknown[] u = Unknown.getUnknownAttributes();
+            //for (int i=0; i < u.length; i++)
+            //  System.err.println("WARNING: " + u[i]);
+            // Everything should have been read now
+            //      if(file.available() > 0) {
+            //        int bytes = file.available();
+            //        byte[] buf = new byte[bytes];
+            //        file.read(buf);
+            //        if(!(is_zip && (buf.length == 1))) {
+            //      System.err.println("WARNING: Trailing garbage at end of " + file_name);
+            //      System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
+            //        }
+            //      }
+        } finally {
+            // Read everything of interest, so close the file
+            if (fileOwned) {
+                try {
+                    if (dataInputStream != null) {
+                        dataInputStream.close();
+                    }
+                } catch (final IOException ioe) {
+                    //ignore close exceptions
+                }
+            }
+            try {
+                if (zip != null) {
+                    zip.close();
+                }
+            } catch (final IOException ioe) {
+                //ignore close exceptions
+            }
+        }
+        // Return the information we have gathered in a new object
+        return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
+                access_flags, constant_pool, interfaces, fields, methods, attributes, is_zip
+                        ? JavaClass.ZIP
+                        : JavaClass.FILE);
+    }
 
-    /* Interfaces are implicitely abstract, the flag should be set
-     * according to the JVM specification.
-     */
-    if((access_flags & Constants.ACC_INTERFACE) != 0)
-      access_flags |= Constants.ACC_ABSTRACT;
-
-    if(((access_flags & Constants.ACC_ABSTRACT) != 0) &&
-       ((access_flags & Constants.ACC_FINAL)    != 0 ))
-      throw new ClassFormatException("Class can't be both final and abstract");
 
-    class_name_index      = file.readUnsignedShort();
-    superclass_name_index = file.readUnsignedShort();
-  }
-  /**
-   * Read constant pool entries.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readConstantPool() throws IOException, ClassFormatException
-  {
-    constant_pool = new ConstantPool(file);
-  }
+    /**
+     * Read information about the attributes of the class.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readAttributes() throws IOException, ClassFormatException {
+        final int attributes_count = dataInputStream.readUnsignedShort();
+        attributes = new Attribute[attributes_count];
+        for (int i = 0; i < attributes_count; i++) {
+            attributes[i] = Attribute.readAttribute(dataInputStream, constant_pool);
+        }
+    }
 
-  /**
-   * Read information about the fields of the class, i.e., its variables.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readFields() throws IOException, ClassFormatException
-  {
-    int fields_count;
 
-    fields_count = file.readUnsignedShort();
-    fields       = new Field[fields_count];
+    /**
+     * Read information about the class and its super class.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readClassInfo() throws IOException, ClassFormatException {
+        access_flags = dataInputStream.readUnsignedShort();
+        /* Interfaces are implicitely abstract, the flag should be set
+         * according to the JVM specification.
+         */
+        if ((access_flags & Const.ACC_INTERFACE) != 0) {
+            access_flags |= Const.ACC_ABSTRACT;
+        }
+        if (((access_flags & Const.ACC_ABSTRACT) != 0)
+                && ((access_flags & Const.ACC_FINAL) != 0)) {
+            throw new ClassFormatException("Class " + file_name + " can't be both final and abstract");
+        }
+        class_name_index = dataInputStream.readUnsignedShort();
+        superclass_name_index = dataInputStream.readUnsignedShort();
+    }
 
-    for(int i=0; i < fields_count; i++)
-      fields[i] = new Field(file, constant_pool);
-  }
-
-  /******************** Private utility methods **********************/
 
-  /**
-   * Check whether the header of the file is ok.
-   * Of course, this has to be the first action on successive file reads.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readID() throws IOException, ClassFormatException
-  {
-    int magic = 0xCAFEBABE;
+    /**
+     * Read constant pool entries.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readConstantPool() throws IOException, ClassFormatException {
+        constant_pool = new ConstantPool(dataInputStream);
+    }
+
 
-    if(file.readInt() != magic)
-      throw new ClassFormatException(file_name + " is not a Java .class file");
-  }
-  /**
-   * Read information about the interfaces implemented by this class.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readInterfaces() throws IOException, ClassFormatException
-  {
-    int interfaces_count;
+    /**
+     * Read information about the fields of the class, i.e., its variables.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readFields() throws IOException, ClassFormatException {
+        final int fields_count = dataInputStream.readUnsignedShort();
+        fields = new Field[fields_count];
+        for (int i = 0; i < fields_count; i++) {
+            fields[i] = new Field(dataInputStream, constant_pool);
+        }
+    }
+
 
-    interfaces_count = file.readUnsignedShort();
-    interfaces       = new int[interfaces_count];
+    /******************** Private utility methods **********************/
+    /**
+     * Check whether the header of the file is ok.
+     * Of course, this has to be the first action on successive file reads.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readID() throws IOException, ClassFormatException {
+        if (dataInputStream.readInt() != Const.JVM_CLASSFILE_MAGIC) {
+            throw new ClassFormatException(file_name + " is not a Java .class file");
+        }
+    }
 
-    for(int i=0; i < interfaces_count; i++)
-      interfaces[i] = file.readUnsignedShort();
-  }
-  /**
-   * Read information about the methods of the class.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readMethods() throws IOException, ClassFormatException
-  {
-    int methods_count;
 
-    methods_count = file.readUnsignedShort();
-    methods       = new Method[methods_count];
+    /**
+     * Read information about the interfaces implemented by this class.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readInterfaces() throws IOException, ClassFormatException {
+        final int interfaces_count = dataInputStream.readUnsignedShort();
+        interfaces = new int[interfaces_count];
+        for (int i = 0; i < interfaces_count; i++) {
+            interfaces[i] = dataInputStream.readUnsignedShort();
+        }
+    }
+
 
-    for(int i=0; i < methods_count; i++)
-      methods[i] = new Method(file, constant_pool);
-  }
-  /**
-   * Read major and minor version of compiler which created the file.
-   * @throws  IOException
-   * @throws  ClassFormatException
-   */
-  private final void readVersion() throws IOException, ClassFormatException
-  {
-    minor = file.readUnsignedShort();
-    major = file.readUnsignedShort();
-  }
+    /**
+     * Read information about the methods of the class.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readMethods() throws IOException, ClassFormatException {
+        final int methods_count = dataInputStream.readUnsignedShort();
+        methods = new Method[methods_count];
+        for (int i = 0; i < methods_count; i++) {
+            methods[i] = new Method(dataInputStream, constant_pool);
+        }
+    }
+
+
+    /**
+     * Read major and minor version of compiler which created the file.
+     * @throws  IOException
+     * @throws  ClassFormatException
+     */
+    private void readVersion() throws IOException, ClassFormatException {
+        minor = dataInputStream.readUnsignedShort();
+        major = dataInputStream.readUnsignedShort();
+    }
 }