jdk/src/share/classes/sun/tools/java/BinaryClass.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/tools/java/BinaryClass.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,534 @@
+/*
+ * Copyright 1994-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.tools.java;
+
+import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayInputStream;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * WARNING: The contents of this source file are not part of any
+ * supported API.  Code that depends on them does so at its own risk:
+ * they are subject to change or removal without notice.
+ */
+public final
+class BinaryClass extends ClassDefinition implements Constants {
+    BinaryConstantPool cpool;
+    BinaryAttribute atts;
+    Vector dependencies;
+    private boolean haveLoadedNested = false;
+
+    /**
+     * Constructor
+     */
+    public BinaryClass(Object source, ClassDeclaration declaration, int modifiers,
+                           ClassDeclaration superClass, ClassDeclaration interfaces[],
+                           Vector dependencies) {
+        super(source, 0, declaration, modifiers, null, null);
+        this.dependencies = dependencies;
+        this.superClass = superClass;
+        this.interfaces = interfaces;
+    }
+
+    /**
+     * Flags used by basicCheck() to avoid duplicate calls.
+     * (Part of fix for 4105911)
+     */
+    private boolean basicCheckDone = false;
+    private boolean basicChecking = false;
+
+    /**
+     * Ready a BinaryClass for further checking.  Note that, until recently,
+     * BinaryClass relied on the default basicCheck() provided by
+     * ClassDefinition.  The definition here has been added to ensure that
+     * the information generated by collectInheritedMethods is available
+     * for BinaryClasses.
+     */
+    protected void basicCheck(Environment env) throws ClassNotFound {
+        if (tracing) env.dtEnter("BinaryClass.basicCheck: " + getName());
+
+        // We need to guard against duplicate calls to basicCheck().  They
+        // can lead to calling collectInheritedMethods() for this class
+        // from within a previous call to collectInheritedMethods() for
+        // this class.  That is not allowed.
+        // (Part of fix for 4105911)
+        if (basicChecking || basicCheckDone) {
+            if (tracing) env.dtExit("BinaryClass.basicCheck: OK " + getName());
+            return;
+        }
+
+        if (tracing) env.dtEvent("BinaryClass.basicCheck: CHECKING " + getName());
+        basicChecking = true;
+
+        super.basicCheck(env);
+
+        // Collect inheritance information.
+        if (doInheritanceChecks) {
+            collectInheritedMethods(env);
+        }
+
+        basicCheckDone = true;
+        basicChecking = false;
+        if (tracing) env.dtExit("BinaryClass.basicCheck: " + getName());
+    }
+
+    /**
+     * Load a binary class
+     */
+    public static BinaryClass load(Environment env, DataInputStream in) throws IOException {
+        return load(env, in, ~(ATT_CODE|ATT_ALLCLASSES));
+    }
+
+    public static BinaryClass load(Environment env,
+                                   DataInputStream in, int mask) throws IOException {
+        // Read the header
+        int magic = in.readInt();                    // JVM 4.1 ClassFile.magic
+        if (magic != JAVA_MAGIC) {
+            throw new ClassFormatError("wrong magic: " + magic + ", expected " + JAVA_MAGIC);
+        }
+        int minor_version = in.readUnsignedShort();  // JVM 4.1 ClassFile.minor_version
+        int version = in.readUnsignedShort();        // JVM 4.1 ClassFile.major_version
+        if (version < JAVA_MIN_SUPPORTED_VERSION) {
+            throw new ClassFormatError(
+                           sun.tools.javac.Main.getText(
+                               "javac.err.version.too.old",
+                               String.valueOf(version)));
+        } else if ((version > JAVA_MAX_SUPPORTED_VERSION)
+                     || (version == JAVA_MAX_SUPPORTED_VERSION
+                  && minor_version > JAVA_MAX_SUPPORTED_MINOR_VERSION)) {
+            throw new ClassFormatError(
+                           sun.tools.javac.Main.getText(
+                               "javac.err.version.too.recent",
+                               version+"."+minor_version));
+        }
+
+        // Read the constant pool
+        BinaryConstantPool cpool = new BinaryConstantPool(in);
+
+        // The dependencies of this class
+        Vector dependencies = cpool.getDependencies(env);
+
+        // Read modifiers
+        int classMod = in.readUnsignedShort() & ACCM_CLASS;  // JVM 4.1 ClassFile.access_flags
+
+        // Read the class name - from JVM 4.1 ClassFile.this_class
+        ClassDeclaration classDecl = cpool.getDeclaration(env, in.readUnsignedShort());
+
+        // Read the super class name (may be null) - from JVM 4.1 ClassFile.super_class
+        ClassDeclaration superClassDecl = cpool.getDeclaration(env, in.readUnsignedShort());
+
+        // Read the interface names - from JVM 4.1 ClassFile.interfaces_count
+        ClassDeclaration interfaces[] = new ClassDeclaration[in.readUnsignedShort()];
+        for (int i = 0 ; i < interfaces.length ; i++) {
+            // JVM 4.1 ClassFile.interfaces[]
+            interfaces[i] = cpool.getDeclaration(env, in.readUnsignedShort());
+        }
+
+        // Allocate the class
+        BinaryClass c = new BinaryClass(null, classDecl, classMod, superClassDecl,
+                                        interfaces, dependencies);
+        c.cpool = cpool;
+
+        // Add any additional dependencies
+        c.addDependency(superClassDecl);
+
+        // Read the fields
+        int nfields = in.readUnsignedShort();  // JVM 4.1 ClassFile.fields_count
+        for (int i = 0 ; i < nfields ; i++) {
+            // JVM 4.5 field_info.access_flags
+            int fieldMod = in.readUnsignedShort() & ACCM_FIELD;
+            // JVM 4.5 field_info.name_index
+            Identifier fieldName = cpool.getIdentifier(in.readUnsignedShort());
+            // JVM 4.5 field_info.descriptor_index
+            Type fieldType = cpool.getType(in.readUnsignedShort());
+            BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
+            c.addMember(new BinaryMember(c, fieldMod, fieldType, fieldName, atts));
+        }
+
+        // Read the methods
+        int nmethods = in.readUnsignedShort();  // JVM 4.1 ClassFile.methods_count
+        for (int i = 0 ; i < nmethods ; i++) {
+            // JVM 4.6 method_info.access_flags
+            int methMod = in.readUnsignedShort() & ACCM_METHOD;
+            // JVM 4.6 method_info.name_index
+            Identifier methName = cpool.getIdentifier(in.readUnsignedShort());
+            // JVM 4.6 method_info.descriptor_index
+            Type methType = cpool.getType(in.readUnsignedShort());
+            BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
+            c.addMember(new BinaryMember(c, methMod, methType, methName, atts));
+        }
+
+        // Read the class attributes
+        c.atts = BinaryAttribute.load(in, cpool, mask);
+
+        // See if the SourceFile is known
+        byte data[] = c.getAttribute(idSourceFile);
+        if (data != null) {
+            DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(data));
+            // JVM 4.7.2 SourceFile_attribute.sourcefile_index
+            c.source = cpool.getString(dataStream.readUnsignedShort());
+        }
+
+        // See if the Documentation is know
+        data = c.getAttribute(idDocumentation);
+        if (data != null) {
+            c.documentation = new DataInputStream(new ByteArrayInputStream(data)).readUTF();
+        }
+
+        // Was it compiled as deprecated?
+        if (c.getAttribute(idDeprecated) != null) {
+            c.modifiers |= M_DEPRECATED;
+        }
+
+        // Was it synthesized by the compiler?
+        if (c.getAttribute(idSynthetic) != null) {
+            c.modifiers |= M_SYNTHETIC;
+        }
+
+        return c;
+    }
+
+    /**
+     * Called when an environment ties a binary definition to a declaration.
+     * At this point, auxiliary definitions may be loaded.
+     */
+
+    public void loadNested(Environment env) {
+        loadNested(env, 0);
+    }
+
+    public void loadNested(Environment env, int flags) {
+        // Sanity check.
+        if (haveLoadedNested) {
+            // Duplicate calls most likely should not occur, but they do
+            // in javap.  Be tolerant of them for the time being.
+            // throw new CompilerError("multiple loadNested");
+            if (tracing) env.dtEvent("loadNested: DUPLICATE CALL SKIPPED");
+            return;
+        }
+        haveLoadedNested = true;
+        // Read class-nesting information.
+        try {
+            byte data[];
+            data = getAttribute(idInnerClasses);
+            if (data != null) {
+                initInnerClasses(env, data, flags);
+            }
+        } catch (IOException ee) {
+            // The inner classes attribute is not well-formed.
+            // It may, for example, contain no data.  Report this.
+            // We used to throw a CompilerError here (bug 4095108).
+            env.error(0, "malformed.attribute", getClassDeclaration(),
+                      idInnerClasses);
+            if (tracing)
+                env.dtEvent("loadNested: MALFORMED ATTRIBUTE (InnerClasses)");
+        }
+    }
+
+    private void initInnerClasses(Environment env,
+                                  byte data[],
+                                  int flags) throws IOException {
+        DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
+        int nrec = ds.readUnsignedShort();  // InnerClasses_attribute.number_of_classes
+        for (int i = 0; i < nrec; i++) {
+            // For each inner class name transformation, we have a record
+            // with the following fields:
+            //
+            //    u2 inner_class_info_index;   // CONSTANT_Class_info index
+            //    u2 outer_class_info_index;   // CONSTANT_Class_info index
+            //    u2 inner_name_index;         // CONSTANT_Utf8_info index
+            //    u2 inner_class_access_flags; // access_flags bitmask
+            //
+            // The spec states that outer_class_info_index is 0 iff
+            // the inner class is not a member of its enclosing class (i.e.
+            // it is a local or anonymous class).  The spec also states
+            // that if a class is anonymous then inner_name_index should
+            // be 0.
+            //
+            // Prior to jdk1.2, javac did not implement the spec.  Instead
+            // it <em>always</em> set outer_class_info_index to the
+            // enclosing outer class and if the class was anonymous,
+            // it set inner_name_index to be the index of a CONSTANT_Utf8
+            // entry containing the null string "" (idNull).  This code is
+            // designed to handle either kind of class file.
+            //
+            // See also the compileClass() method in SourceClass.java.
+
+            // Read in the inner_class_info
+            // InnerClasses_attribute.classes.inner_class_info_index
+            int inner_index = ds.readUnsignedShort();
+            // could check for zero.
+            ClassDeclaration inner = cpool.getDeclaration(env, inner_index);
+
+            // Read in the outer_class_info.  Note that the index will be
+            // zero if the class is "not a member".
+            ClassDeclaration outer = null;
+            // InnerClasses_attribute.classes.outer_class_info_index
+            int outer_index = ds.readUnsignedShort();
+            if (outer_index != 0) {
+                outer = cpool.getDeclaration(env, outer_index);
+            }
+
+            // Read in the inner_name_index.  This may be zero.  An anonymous
+            // class will either have an inner_nm_index of zero (as the spec
+            // dictates) or it will have an inner_nm of idNull (for classes
+            // generated by pre-1.2 compilers).  Handle both.
+            Identifier inner_nm = idNull;
+            // InnerClasses_attribute.classes.inner_name_index
+            int inner_nm_index = ds.readUnsignedShort();
+            if (inner_nm_index != 0) {
+                inner_nm = Identifier.lookup(cpool.getString(inner_nm_index));
+            }
+
+            // Read in the modifiers for the inner class.
+            // InnerClasses_attribute.classes.inner_name_index
+            int mods = ds.readUnsignedShort();
+
+            // Is the class accessible?
+            // The old code checked for
+            //
+            //    (!inner_nm.equals(idNull) && (mods & M_PRIVATE) == 0)
+            //
+            // which we will preserve to keep it working for class files
+            // generated by 1.1 compilers.  In addition we check for
+            //
+            //    (outer != null)
+            //
+            // as an additional check that only makes sense with 1.2
+            // generated files.  Note that it is entirely possible that
+            // the M_PRIVATE bit is always enough.  We are being
+            // conservative here.
+            //
+            // The ATT_ALLCLASSES flag causes the M_PRIVATE modifier
+            // to be ignored, and is used by tools such as 'javap' that
+            // wish to examine all classes regardless of the normal access
+            // controls that apply during compilation.  Note that anonymous
+            // and local classes are still not considered accessible, though
+            // named local classes in jdk1.1 may slip through.  Note that
+            // this accessibility test is an optimization, and it is safe to
+            // err on the side of greater accessibility.
+            boolean accessible =
+                (outer != null) &&
+                (!inner_nm.equals(idNull)) &&
+                ((mods & M_PRIVATE) == 0 ||
+                 (flags & ATT_ALLCLASSES) != 0);
+
+            // The reader should note that there has been a significant change
+            // in the way that the InnerClasses attribute is being handled.
+            // In particular, previously the compiler called initInner() for
+            // <em>every</em> inner class.  Now the compiler does not call
+            // initInner() if the inner class is inaccessible.  This means
+            // that inaccessible inner classes don't have any of the processing
+            // from initInner() done for them: fixing the access flags,
+            // setting outerClass, setting outerMember in their outerClass,
+            // etc.  We believe this is fine: if the class is inaccessible
+            // and binary, then everyone who needs to see its internals
+            // has already been compiled.  Hopefully.
+
+            if (accessible) {
+                Identifier nm =
+                    Identifier.lookupInner(outer.getName(), inner_nm);
+
+                // Tell the type module about the nesting relation:
+                Type.tClass(nm);
+
+                if (inner.equals(getClassDeclaration())) {
+                    // The inner class in the record is this class.
+                    try {
+                        ClassDefinition outerClass = outer.getClassDefinition(env);
+                        initInner(outerClass, mods);
+                    } catch (ClassNotFound e) {
+                        // report the error elsewhere
+                    }
+                } else if (outer.equals(getClassDeclaration())) {
+                    // The outer class in the record is this class.
+                    try {
+                        ClassDefinition innerClass =
+                            inner.getClassDefinition(env);
+                        initOuter(innerClass, mods);
+                    } catch (ClassNotFound e) {
+                        // report the error elsewhere
+                    }
+                }
+            }
+        }
+    }
+
+    private void initInner(ClassDefinition outerClass, int mods) {
+        if (getOuterClass() != null)
+            return;             // already done
+        /******
+        // Maybe set static, protected, or private.
+        if ((modifiers & M_PUBLIC) != 0)
+            mods &= M_STATIC;
+        else
+            mods &= M_PRIVATE | M_PROTECTED | M_STATIC;
+        modifiers |= mods;
+        ******/
+        // For an inner class, the class access may have been weakened
+        // from that originally declared the source.  We must take the
+        // actual access permissions against which we check any source
+        // we are currently compiling from the InnerClasses attribute.
+        // We attempt to guard here against bogus combinations of modifiers.
+        if ((mods & M_PRIVATE) != 0) {
+            // Private cannot be combined with public or protected.
+            mods &= ~(M_PUBLIC | M_PROTECTED);
+        } else if ((mods & M_PROTECTED) != 0) {
+            // Protected cannot be combined with public.
+            mods &= ~M_PUBLIC;
+        }
+        if ((mods & M_INTERFACE) != 0) {
+            // All interfaces are implicitly abstract.
+            // All interfaces that are members of a type are implicitly static.
+            mods |= (M_ABSTRACT | M_STATIC);
+        }
+        if (outerClass.isInterface()) {
+            // All types that are members of interfaces are implicitly
+            // public and static.
+            mods |= (M_PUBLIC | M_STATIC);
+            mods &= ~(M_PRIVATE | M_PROTECTED);
+        }
+        modifiers = mods;
+
+        setOuterClass(outerClass);
+
+        for (MemberDefinition field = getFirstMember();
+             field != null;
+             field = field.getNextMember()) {
+            if (field.isUplevelValue()
+                    && outerClass.getType().equals(field.getType())
+                    && field.getName().toString().startsWith(prefixThis)) {
+                setOuterMember(field);
+            }
+        }
+    }
+
+    private void initOuter(ClassDefinition innerClass, int mods) {
+        if (innerClass instanceof BinaryClass)
+            ((BinaryClass)innerClass).initInner(this, mods);
+        addMember(new BinaryMember(innerClass));
+    }
+
+    /**
+     * Write the class out to a given stream.  This function mirrors the loader.
+     */
+    public void write(Environment env, OutputStream out) throws IOException {
+        DataOutputStream data = new DataOutputStream(out);
+
+        // write out the header
+        data.writeInt(JAVA_MAGIC);
+        data.writeShort(env.getMinorVersion());
+        data.writeShort(env.getMajorVersion());
+
+        // Write out the constant pool
+        cpool.write(data, env);
+
+        // Write class information
+        data.writeShort(getModifiers() & ACCM_CLASS);
+        data.writeShort(cpool.indexObject(getClassDeclaration(), env));
+        data.writeShort((getSuperClass() != null)
+                        ? cpool.indexObject(getSuperClass(), env) : 0);
+        data.writeShort(interfaces.length);
+        for (int i = 0 ; i < interfaces.length ; i++) {
+            data.writeShort(cpool.indexObject(interfaces[i], env));
+        }
+
+        // count the fields and the methods
+        int fieldCount = 0, methodCount = 0;
+        for (MemberDefinition f = firstMember; f != null; f = f.getNextMember())
+            if (f.isMethod()) methodCount++; else fieldCount++;
+
+        // write out each the field count, and then each field
+        data.writeShort(fieldCount);
+        for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
+            if (!f.isMethod()) {
+                data.writeShort(f.getModifiers() & ACCM_FIELD);
+                String name = f.getName().toString();
+                String signature = f.getType().getTypeSignature();
+                data.writeShort(cpool.indexString(name, env));
+                data.writeShort(cpool.indexString(signature, env));
+                BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
+            }
+        }
+
+        // write out each method count, and then each method
+        data.writeShort(methodCount);
+        for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
+            if (f.isMethod()) {
+                data.writeShort(f.getModifiers() & ACCM_METHOD);
+                String name = f.getName().toString();
+                String signature = f.getType().getTypeSignature();
+                data.writeShort(cpool.indexString(name, env));
+                data.writeShort(cpool.indexString(signature, env));
+                BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
+            }
+        }
+
+        // write out the class attributes
+        BinaryAttribute.write(atts, data, cpool, env);
+        data.flush();
+    }
+
+    /**
+     * Get the dependencies
+     */
+    public Enumeration getDependencies() {
+        return dependencies.elements();
+    }
+
+    /**
+     * Add a dependency
+     */
+    public void addDependency(ClassDeclaration c) {
+        if ((c != null) && !dependencies.contains(c)) {
+            dependencies.addElement(c);
+        }
+    }
+
+    /**
+     * Get the constant pool
+     */
+    public BinaryConstantPool getConstants() {
+        return cpool;
+    }
+
+    /**
+     * Get a class attribute
+     */
+    public byte getAttribute(Identifier name)[] {
+        for (BinaryAttribute att = atts ; att != null ; att = att.next) {
+            if (att.name.equals(name)) {
+                return att.data;
+            }
+        }
+        return null;
+    }
+}