1 /*
   2  * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.java;
  27 
  28 import java.io.IOException;
  29 import java.io.DataInputStream;
  30 import java.io.OutputStream;
  31 import java.io.DataOutputStream;
  32 import java.io.ByteArrayInputStream;
  33 import java.util.Hashtable;
  34 import java.util.Vector;
  35 import java.util.Enumeration;
  36 
  37 /**
  38  * WARNING: The contents of this source file are not part of any
  39  * supported API.  Code that depends on them does so at its own risk:
  40  * they are subject to change or removal without notice.
  41  */
  42 @SuppressWarnings("deprecation")
  43 public final
  44 class BinaryClass extends ClassDefinition implements Constants {
  45     BinaryConstantPool cpool;
  46     BinaryAttribute atts;
  47     Vector<ClassDeclaration> dependencies;
  48     private boolean haveLoadedNested = false;
  49 
  50     /**
  51      * Constructor
  52      */
  53     public BinaryClass(Object source, ClassDeclaration declaration, int modifiers,
  54                            ClassDeclaration superClass, ClassDeclaration interfaces[],
  55                            Vector<ClassDeclaration> dependencies) {
  56         super(source, 0, declaration, modifiers, null, null);
  57         this.dependencies = dependencies;
  58         this.superClass = superClass;
  59         this.interfaces = interfaces;
  60     }
  61 
  62     /**
  63      * Flags used by basicCheck() to avoid duplicate calls.
  64      * (Part of fix for 4105911)
  65      */
  66     private boolean basicCheckDone = false;
  67     private boolean basicChecking = false;
  68 
  69     /**
  70      * Ready a BinaryClass for further checking.  Note that, until recently,
  71      * BinaryClass relied on the default basicCheck() provided by
  72      * ClassDefinition.  The definition here has been added to ensure that
  73      * the information generated by collectInheritedMethods is available
  74      * for BinaryClasses.
  75      */
  76     protected void basicCheck(Environment env) throws ClassNotFound {
  77         if (tracing) env.dtEnter("BinaryClass.basicCheck: " + getName());
  78 
  79         // We need to guard against duplicate calls to basicCheck().  They
  80         // can lead to calling collectInheritedMethods() for this class
  81         // from within a previous call to collectInheritedMethods() for
  82         // this class.  That is not allowed.
  83         // (Part of fix for 4105911)
  84         if (basicChecking || basicCheckDone) {
  85             if (tracing) env.dtExit("BinaryClass.basicCheck: OK " + getName());
  86             return;
  87         }
  88 
  89         if (tracing) env.dtEvent("BinaryClass.basicCheck: CHECKING " + getName());
  90         basicChecking = true;
  91 
  92         super.basicCheck(env);
  93 
  94         // Collect inheritance information.
  95         if (doInheritanceChecks) {
  96             collectInheritedMethods(env);
  97         }
  98 
  99         basicCheckDone = true;
 100         basicChecking = false;
 101         if (tracing) env.dtExit("BinaryClass.basicCheck: " + getName());
 102     }
 103 
 104     /**
 105      * Load a binary class
 106      */
 107     public static BinaryClass load(Environment env, DataInputStream in) throws IOException {
 108         return load(env, in, ~(ATT_CODE|ATT_ALLCLASSES));
 109     }
 110 
 111     public static BinaryClass load(Environment env,
 112                                    DataInputStream in, int mask) throws IOException {
 113         // Read the header
 114         int magic = in.readInt();                    // JVM 4.1 ClassFile.magic
 115         if (magic != JAVA_MAGIC) {
 116             throw new ClassFormatError("wrong magic: " + magic + ", expected " + JAVA_MAGIC);
 117         }
 118         int minor_version = in.readUnsignedShort();  // JVM 4.1 ClassFile.minor_version
 119         int version = in.readUnsignedShort();        // JVM 4.1 ClassFile.major_version
 120         if (version < JAVA_MIN_SUPPORTED_VERSION) {
 121             throw new ClassFormatError(
 122                            sun.tools.javac.Main.getText(
 123                                "javac.err.version.too.old",
 124                                String.valueOf(version)));
 125         } else if ((version >= JAVA_MIN_PREVIEW_MAJOR_VERSION)
 126                      && (minor_version == JAVA_PREVIEW_MINOR_VERSION)) {
 127             // reject all class files that have preview features enabled
 128             throw new ClassFormatError(
 129                            sun.tools.javac.Main.getText(
 130                                "javac.err.version.preview",
 131                                version+"."+minor_version));
 132         } else if ((version > JAVA_MAX_SUPPORTED_VERSION)
 133                      || (version == JAVA_MAX_SUPPORTED_VERSION
 134                   && minor_version > JAVA_MAX_SUPPORTED_MINOR_VERSION)) {
 135             throw new ClassFormatError(
 136                            sun.tools.javac.Main.getText(
 137                                "javac.err.version.too.recent",
 138                                version+"."+minor_version));
 139         }
 140 
 141         // Read the constant pool
 142         BinaryConstantPool cpool = new BinaryConstantPool(in);
 143 
 144         // The dependencies of this class
 145         Vector<ClassDeclaration> dependencies = cpool.getDependencies(env);
 146 
 147         // Read modifiers
 148         int classMod = in.readUnsignedShort() & ACCM_CLASS;  // JVM 4.1 ClassFile.access_flags
 149 
 150         // Read the class name - from JVM 4.1 ClassFile.this_class
 151         ClassDeclaration classDecl = cpool.getDeclaration(env, in.readUnsignedShort());
 152 
 153         // Read the super class name (may be null) - from JVM 4.1 ClassFile.super_class
 154         ClassDeclaration superClassDecl = cpool.getDeclaration(env, in.readUnsignedShort());
 155 
 156         // Read the interface names - from JVM 4.1 ClassFile.interfaces_count
 157         ClassDeclaration interfaces[] = new ClassDeclaration[in.readUnsignedShort()];
 158         for (int i = 0 ; i < interfaces.length ; i++) {
 159             // JVM 4.1 ClassFile.interfaces[]
 160             interfaces[i] = cpool.getDeclaration(env, in.readUnsignedShort());
 161         }
 162 
 163         // Allocate the class
 164         BinaryClass c = new BinaryClass(null, classDecl, classMod, superClassDecl,
 165                                         interfaces, dependencies);
 166         c.cpool = cpool;
 167 
 168         // Add any additional dependencies
 169         c.addDependency(superClassDecl);
 170 
 171         // Read the fields
 172         int nfields = in.readUnsignedShort();  // JVM 4.1 ClassFile.fields_count
 173         for (int i = 0 ; i < nfields ; i++) {
 174             // JVM 4.5 field_info.access_flags
 175             int fieldMod = in.readUnsignedShort() & ACCM_FIELD;
 176             // JVM 4.5 field_info.name_index
 177             Identifier fieldName = cpool.getIdentifier(in.readUnsignedShort());
 178             // JVM 4.5 field_info.descriptor_index
 179             Type fieldType = cpool.getType(in.readUnsignedShort());
 180             BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
 181             c.addMember(new BinaryMember(c, fieldMod, fieldType, fieldName, atts));
 182         }
 183 
 184         // Read the methods
 185         int nmethods = in.readUnsignedShort();  // JVM 4.1 ClassFile.methods_count
 186         for (int i = 0 ; i < nmethods ; i++) {
 187             // JVM 4.6 method_info.access_flags
 188             int methMod = in.readUnsignedShort() & ACCM_METHOD;
 189             // JVM 4.6 method_info.name_index
 190             Identifier methName = cpool.getIdentifier(in.readUnsignedShort());
 191             // JVM 4.6 method_info.descriptor_index
 192             Type methType = cpool.getType(in.readUnsignedShort());
 193             BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
 194             c.addMember(new BinaryMember(c, methMod, methType, methName, atts));
 195         }
 196 
 197         // Read the class attributes
 198         c.atts = BinaryAttribute.load(in, cpool, mask);
 199 
 200         // See if the SourceFile is known
 201         byte data[] = c.getAttribute(idSourceFile);
 202         if (data != null) {
 203             DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(data));
 204             // JVM 4.7.2 SourceFile_attribute.sourcefile_index
 205             c.source = cpool.getString(dataStream.readUnsignedShort());
 206         }
 207 
 208         // See if the Documentation is know
 209         data = c.getAttribute(idDocumentation);
 210         if (data != null) {
 211             c.documentation = new DataInputStream(new ByteArrayInputStream(data)).readUTF();
 212         }
 213 
 214         // Was it compiled as deprecated?
 215         if (c.getAttribute(idDeprecated) != null) {
 216             c.modifiers |= M_DEPRECATED;
 217         }
 218 
 219         // Was it synthesized by the compiler?
 220         if (c.getAttribute(idSynthetic) != null) {
 221             c.modifiers |= M_SYNTHETIC;
 222         }
 223 
 224         return c;
 225     }
 226 
 227     /**
 228      * Called when an environment ties a binary definition to a declaration.
 229      * At this point, auxiliary definitions may be loaded.
 230      */
 231 
 232     public void loadNested(Environment env) {
 233         loadNested(env, 0);
 234     }
 235 
 236     public void loadNested(Environment env, int flags) {
 237         // Sanity check.
 238         if (haveLoadedNested) {
 239             // Duplicate calls most likely should not occur, but they do
 240             // in javap.  Be tolerant of them for the time being.
 241             // throw new CompilerError("multiple loadNested");
 242             if (tracing) env.dtEvent("loadNested: DUPLICATE CALL SKIPPED");
 243             return;
 244         }
 245         haveLoadedNested = true;
 246         // Read class-nesting information.
 247         try {
 248             byte data[];
 249             data = getAttribute(idInnerClasses);
 250             if (data != null) {
 251                 initInnerClasses(env, data, flags);
 252             }
 253         } catch (IOException ee) {
 254             // The inner classes attribute is not well-formed.
 255             // It may, for example, contain no data.  Report this.
 256             // We used to throw a CompilerError here (bug 4095108).
 257             env.error(0, "malformed.attribute", getClassDeclaration(),
 258                       idInnerClasses);
 259             if (tracing)
 260                 env.dtEvent("loadNested: MALFORMED ATTRIBUTE (InnerClasses)");
 261         }
 262     }
 263 
 264     private void initInnerClasses(Environment env,
 265                                   byte data[],
 266                                   int flags) throws IOException {
 267         DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
 268         int nrec = ds.readUnsignedShort();  // InnerClasses_attribute.number_of_classes
 269         for (int i = 0; i < nrec; i++) {
 270             // For each inner class name transformation, we have a record
 271             // with the following fields:
 272             //
 273             //    u2 inner_class_info_index;   // CONSTANT_Class_info index
 274             //    u2 outer_class_info_index;   // CONSTANT_Class_info index
 275             //    u2 inner_name_index;         // CONSTANT_Utf8_info index
 276             //    u2 inner_class_access_flags; // access_flags bitmask
 277             //
 278             // The spec states that outer_class_info_index is 0 iff
 279             // the inner class is not a member of its enclosing class (i.e.
 280             // it is a local or anonymous class).  The spec also states
 281             // that if a class is anonymous then inner_name_index should
 282             // be 0.
 283             //
 284             // Prior to jdk1.2, javac did not implement the spec.  Instead
 285             // it <em>always</em> set outer_class_info_index to the
 286             // enclosing outer class and if the class was anonymous,
 287             // it set inner_name_index to be the index of a CONSTANT_Utf8
 288             // entry containing the null string "" (idNull).  This code is
 289             // designed to handle either kind of class file.
 290             //
 291             // See also the compileClass() method in SourceClass.java.
 292 
 293             // Read in the inner_class_info
 294             // InnerClasses_attribute.classes.inner_class_info_index
 295             int inner_index = ds.readUnsignedShort();
 296             // could check for zero.
 297             ClassDeclaration inner = cpool.getDeclaration(env, inner_index);
 298 
 299             // Read in the outer_class_info.  Note that the index will be
 300             // zero if the class is "not a member".
 301             ClassDeclaration outer = null;
 302             // InnerClasses_attribute.classes.outer_class_info_index
 303             int outer_index = ds.readUnsignedShort();
 304             if (outer_index != 0) {
 305                 outer = cpool.getDeclaration(env, outer_index);
 306             }
 307 
 308             // Read in the inner_name_index.  This may be zero.  An anonymous
 309             // class will either have an inner_nm_index of zero (as the spec
 310             // dictates) or it will have an inner_nm of idNull (for classes
 311             // generated by pre-1.2 compilers).  Handle both.
 312             Identifier inner_nm = idNull;
 313             // InnerClasses_attribute.classes.inner_name_index
 314             int inner_nm_index = ds.readUnsignedShort();
 315             if (inner_nm_index != 0) {
 316                 inner_nm = Identifier.lookup(cpool.getString(inner_nm_index));
 317             }
 318 
 319             // Read in the modifiers for the inner class.
 320             // InnerClasses_attribute.classes.inner_name_index
 321             int mods = ds.readUnsignedShort();
 322 
 323             // Is the class accessible?
 324             // The old code checked for
 325             //
 326             //    (!inner_nm.equals(idNull) && (mods & M_PRIVATE) == 0)
 327             //
 328             // which we will preserve to keep it working for class files
 329             // generated by 1.1 compilers.  In addition we check for
 330             //
 331             //    (outer != null)
 332             //
 333             // as an additional check that only makes sense with 1.2
 334             // generated files.  Note that it is entirely possible that
 335             // the M_PRIVATE bit is always enough.  We are being
 336             // conservative here.
 337             //
 338             // The ATT_ALLCLASSES flag causes the M_PRIVATE modifier
 339             // to be ignored, and is used by tools such as 'javap' that
 340             // wish to examine all classes regardless of the normal access
 341             // controls that apply during compilation.  Note that anonymous
 342             // and local classes are still not considered accessible, though
 343             // named local classes in jdk1.1 may slip through.  Note that
 344             // this accessibility test is an optimization, and it is safe to
 345             // err on the side of greater accessibility.
 346             boolean accessible =
 347                 (outer != null) &&
 348                 (!inner_nm.equals(idNull)) &&
 349                 ((mods & M_PRIVATE) == 0 ||
 350                  (flags & ATT_ALLCLASSES) != 0);
 351 
 352             // The reader should note that there has been a significant change
 353             // in the way that the InnerClasses attribute is being handled.
 354             // In particular, previously the compiler called initInner() for
 355             // <em>every</em> inner class.  Now the compiler does not call
 356             // initInner() if the inner class is inaccessible.  This means
 357             // that inaccessible inner classes don't have any of the processing
 358             // from initInner() done for them: fixing the access flags,
 359             // setting outerClass, setting outerMember in their outerClass,
 360             // etc.  We believe this is fine: if the class is inaccessible
 361             // and binary, then everyone who needs to see its internals
 362             // has already been compiled.  Hopefully.
 363 
 364             if (accessible) {
 365                 Identifier nm =
 366                     Identifier.lookupInner(outer.getName(), inner_nm);
 367 
 368                 // Tell the type module about the nesting relation:
 369                 Type.tClass(nm);
 370 
 371                 if (inner.equals(getClassDeclaration())) {
 372                     // The inner class in the record is this class.
 373                     try {
 374                         ClassDefinition outerClass = outer.getClassDefinition(env);
 375                         initInner(outerClass, mods);
 376                     } catch (ClassNotFound e) {
 377                         // report the error elsewhere
 378                     }
 379                 } else if (outer.equals(getClassDeclaration())) {
 380                     // The outer class in the record is this class.
 381                     try {
 382                         ClassDefinition innerClass =
 383                             inner.getClassDefinition(env);
 384                         initOuter(innerClass, mods);
 385                     } catch (ClassNotFound e) {
 386                         // report the error elsewhere
 387                     }
 388                 }
 389             }
 390         }
 391     }
 392 
 393     private void initInner(ClassDefinition outerClass, int mods) {
 394         if (getOuterClass() != null)
 395             return;             // already done
 396         /******
 397         // Maybe set static, protected, or private.
 398         if ((modifiers & M_PUBLIC) != 0)
 399             mods &= M_STATIC;
 400         else
 401             mods &= M_PRIVATE | M_PROTECTED | M_STATIC;
 402         modifiers |= mods;
 403         ******/
 404         // For an inner class, the class access may have been weakened
 405         // from that originally declared the source.  We must take the
 406         // actual access permissions against which we check any source
 407         // we are currently compiling from the InnerClasses attribute.
 408         // We attempt to guard here against bogus combinations of modifiers.
 409         if ((mods & M_PRIVATE) != 0) {
 410             // Private cannot be combined with public or protected.
 411             mods &= ~(M_PUBLIC | M_PROTECTED);
 412         } else if ((mods & M_PROTECTED) != 0) {
 413             // Protected cannot be combined with public.
 414             mods &= ~M_PUBLIC;
 415         }
 416         if ((mods & M_INTERFACE) != 0) {
 417             // All interfaces are implicitly abstract.
 418             // All interfaces that are members of a type are implicitly static.
 419             mods |= (M_ABSTRACT | M_STATIC);
 420         }
 421         if (outerClass.isInterface()) {
 422             // All types that are members of interfaces are implicitly
 423             // public and static.
 424             mods |= (M_PUBLIC | M_STATIC);
 425             mods &= ~(M_PRIVATE | M_PROTECTED);
 426         }
 427         modifiers = mods;
 428 
 429         setOuterClass(outerClass);
 430 
 431         for (MemberDefinition field = getFirstMember();
 432              field != null;
 433              field = field.getNextMember()) {
 434             if (field.isUplevelValue()
 435                     && outerClass.getType().equals(field.getType())
 436                     && field.getName().toString().startsWith(prefixThis)) {
 437                 setOuterMember(field);
 438             }
 439         }
 440     }
 441 
 442     private void initOuter(ClassDefinition innerClass, int mods) {
 443         if (innerClass instanceof BinaryClass)
 444             ((BinaryClass)innerClass).initInner(this, mods);
 445         addMember(new BinaryMember(innerClass));
 446     }
 447 
 448     /**
 449      * Write the class out to a given stream.  This function mirrors the loader.
 450      */
 451     public void write(Environment env, OutputStream out) throws IOException {
 452         DataOutputStream data = new DataOutputStream(out);
 453 
 454         // write out the header
 455         data.writeInt(JAVA_MAGIC);
 456         data.writeShort(env.getMinorVersion());
 457         data.writeShort(env.getMajorVersion());
 458 
 459         // Write out the constant pool
 460         cpool.write(data, env);
 461 
 462         // Write class information
 463         data.writeShort(getModifiers() & ACCM_CLASS);
 464         data.writeShort(cpool.indexObject(getClassDeclaration(), env));
 465         data.writeShort((getSuperClass() != null)
 466                         ? cpool.indexObject(getSuperClass(), env) : 0);
 467         data.writeShort(interfaces.length);
 468         for (int i = 0 ; i < interfaces.length ; i++) {
 469             data.writeShort(cpool.indexObject(interfaces[i], env));
 470         }
 471 
 472         // count the fields and the methods
 473         int fieldCount = 0, methodCount = 0;
 474         for (MemberDefinition f = firstMember; f != null; f = f.getNextMember())
 475             if (f.isMethod()) methodCount++; else fieldCount++;
 476 
 477         // write out each the field count, and then each field
 478         data.writeShort(fieldCount);
 479         for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
 480             if (!f.isMethod()) {
 481                 data.writeShort(f.getModifiers() & ACCM_FIELD);
 482                 String name = f.getName().toString();
 483                 String signature = f.getType().getTypeSignature();
 484                 data.writeShort(cpool.indexString(name, env));
 485                 data.writeShort(cpool.indexString(signature, env));
 486                 BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
 487             }
 488         }
 489 
 490         // write out each method count, and then each method
 491         data.writeShort(methodCount);
 492         for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
 493             if (f.isMethod()) {
 494                 data.writeShort(f.getModifiers() & ACCM_METHOD);
 495                 String name = f.getName().toString();
 496                 String signature = f.getType().getTypeSignature();
 497                 data.writeShort(cpool.indexString(name, env));
 498                 data.writeShort(cpool.indexString(signature, env));
 499                 BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
 500             }
 501         }
 502 
 503         // write out the class attributes
 504         BinaryAttribute.write(atts, data, cpool, env);
 505         data.flush();
 506     }
 507 
 508     /**
 509      * Get the dependencies
 510      */
 511     public Enumeration<ClassDeclaration> getDependencies() {
 512         return dependencies.elements();
 513     }
 514 
 515     /**
 516      * Add a dependency
 517      */
 518     public void addDependency(ClassDeclaration c) {
 519         if ((c != null) && !dependencies.contains(c)) {
 520             dependencies.addElement(c);
 521         }
 522     }
 523 
 524     /**
 525      * Get the constant pool
 526      */
 527     public BinaryConstantPool getConstants() {
 528         return cpool;
 529     }
 530 
 531     /**
 532      * Get a class attribute
 533      */
 534     public byte[] getAttribute(Identifier name) {
 535         for (BinaryAttribute att = atts ; att != null ; att = att.next) {
 536             if (att.name.equals(name)) {
 537                 return att.data;
 538             }
 539         }
 540         return null;
 541     }
 542 }