text: /*
   * Copyright (c) 1994, 2015, Oracle and/or its affiliates. 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.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.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.
   */
  @SuppressWarnings("deprecation")
  public final
  class BinaryClass extends ClassDefinition implements Constants {
      BinaryConstantPool cpool;
      BinaryAttribute atts;
      Vector<ClassDeclaration> dependencies;
      private boolean haveLoadedNested = false;
  
      /**
       * Constructor
       */
      public BinaryClass(Object source, ClassDeclaration declaration, int modifiers,
                             ClassDeclaration superClass, ClassDeclaration interfaces[],
                             Vector<ClassDeclaration> 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<ClassDeclaration> 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<ClassDeclaration> 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;
      }
  }
  