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 }