1 /* 2 * Copyright (c) 2001, 2013, 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 com.sun.java.util.jar.pack; 27 28 import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; 29 import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; 30 import com.sun.java.util.jar.pack.ConstantPool.Entry; 31 import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; 32 import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; 33 import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; 34 import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; 35 import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; 36 import com.sun.java.util.jar.pack.Package.Class; 37 import com.sun.java.util.jar.pack.Package.InnerClass; 38 import java.io.DataInputStream; 39 import java.io.FilterInputStream; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Map; 45 import static com.sun.java.util.jar.pack.Constants.*; 46 47 /** 48 * Reader for a class file that is being incorporated into a package. 49 * @author John Rose 50 */ 51 class ClassReader { 52 int verbose; 53 54 Package pkg; 55 Class cls; 56 long inPos; 57 long constantPoolLimit = -1; 58 DataInputStream in; 59 Map<Attribute.Layout, Attribute> attrDefs; 60 Map<Attribute.Layout, String> attrCommands; 61 String unknownAttrCommand = "error";; 62 63 ClassReader(Class cls, InputStream in) throws IOException { 64 this.pkg = cls.getPackage(); 65 this.cls = cls; 66 this.verbose = pkg.verbose; 67 this.in = new DataInputStream(new FilterInputStream(in) { 68 public int read(byte b[], int off, int len) throws IOException { 69 int nr = super.read(b, off, len); 70 if (nr >= 0) inPos += nr; 71 return nr; 72 } 73 public int read() throws IOException { 74 int ch = super.read(); 75 if (ch >= 0) inPos += 1; 76 return ch; 77 } 78 public long skip(long n) throws IOException { 79 long ns = super.skip(n); 80 if (ns >= 0) inPos += ns; 81 return ns; 82 } 83 }); 84 } 85 86 public void setAttrDefs(Map<Attribute.Layout, Attribute> attrDefs) { 87 this.attrDefs = attrDefs; 88 } 89 90 public void setAttrCommands(Map<Attribute.Layout, String> attrCommands) { 91 this.attrCommands = attrCommands; 92 } 93 94 private void skip(int n, String what) throws IOException { 95 Utils.log.warning("skipping "+n+" bytes of "+what); 96 long skipped = 0; 97 while (skipped < n) { 98 long j = in.skip(n - skipped); 99 assert(j > 0); 100 skipped += j; 101 } 102 assert(skipped == n); 103 } 104 105 private int readUnsignedShort() throws IOException { 106 return in.readUnsignedShort(); 107 } 108 109 private int readInt() throws IOException { 110 return in.readInt(); 111 } 112 113 /** Read a 2-byte int, and return the <em>global</em> CP entry for it. */ 114 private Entry readRef() throws IOException { 115 int i = in.readUnsignedShort(); 116 return i == 0 ? null : cls.cpMap[i]; 117 } 118 119 private Entry readRef(byte tag) throws IOException { 120 Entry e = readRef(); 121 assert(!(e instanceof UnresolvedEntry)); 122 checkTag(e, tag); 123 return e; 124 } 125 126 /** Throw a ClassFormatException if the entry does not match the expected tag type. */ 127 private Entry checkTag(Entry e, byte tag) throws ClassFormatException { 128 if (e == null || !e.tagMatches(tag)) { 129 String where = (inPos == constantPoolLimit 130 ? " in constant pool" 131 : " at pos: " + inPos); 132 String got = (e == null 133 ? "null CP index" 134 : "type=" + ConstantPool.tagName(e.tag)); 135 throw new ClassFormatException("Bad constant, expected type=" + 136 ConstantPool.tagName(tag) + 137 " got "+ got + ", in File: " + cls.file.nameString + where); 138 } 139 return e; 140 } 141 private Entry checkTag(Entry e, byte tag, boolean nullOK) throws ClassFormatException { 142 return nullOK && e == null ? null : checkTag(e, tag); 143 } 144 145 private Entry readRefOrNull(byte tag) throws IOException { 146 Entry e = readRef(); 147 checkTag(e, tag, true); 148 return e; 149 } 150 151 private Utf8Entry readUtf8Ref() throws IOException { 152 return (Utf8Entry) readRef(CONSTANT_Utf8); 153 } 154 155 private ClassEntry readClassRef() throws IOException { 156 return (ClassEntry) readRef(CONSTANT_Class); 157 } 158 159 private ClassEntry readClassRefOrNull() throws IOException { 160 return (ClassEntry) readRefOrNull(CONSTANT_Class); 161 } 162 163 private SignatureEntry readSignatureRef() throws IOException { 164 // The class file stores a Utf8, but we want a Signature. 165 Entry e = readRef(CONSTANT_Signature); 166 return (e != null && e.getTag() == CONSTANT_Utf8) 167 ? ConstantPool.getSignatureEntry(e.stringValue()) 168 : (SignatureEntry) e; 169 } 170 171 void read() throws IOException { 172 boolean ok = false; 173 try { 174 readMagicNumbers(); 175 readConstantPool(); 176 readHeader(); 177 readMembers(false); // fields 178 readMembers(true); // methods 179 readAttributes(ATTR_CONTEXT_CLASS, cls); 180 fixUnresolvedEntries(); 181 cls.finishReading(); 182 assert(0 >= in.read(new byte[1])); 183 ok = true; 184 } finally { 185 if (!ok) { 186 if (verbose > 0) Utils.log.warning("Erroneous data at input offset "+inPos+" of "+cls.file); 187 } 188 } 189 } 190 191 void readMagicNumbers() throws IOException { 192 cls.magic = in.readInt(); 193 if (cls.magic != JAVA_MAGIC) 194 throw new Attribute.FormatException 195 ("Bad magic number in class file " 196 +Integer.toHexString(cls.magic), 197 ATTR_CONTEXT_CLASS, "magic-number", "pass"); 198 int minver = (short) readUnsignedShort(); 199 int majver = (short) readUnsignedShort(); 200 cls.version = Package.Version.of(majver, minver); 201 202 //System.out.println("ClassFile.version="+cls.majver+"."+cls.minver); 203 String bad = checkVersion(cls.version); 204 if (bad != null) { 205 throw new Attribute.FormatException 206 ("classfile version too "+bad+": " 207 +cls.version+" in "+cls.file, 208 ATTR_CONTEXT_CLASS, "version", "pass"); 209 } 210 } 211 212 private String checkVersion(Package.Version ver) { 213 int majver = ver.major; 214 int minver = ver.minor; 215 if (majver < pkg.minClassVersion.major || 216 (majver == pkg.minClassVersion.major && 217 minver < pkg.minClassVersion.minor)) { 218 return "small"; 219 } 220 if (majver > pkg.maxClassVersion.major || 221 (majver == pkg.maxClassVersion.major && 222 minver > pkg.maxClassVersion.minor)) { 223 return "large"; 224 } 225 return null; // OK 226 } 227 228 void readConstantPool() throws IOException { 229 int length = in.readUnsignedShort(); 230 //System.err.println("reading CP, length="+length); 231 232 int[] fixups = new int[length*4]; 233 int fptr = 0; 234 235 Entry[] cpMap = new Entry[length]; 236 cpMap[0] = null; 237 for (int i = 1; i < length; i++) { 238 //System.err.println("reading CP elt, i="+i); 239 int tag = in.readByte(); 240 switch (tag) { 241 case CONSTANT_Utf8: 242 cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF()); 243 break; 244 case CONSTANT_Integer: 245 { 246 cpMap[i] = ConstantPool.getLiteralEntry(in.readInt()); 247 } 248 break; 249 case CONSTANT_Float: 250 { 251 cpMap[i] = ConstantPool.getLiteralEntry(in.readFloat()); 252 } 253 break; 254 case CONSTANT_Long: 255 { 256 cpMap[i] = ConstantPool.getLiteralEntry(in.readLong()); 257 cpMap[++i] = null; 258 } 259 break; 260 case CONSTANT_Double: 261 { 262 cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble()); 263 cpMap[++i] = null; 264 } 265 break; 266 267 // just read the refs; do not attempt to resolve while reading 268 case CONSTANT_Class: 269 case CONSTANT_String: 270 case CONSTANT_MethodType: 271 fixups[fptr++] = i; 272 fixups[fptr++] = tag; 273 fixups[fptr++] = in.readUnsignedShort(); 274 fixups[fptr++] = -1; // empty ref2 275 break; 276 case CONSTANT_Fieldref: 277 case CONSTANT_Methodref: 278 case CONSTANT_InterfaceMethodref: 279 case CONSTANT_NameandType: 280 fixups[fptr++] = i; 281 fixups[fptr++] = tag; 282 fixups[fptr++] = in.readUnsignedShort(); 283 fixups[fptr++] = in.readUnsignedShort(); 284 break; 285 case CONSTANT_InvokeDynamic: 286 fixups[fptr++] = i; 287 fixups[fptr++] = tag; 288 fixups[fptr++] = -1 ^ in.readUnsignedShort(); // not a ref 289 fixups[fptr++] = in.readUnsignedShort(); 290 break; 291 case CONSTANT_MethodHandle: 292 fixups[fptr++] = i; 293 fixups[fptr++] = tag; 294 fixups[fptr++] = -1 ^ in.readUnsignedByte(); 295 fixups[fptr++] = in.readUnsignedShort(); 296 break; 297 default: 298 throw new ClassFormatException("Bad constant pool tag " + 299 tag + " in File: " + cls.file.nameString + 300 " at pos: " + inPos); 301 } 302 } 303 constantPoolLimit = inPos; 304 305 // Fix up refs, which might be out of order. 306 while (fptr > 0) { 307 if (verbose > 3) 308 Utils.log.fine("CP fixups ["+fptr/4+"]"); 309 int flimit = fptr; 310 fptr = 0; 311 for (int fi = 0; fi < flimit; ) { 312 int cpi = fixups[fi++]; 313 int tag = fixups[fi++]; 314 int ref = fixups[fi++]; 315 int ref2 = fixups[fi++]; 316 if (verbose > 3) 317 Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}"); 318 if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) { 319 // Defer. 320 fixups[fptr++] = cpi; 321 fixups[fptr++] = tag; 322 fixups[fptr++] = ref; 323 fixups[fptr++] = ref2; 324 continue; 325 } 326 switch (tag) { 327 case CONSTANT_Class: 328 cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref].stringValue()); 329 break; 330 case CONSTANT_String: 331 cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref].stringValue()); 332 break; 333 case CONSTANT_Fieldref: 334 case CONSTANT_Methodref: 335 case CONSTANT_InterfaceMethodref: 336 ClassEntry mclass = (ClassEntry) checkTag(cpMap[ref], CONSTANT_Class); 337 DescriptorEntry mdescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType); 338 cpMap[cpi] = ConstantPool.getMemberEntry((byte)tag, mclass, mdescr); 339 break; 340 case CONSTANT_NameandType: 341 Utf8Entry mname = (Utf8Entry) checkTag(cpMap[ref], CONSTANT_Utf8); 342 Utf8Entry mtype = (Utf8Entry) checkTag(cpMap[ref2], CONSTANT_Signature); 343 cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype); 344 break; 345 case CONSTANT_MethodType: 346 cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) checkTag(cpMap[ref], CONSTANT_Signature)); 347 break; 348 case CONSTANT_MethodHandle: 349 byte refKind = (byte)(-1 ^ ref); 350 MemberEntry memRef = (MemberEntry) checkTag(cpMap[ref2], CONSTANT_AnyMember); 351 cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef); 352 break; 353 case CONSTANT_InvokeDynamic: 354 DescriptorEntry idescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType); 355 cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr); 356 // Note that ref must be resolved later, using the BootstrapMethods attribute. 357 break; 358 default: 359 assert(false); 360 } 361 } 362 assert(fptr < flimit); // Must make progress. 363 } 364 365 cls.cpMap = cpMap; 366 } 367 368 private /*non-static*/ 369 class UnresolvedEntry extends Entry { 370 final Object[] refsOrIndexes; 371 UnresolvedEntry(byte tag, Object... refsOrIndexes) { 372 super(tag); 373 this.refsOrIndexes = refsOrIndexes; 374 ClassReader.this.haveUnresolvedEntry = true; 375 } 376 Entry resolve() { 377 Class cls = ClassReader.this.cls; 378 Entry res; 379 switch (tag) { 380 case CONSTANT_InvokeDynamic: 381 BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]); 382 DescriptorEntry idescr = (DescriptorEntry) refsOrIndexes[1]; 383 res = ConstantPool.getInvokeDynamicEntry(iboots, idescr); 384 break; 385 default: 386 throw new AssertionError(); 387 } 388 return res; 389 } 390 private void unresolved() { throw new RuntimeException("unresolved entry has no string"); } 391 public int compareTo(Object x) { unresolved(); return 0; } 392 public boolean equals(Object x) { unresolved(); return false; } 393 protected int computeValueHash() { unresolved(); return 0; } 394 public String stringValue() { unresolved(); return toString(); } 395 public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; } 396 } 397 398 boolean haveUnresolvedEntry; 399 private void fixUnresolvedEntries() { 400 if (!haveUnresolvedEntry) return; 401 Entry[] cpMap = cls.getCPMap(); 402 for (int i = 0; i < cpMap.length; i++) { 403 Entry e = cpMap[i]; 404 if (e instanceof UnresolvedEntry) { 405 cpMap[i] = e = ((UnresolvedEntry)e).resolve(); 406 assert(!(e instanceof UnresolvedEntry)); 407 } 408 } 409 haveUnresolvedEntry = false; 410 } 411 412 void readHeader() throws IOException { 413 cls.flags = readUnsignedShort(); 414 cls.thisClass = readClassRef(); 415 cls.superClass = readClassRefOrNull(); 416 int ni = readUnsignedShort(); 417 cls.interfaces = new ClassEntry[ni]; 418 for (int i = 0; i < ni; i++) { 419 cls.interfaces[i] = readClassRef(); 420 } 421 } 422 423 void readMembers(boolean doMethods) throws IOException { 424 int nm = readUnsignedShort(); 425 for (int i = 0; i < nm; i++) { 426 readMember(doMethods); 427 } 428 } 429 430 void readMember(boolean doMethod) throws IOException { 431 int mflags = readUnsignedShort(); 432 Utf8Entry mname = readUtf8Ref(); 433 SignatureEntry mtype = readSignatureRef(); 434 DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname, mtype); 435 Class.Member m; 436 if (!doMethod) 437 m = cls.new Field(mflags, descr); 438 else 439 m = cls.new Method(mflags, descr); 440 readAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD, 441 m); 442 } 443 void readAttributes(int ctype, Attribute.Holder h) throws IOException { 444 int na = readUnsignedShort(); 445 if (na == 0) return; // nothing to do here 446 if (verbose > 3) 447 Utils.log.fine("readAttributes "+h+" ["+na+"]"); 448 for (int i = 0; i < na; i++) { 449 String name = readUtf8Ref().stringValue(); 450 int length = readInt(); 451 // See if there is a special command that applies. 452 if (attrCommands != null) { 453 Attribute.Layout lkey = Attribute.keyForLookup(ctype, name); 454 String cmd = attrCommands.get(lkey); 455 if (cmd != null) { 456 switch (cmd) { 457 case "pass": 458 String message1 = "passing attribute bitwise in " + h; 459 throw new Attribute.FormatException(message1, ctype, name, cmd); 460 case "error": 461 String message2 = "attribute not allowed in " + h; 462 throw new Attribute.FormatException(message2, ctype, name, cmd); 463 case "strip": 464 skip(length, name + " attribute in " + h); 465 continue; 466 } 467 } 468 } 469 // Find canonical instance of the requested attribute. 470 Attribute a = Attribute.lookup(Package.attrDefs, ctype, name); 471 if (verbose > 4 && a != null) 472 Utils.log.fine("pkg_attribute_lookup "+name+" = "+a); 473 if (a == null) { 474 a = Attribute.lookup(this.attrDefs, ctype, name); 475 if (verbose > 4 && a != null) 476 Utils.log.fine("this "+name+" = "+a); 477 } 478 if (a == null) { 479 a = Attribute.lookup(null, ctype, name); 480 if (verbose > 4 && a != null) 481 Utils.log.fine("null_attribute_lookup "+name+" = "+a); 482 } 483 if (a == null && length == 0) { 484 // Any zero-length attr is "known"... 485 // We can assume an empty attr. has an empty layout. 486 // Handles markers like Enum, Bridge, Synthetic, Deprecated. 487 a = Attribute.find(ctype, name, ""); 488 } 489 boolean isStackMap = (ctype == ATTR_CONTEXT_CODE 490 && (name.equals("StackMap") || 491 name.equals("StackMapX"))); 492 if (isStackMap) { 493 // Known attribute but with a corner case format, "pass" it. 494 Code code = (Code) h; 495 final int TOO_BIG = 0x10000; 496 if (code.max_stack >= TOO_BIG || 497 code.max_locals >= TOO_BIG || 498 code.getLength() >= TOO_BIG || 499 name.endsWith("X")) { 500 // No, we don't really know what to do with this one. 501 // Do not compress the rare and strange "u4" and "X" cases. 502 a = null; 503 } 504 } 505 if (a == null) { 506 if (isStackMap) { 507 // Known attribute but w/o a format; pass it. 508 String message = "unsupported StackMap variant in "+h; 509 throw new Attribute.FormatException(message, ctype, name, 510 "pass"); 511 } else if ("strip".equals(unknownAttrCommand)) { 512 // Skip the unknown attribute. 513 skip(length, "unknown "+name+" attribute in "+h); 514 continue; 515 } else { 516 String message = " is unknown attribute in class " + h; 517 throw new Attribute.FormatException(message, ctype, name, 518 unknownAttrCommand); 519 } 520 } 521 long pos0 = inPos; // in case we want to check it 522 if (a.layout() == Package.attrCodeEmpty) { 523 // These are hardwired. 524 Class.Method m = (Class.Method) h; 525 m.code = new Code(m); 526 try { 527 readCode(m.code); 528 } catch (Instruction.FormatException iie) { 529 String message = iie.getMessage() + " in " + h; 530 throw new ClassReader.ClassFormatException(message, iie); 531 } 532 assert(length == inPos - pos0); 533 // Keep empty attribute a... 534 } else if (a.layout() == Package.attrBootstrapMethodsEmpty) { 535 assert(h == cls); 536 readBootstrapMethods(cls); 537 assert(length == inPos - pos0); 538 // Delete the attribute; it is logically part of the constant pool. 539 continue; 540 } else if (a.layout() == Package.attrInnerClassesEmpty) { 541 // These are hardwired also. 542 assert(h == cls); 543 readInnerClasses(cls); 544 assert(length == inPos - pos0); 545 // Keep empty attribute a... 546 } else if (length > 0) { 547 byte[] bytes = new byte[length]; 548 in.readFully(bytes); 549 a = a.addContent(bytes); 550 } 551 if (a.size() == 0 && !a.layout().isEmpty()) { 552 throw new ClassFormatException(name + 553 ": attribute length cannot be zero, in " + h); 554 } 555 h.addAttribute(a); 556 if (verbose > 2) 557 Utils.log.fine("read "+a); 558 } 559 } 560 561 void readCode(Code code) throws IOException { 562 code.max_stack = readUnsignedShort(); 563 code.max_locals = readUnsignedShort(); 564 code.bytes = new byte[readInt()]; 565 in.readFully(code.bytes); 566 Entry[] cpMap = cls.getCPMap(); 567 Instruction.opcodeChecker(code.bytes, cpMap, this.cls.version); 568 int nh = readUnsignedShort(); 569 code.setHandlerCount(nh); 570 for (int i = 0; i < nh; i++) { 571 code.handler_start[i] = readUnsignedShort(); 572 code.handler_end[i] = readUnsignedShort(); 573 code.handler_catch[i] = readUnsignedShort(); 574 code.handler_class[i] = readClassRefOrNull(); 575 } 576 readAttributes(ATTR_CONTEXT_CODE, code); 577 } 578 579 void readBootstrapMethods(Class cls) throws IOException { 580 BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()]; 581 for (int i = 0; i < bsms.length; i++) { 582 MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle); 583 Entry[] argRefs = new Entry[readUnsignedShort()]; 584 for (int j = 0; j < argRefs.length; j++) { 585 argRefs[j] = readRef(CONSTANT_LoadableValue); 586 } 587 bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs); 588 } 589 cls.setBootstrapMethods(Arrays.asList(bsms)); 590 } 591 592 void readInnerClasses(Class cls) throws IOException { 593 int nc = readUnsignedShort(); 594 ArrayList<InnerClass> ics = new ArrayList<>(nc); 595 for (int i = 0; i < nc; i++) { 596 InnerClass ic = 597 new InnerClass(readClassRef(), 598 readClassRefOrNull(), 599 (Utf8Entry)readRefOrNull(CONSTANT_Utf8), 600 readUnsignedShort()); 601 ics.add(ic); 602 } 603 cls.innerClasses = ics; // set directly; do not use setInnerClasses. 604 // (Later, ics may be transferred to the pkg.) 605 } 606 607 static class ClassFormatException extends IOException { 608 private static final long serialVersionUID = -3564121733989501833L; 609 610 public ClassFormatException(String message) { 611 super(message); 612 } 613 614 public ClassFormatException(String message, Throwable cause) { 615 super(message, cause); 616 } 617 } 618 }