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 26package sun.tools.java; 27 28import java.io.IOException; 29import java.io.DataInputStream; 30import java.io.OutputStream; 31import java.io.DataOutputStream; 32import java.io.ByteArrayInputStream; 33import java.util.Hashtable; 34import java.util.Vector; 35import 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") 43public final 44class 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} 536