1 /*
   2  * Copyright (c) 1994, 2015, 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_MAX_SUPPORTED_VERSION)
 126                      || (version == JAVA_MAX_SUPPORTED_VERSION
 127                   && minor_version > JAVA_MAX_SUPPORTED_MINOR_VERSION)) {
 128             throw new ClassFormatError(
 129                            sun.tools.javac.Main.getText(
 130                                "javac.err.version.too.recent",
 131                                version+"."+minor_version));
 132         }
 133 
 134         // Read the constant pool
 135         BinaryConstantPool cpool = new BinaryConstantPool(in);
 136 
 137         // The dependencies of this class
 138         Vector<ClassDeclaration> dependencies = cpool.getDependencies(env);
 139 
 140         // Read modifiers
 141         int classMod = in.readUnsignedShort() & ACCM_CLASS;  // JVM 4.1 ClassFile.access_flags
 142 
 143         // Read the class name - from JVM 4.1 ClassFile.this_class
 144         ClassDeclaration classDecl = cpool.getDeclaration(env, in.readUnsignedShort());
 145 
 146         // Read the super class name (may be null) - from JVM 4.1 ClassFile.super_class
 147         ClassDeclaration superClassDecl = cpool.getDeclaration(env, in.readUnsignedShort());
 148 
 149         // Read the interface names - from JVM 4.1 ClassFile.interfaces_count
 150         ClassDeclaration interfaces[] = new ClassDeclaration[in.readUnsignedShort()];
 151         for (int i = 0 ; i < interfaces.length ; i++) {
 152             // JVM 4.1 ClassFile.interfaces[]
 153             interfaces[i] = cpool.getDeclaration(env, in.readUnsignedShort());
 154         }
 155 
 156         // Allocate the class
 157         BinaryClass c = new BinaryClass(null, classDecl, classMod, superClassDecl,
 158                                         interfaces, dependencies);
 159         c.cpool = cpool;
 160 
 161         // Add any additional dependencies
 162         c.addDependency(superClassDecl);
 163 
 164         // Read the fields
 165         int nfields = in.readUnsignedShort();  // JVM 4.1 ClassFile.fields_count
 166         for (int i = 0 ; i < nfields ; i++) {
 167             // JVM 4.5 field_info.access_flags
 168             int fieldMod = in.readUnsignedShort() & ACCM_FIELD;
 169             // JVM 4.5 field_info.name_index
 170             Identifier fieldName = cpool.getIdentifier(in.readUnsignedShort());
 171             // JVM 4.5 field_info.descriptor_index
 172             Type fieldType = cpool.getType(in.readUnsignedShort());
 173             BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
 174             c.addMember(new BinaryMember(c, fieldMod, fieldType, fieldName, atts));
 175         }
 176 
 177         // Read the methods
 178         int nmethods = in.readUnsignedShort();  // JVM 4.1 ClassFile.methods_count
 179         for (int i = 0 ; i < nmethods ; i++) {
 180             // JVM 4.6 method_info.access_flags
 181             int methMod = in.readUnsignedShort() & ACCM_METHOD;
 182             // JVM 4.6 method_info.name_index
 183             Identifier methName = cpool.getIdentifier(in.readUnsignedShort());
 184             // JVM 4.6 method_info.descriptor_index
 185             Type methType = cpool.getType(in.readUnsignedShort());
 186             BinaryAttribute atts = BinaryAttribute.load(in, cpool, mask);
 187             c.addMember(new BinaryMember(c, methMod, methType, methName, atts));
 188         }
 189 
 190         // Read the class attributes
 191         c.atts = BinaryAttribute.load(in, cpool, mask);
 192 
 193         // See if the SourceFile is known
 194         byte data[] = c.getAttribute(idSourceFile);
 195         if (data != null) {
 196             DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(data));
 197             // JVM 4.7.2 SourceFile_attribute.sourcefile_index
 198             c.source = cpool.getString(dataStream.readUnsignedShort());
 199         }
 200 
 201         // See if the Documentation is know
 202         data = c.getAttribute(idDocumentation);
 203         if (data != null) {
 204             c.documentation = new DataInputStream(new ByteArrayInputStream(data)).readUTF();
 205         }
 206 
 207         // Was it compiled as deprecated?
 208         if (c.getAttribute(idDeprecated) != null) {
 209             c.modifiers |= M_DEPRECATED;
 210         }
 211 
 212         // Was it synthesized by the compiler?
 213         if (c.getAttribute(idSynthetic) != null) {
 214             c.modifiers |= M_SYNTHETIC;
 215         }
 216 
 217         return c;
 218     }
 219 
 220     /**
 221      * Called when an environment ties a binary definition to a declaration.
 222      * At this point, auxiliary definitions may be loaded.
 223      */
 224 
 225     public void loadNested(Environment env) {
 226         loadNested(env, 0);
 227     }
 228 
 229     public void loadNested(Environment env, int flags) {
 230         // Sanity check.
 231         if (haveLoadedNested) {
 232             // Duplicate calls most likely should not occur, but they do
 233             // in javap.  Be tolerant of them for the time being.
 234             // throw new CompilerError("multiple loadNested");
 235             if (tracing) env.dtEvent("loadNested: DUPLICATE CALL SKIPPED");
 236             return;
 237         }
 238         haveLoadedNested = true;
 239         // Read class-nesting information.
 240         try {
 241             byte data[];
 242             data = getAttribute(idInnerClasses);
 243             if (data != null) {
 244                 initInnerClasses(env, data, flags);
 245             }
 246         } catch (IOException ee) {
 247             // The inner classes attribute is not well-formed.
 248             // It may, for example, contain no data.  Report this.
 249             // We used to throw a CompilerError here (bug 4095108).
 250             env.error(0, "malformed.attribute", getClassDeclaration(),
 251                       idInnerClasses);
 252             if (tracing)
 253                 env.dtEvent("loadNested: MALFORMED ATTRIBUTE (InnerClasses)");
 254         }
 255     }
 256 
 257     private void initInnerClasses(Environment env,
 258                                   byte data[],
 259                                   int flags) throws IOException {
 260         DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
 261         int nrec = ds.readUnsignedShort();  // InnerClasses_attribute.number_of_classes
 262         for (int i = 0; i < nrec; i++) {
 263             // For each inner class name transformation, we have a record
 264             // with the following fields:
 265             //
 266             //    u2 inner_class_info_index;   // CONSTANT_Class_info index
 267             //    u2 outer_class_info_index;   // CONSTANT_Class_info index
 268             //    u2 inner_name_index;         // CONSTANT_Utf8_info index
 269             //    u2 inner_class_access_flags; // access_flags bitmask
 270             //
 271             // The spec states that outer_class_info_index is 0 iff
 272             // the inner class is not a member of its enclosing class (i.e.
 273             // it is a local or anonymous class).  The spec also states
 274             // that if a class is anonymous then inner_name_index should
 275             // be 0.
 276             //
 277             // Prior to jdk1.2, javac did not implement the spec.  Instead
 278             // it <em>always</em> set outer_class_info_index to the
 279             // enclosing outer class and if the class was anonymous,
 280             // it set inner_name_index to be the index of a CONSTANT_Utf8
 281             // entry containing the null string "" (idNull).  This code is
 282             // designed to handle either kind of class file.
 283             //
 284             // See also the compileClass() method in SourceClass.java.
 285 
 286             // Read in the inner_class_info
 287             // InnerClasses_attribute.classes.inner_class_info_index
 288             int inner_index = ds.readUnsignedShort();
 289             // could check for zero.
 290             ClassDeclaration inner = cpool.getDeclaration(env, inner_index);
 291 
 292             // Read in the outer_class_info.  Note that the index will be
 293             // zero if the class is "not a member".
 294             ClassDeclaration outer = null;
 295             // InnerClasses_attribute.classes.outer_class_info_index
 296             int outer_index = ds.readUnsignedShort();
 297             if (outer_index != 0) {
 298                 outer = cpool.getDeclaration(env, outer_index);
 299             }
 300 
 301             // Read in the inner_name_index.  This may be zero.  An anonymous
 302             // class will either have an inner_nm_index of zero (as the spec
 303             // dictates) or it will have an inner_nm of idNull (for classes
 304             // generated by pre-1.2 compilers).  Handle both.
 305             Identifier inner_nm = idNull;
 306             // InnerClasses_attribute.classes.inner_name_index
 307             int inner_nm_index = ds.readUnsignedShort();
 308             if (inner_nm_index != 0) {
 309                 inner_nm = Identifier.lookup(cpool.getString(inner_nm_index));
 310             }
 311 
 312             // Read in the modifiers for the inner class.
 313             // InnerClasses_attribute.classes.inner_name_index
 314             int mods = ds.readUnsignedShort();
 315 
 316             // Is the class accessible?
 317             // The old code checked for
 318             //
 319             //    (!inner_nm.equals(idNull) && (mods & M_PRIVATE) == 0)
 320             //
 321             // which we will preserve to keep it working for class files
 322             // generated by 1.1 compilers.  In addition we check for
 323             //
 324             //    (outer != null)
 325             //
 326             // as an additional check that only makes sense with 1.2
 327             // generated files.  Note that it is entirely possible that
 328             // the M_PRIVATE bit is always enough.  We are being
 329             // conservative here.
 330             //
 331             // The ATT_ALLCLASSES flag causes the M_PRIVATE modifier
 332             // to be ignored, and is used by tools such as 'javap' that
 333             // wish to examine all classes regardless of the normal access
 334             // controls that apply during compilation.  Note that anonymous
 335             // and local classes are still not considered accessible, though
 336             // named local classes in jdk1.1 may slip through.  Note that
 337             // this accessibility test is an optimization, and it is safe to
 338             // err on the side of greater accessibility.
 339             boolean accessible =
 340                 (outer != null) &&
 341                 (!inner_nm.equals(idNull)) &&
 342                 ((mods & M_PRIVATE) == 0 ||
 343                  (flags & ATT_ALLCLASSES) != 0);
 344 
 345             // The reader should note that there has been a significant change
 346             // in the way that the InnerClasses attribute is being handled.
 347             // In particular, previously the compiler called initInner() for
 348             // <em>every</em> inner class.  Now the compiler does not call
 349             // initInner() if the inner class is inaccessible.  This means
 350             // that inaccessible inner classes don't have any of the processing
 351             // from initInner() done for them: fixing the access flags,
 352             // setting outerClass, setting outerMember in their outerClass,
 353             // etc.  We believe this is fine: if the class is inaccessible
 354             // and binary, then everyone who needs to see its internals
 355             // has already been compiled.  Hopefully.
 356 
 357             if (accessible) {
 358                 Identifier nm =
 359                     Identifier.lookupInner(outer.getName(), inner_nm);
 360 
 361                 // Tell the type module about the nesting relation:
 362                 Type.tClass(nm);
 363 
 364                 if (inner.equals(getClassDeclaration())) {
 365                     // The inner class in the record is this class.
 366                     try {
 367                         ClassDefinition outerClass = outer.getClassDefinition(env);
 368                         initInner(outerClass, mods);
 369                     } catch (ClassNotFound e) {
 370                         // report the error elsewhere
 371                     }
 372                 } else if (outer.equals(getClassDeclaration())) {
 373                     // The outer class in the record is this class.
 374                     try {
 375                         ClassDefinition innerClass =
 376                             inner.getClassDefinition(env);
 377                         initOuter(innerClass, mods);
 378                     } catch (ClassNotFound e) {
 379                         // report the error elsewhere
 380                     }
 381                 }
 382             }
 383         }
 384     }
 385 
 386     private void initInner(ClassDefinition outerClass, int mods) {
 387         if (getOuterClass() != null)
 388             return;             // already done
 389         /******
 390         // Maybe set static, protected, or private.
 391         if ((modifiers & M_PUBLIC) != 0)
 392             mods &= M_STATIC;
 393         else
 394             mods &= M_PRIVATE | M_PROTECTED | M_STATIC;
 395         modifiers |= mods;
 396         ******/
 397         // For an inner class, the class access may have been weakened
 398         // from that originally declared the source.  We must take the
 399         // actual access permissions against which we check any source
 400         // we are currently compiling from the InnerClasses attribute.
 401         // We attempt to guard here against bogus combinations of modifiers.
 402         if ((mods & M_PRIVATE) != 0) {
 403             // Private cannot be combined with public or protected.
 404             mods &= ~(M_PUBLIC | M_PROTECTED);
 405         } else if ((mods & M_PROTECTED) != 0) {
 406             // Protected cannot be combined with public.
 407             mods &= ~M_PUBLIC;
 408         }
 409         if ((mods & M_INTERFACE) != 0) {
 410             // All interfaces are implicitly abstract.
 411             // All interfaces that are members of a type are implicitly static.
 412             mods |= (M_ABSTRACT | M_STATIC);
 413         }
 414         if (outerClass.isInterface()) {
 415             // All types that are members of interfaces are implicitly
 416             // public and static.
 417             mods |= (M_PUBLIC | M_STATIC);
 418             mods &= ~(M_PRIVATE | M_PROTECTED);
 419         }
 420         modifiers = mods;
 421 
 422         setOuterClass(outerClass);
 423 
 424         for (MemberDefinition field = getFirstMember();
 425              field != null;
 426              field = field.getNextMember()) {
 427             if (field.isUplevelValue()
 428                     && outerClass.getType().equals(field.getType())
 429                     && field.getName().toString().startsWith(prefixThis)) {
 430                 setOuterMember(field);
 431             }
 432         }
 433     }
 434 
 435     private void initOuter(ClassDefinition innerClass, int mods) {
 436         if (innerClass instanceof BinaryClass)
 437             ((BinaryClass)innerClass).initInner(this, mods);
 438         addMember(new BinaryMember(innerClass));
 439     }
 440 
 441     /**
 442      * Write the class out to a given stream.  This function mirrors the loader.
 443      */
 444     public void write(Environment env, OutputStream out) throws IOException {
 445         DataOutputStream data = new DataOutputStream(out);
 446 
 447         // write out the header
 448         data.writeInt(JAVA_MAGIC);
 449         data.writeShort(env.getMinorVersion());
 450         data.writeShort(env.getMajorVersion());
 451 
 452         // Write out the constant pool
 453         cpool.write(data, env);
 454 
 455         // Write class information
 456         data.writeShort(getModifiers() & ACCM_CLASS);
 457         data.writeShort(cpool.indexObject(getClassDeclaration(), env));
 458         data.writeShort((getSuperClass() != null)
 459                         ? cpool.indexObject(getSuperClass(), env) : 0);
 460         data.writeShort(interfaces.length);
 461         for (int i = 0 ; i < interfaces.length ; i++) {
 462             data.writeShort(cpool.indexObject(interfaces[i], env));
 463         }
 464 
 465         // count the fields and the methods
 466         int fieldCount = 0, methodCount = 0;
 467         for (MemberDefinition f = firstMember; f != null; f = f.getNextMember())
 468             if (f.isMethod()) methodCount++; else fieldCount++;
 469 
 470         // write out each the field count, and then each field
 471         data.writeShort(fieldCount);
 472         for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
 473             if (!f.isMethod()) {
 474                 data.writeShort(f.getModifiers() & ACCM_FIELD);
 475                 String name = f.getName().toString();
 476                 String signature = f.getType().getTypeSignature();
 477                 data.writeShort(cpool.indexString(name, env));
 478                 data.writeShort(cpool.indexString(signature, env));
 479                 BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
 480             }
 481         }
 482 
 483         // write out each method count, and then each method
 484         data.writeShort(methodCount);
 485         for (MemberDefinition f = firstMember; f != null; f = f.getNextMember()) {
 486             if (f.isMethod()) {
 487                 data.writeShort(f.getModifiers() & ACCM_METHOD);
 488                 String name = f.getName().toString();
 489                 String signature = f.getType().getTypeSignature();
 490                 data.writeShort(cpool.indexString(name, env));
 491                 data.writeShort(cpool.indexString(signature, env));
 492                 BinaryAttribute.write(((BinaryMember)f).atts, data, cpool, env);
 493             }
 494         }
 495 
 496         // write out the class attributes
 497         BinaryAttribute.write(atts, data, cpool, env);
 498         data.flush();
 499     }
 500 
 501     /**
 502      * Get the dependencies
 503      */
 504     public Enumeration<ClassDeclaration> getDependencies() {
 505         return dependencies.elements();
 506     }
 507 
 508     /**
 509      * Add a dependency
 510      */
 511     public void addDependency(ClassDeclaration c) {
 512         if ((c != null) && !dependencies.contains(c)) {
 513             dependencies.addElement(c);
 514         }
 515     }
 516 
 517     /**
 518      * Get the constant pool
 519      */
 520     public BinaryConstantPool getConstants() {
 521         return cpool;
 522     }
 523 
 524     /**
 525      * Get a class attribute
 526      */
 527     public byte getAttribute(Identifier name)[] {
 528         for (BinaryAttribute att = atts ; att != null ; att = att.next) {
 529             if (att.name.equals(name)) {
 530                 return att.data;
 531             }
 532         }
 533         return null;
 534     }
 535 }