diff --git a/src/share/classes/com/sun/java/util/jar/pack/Attribute.java b/src/share/classes/com/sun/java/util/jar/pack/Attribute.java index 164870e4a827411acfc75c6595634df08a404659..5cf2456bdf1653393949aa75d9fa1e9ec1b0a437 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/Attribute.java +++ b/src/share/classes/com/sun/java/util/jar/pack/Attribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -166,6 +166,7 @@ class Attribute implements Comparable { define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH"); define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH"); define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]"); + define(sd, ATTR_CONTEXT_CLASS, "BootstrapMethods", "NH[RMHNH[KLH]]"); define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH"); define(sd, ATTR_CONTEXT_FIELD, "Synthetic", ""); @@ -203,6 +204,8 @@ class Attribute implements Comparable { // Their layout specs. are given here for completeness. // The Code spec is incomplete, in that it does not distinguish // bytecode bytes or locate CP references. + // The BootstrapMethods attribute is also special-cased + // elsewhere as an appendix to the local constant pool. } // Metadata. @@ -822,9 +825,9 @@ class Attribute implements Comparable { reference_type: ( constant_ref | schema_ref | utf8_ref | untyped_ref ) constant_ref: - ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' ) + ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' ) schema_ref: - ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' ) + ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' ) utf8_ref: 'RU' untyped_ref: @@ -1012,7 +1015,12 @@ class Attribute implements Comparable { case 'F': e.refKind = CONSTANT_Float; break; case 'D': e.refKind = CONSTANT_Double; break; case 'S': e.refKind = CONSTANT_String; break; - case 'Q': e.refKind = CONSTANT_Literal; break; + case 'Q': e.refKind = CONSTANT_FieldSpecific; break; + + // new in 1.7: + case 'M': e.refKind = CONSTANT_MethodHandle; break; + case 'T': e.refKind = CONSTANT_MethodType; break; + case 'L': e.refKind = CONSTANT_LoadableValue; break; default: { i = -i; continue; } // fail } break; @@ -1029,6 +1037,11 @@ class Attribute implements Comparable { case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref + // new in 1.7: + case 'Y': e.refKind = CONSTANT_InvokeDynamic; break; + case 'B': e.refKind = CONSTANT_BootstrapMethod; break; + case 'N': e.refKind = CONSTANT_AnyMember; break; + default: { i = -i; continue; } // fail } break; @@ -1279,10 +1292,12 @@ class Attribute implements Comparable { // Cf. ClassReader.readSignatureRef. String typeName = globalRef.stringValue(); globalRef = ConstantPool.getSignatureEntry(typeName); - } else if (e.refKind == CONSTANT_Literal) { + } else if (e.refKind == CONSTANT_FieldSpecific) { assert(globalRef.getTag() >= CONSTANT_Integer); - assert(globalRef.getTag() <= CONSTANT_String); - } else if (e.refKind != CONSTANT_All) { + assert(globalRef.getTag() <= CONSTANT_String || + globalRef.getTag() >= CONSTANT_MethodHandle); + assert(globalRef.getTag() <= CONSTANT_MethodType); + } else if (e.refKind < CONSTANT_GroupFirst) { assert(e.refKind == globalRef.getTag()); } } @@ -1462,27 +1477,29 @@ class Attribute implements Comparable { "NH[PHPOHIIH]", // CharacterRangeTable "NH[PHHII]", // CoverageTable "NH[RCHRCNHRUNHFH]", // InnerClasses + "NH[RMHNH[KLH]]", // BootstrapMethods "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code "=AnnotationDefault", // Like metadata, but with a compact tag set: "[NH[(1)]]" - +"[NH[(2)]]" - +"[RSHNH[RUH(3)]]" - +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(2)](6)[NH[(3)]]()[]]", + +"[NH[(1)]]" + +"[RSHNH[RUH(1)]]" + +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]", "" }; ap = 0; } + Utils.currentInstance.set(new PackerImpl()); final int[][] counts = new int[2][3]; // int bci ref final Entry[] cpMap = new Entry[maxVal+1]; for (int i = 0; i < cpMap.length; i++) { if (i == 0) continue; // 0 => null cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i)); } - Class cls = new Package().new Class(""); + Package.Class cls = new Package().new Class(""); cls.cpMap = cpMap; class TestValueStream extends ValueStream { - Random rand = new Random(0); + java.util.Random rand = new java.util.Random(0); ArrayList history = new ArrayList(); int ckidx = 0; int maxVal; @@ -1570,8 +1587,7 @@ class Attribute implements Comparable { String layout = av[i]; if (layout.startsWith("=")) { String name = layout.substring(1); - for (Iterator j = standardDefs.values().iterator(); j.hasNext(); ) { - Attribute a = (Attribute) j.next(); + for (Attribute a : standardDefs.values()) { if (a.name().equals(name)) { layout = a.layout().layout(); break; @@ -1604,7 +1620,7 @@ class Attribute implements Comparable { if (verbose) { System.out.print(" parse: {"); } - self.parse(0, cls, bytes, 0, bytes.length, tts); + self.parse(cls, bytes, 0, bytes.length, tts); if (verbose) { System.out.println("}"); } diff --git a/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java b/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java index 8d472b9bdd41e89bffcdf474ab8fb20740e19bdc..a63491b9a34cfe04b01ab877ff9822cd68a1fd96 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java +++ b/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1372,17 +1372,17 @@ class BandStructure { protected long archiveSize1; // size reported in archive_header protected int archiveNextCount; // reported in archive_header - static final int AH_LENGTH_0 = 3; //minver, majver, options - static final int AH_ARCHIVE_SIZE_HI = 0; - static final int AH_ARCHIVE_SIZE_LO = 1; - static final int AH_LENGTH_S = 2; //optional size hi/lo - static final int AH_LENGTH = 26; // mentioned in spec + static final int AH_LENGTH_0 = 3; // archive_header_0 = {minver, majver, options} + static final int AH_LENGTH_MIN = 15; // observed in spec {header_0[3], cp_counts[8], class_counts[4]} + // Length contributions from optional archive size fields: + static final int AH_LENGTH_S = 2; // archive_header_S = optional {size_hi, size_lo} + static final int AH_ARCHIVE_SIZE_HI = 0; // offset in archive_header_S + static final int AH_ARCHIVE_SIZE_LO = 1; // offset in archive_header_S // Length contributions from optional header fields: - static final int AH_FILE_HEADER_LEN = 5; // sizehi/lo/next/modtime/files - static final int AH_SPECIAL_FORMAT_LEN = 2; // layouts/band-headers - static final int AH_CP_NUMBER_LEN = 4; // int/float/long/double - static final int AH_LENGTH_MIN = AH_LENGTH - -(AH_SPECIAL_FORMAT_LEN+AH_FILE_HEADER_LEN+AH_CP_NUMBER_LEN); + static final int AH_FILE_HEADER_LEN = 5; // file_counts = {{size_hi, size_lo}, next, modtime, files} + static final int AH_SPECIAL_FORMAT_LEN = 2; // special_counts = {layouts, band_headers} + static final int AH_CP_NUMBER_LEN = 4; // cp_number_counts = {int, float, long, double} + static final int AH_CP_EXTRA_LEN = 4; // cp_attr_counts = {MH, MT, InDy, BSM} // Common structure of attribute band groups: static final int AB_FLAGS_HI = 0; @@ -1446,6 +1446,14 @@ class BandStructure { CPRefBand cp_Method_desc = cp_bands.newCPRefBand("cp_Method_desc", UDELTA5, CONSTANT_NameandType); CPRefBand cp_Imethod_class = cp_bands.newCPRefBand("cp_Imethod_class", CONSTANT_Class); CPRefBand cp_Imethod_desc = cp_bands.newCPRefBand("cp_Imethod_desc", UDELTA5, CONSTANT_NameandType); + IntBand cp_MethodHandle_refkind = cp_bands.newIntBand("cp_MethodHandle_refkind", DELTA5); + CPRefBand cp_MethodHandle_member = cp_bands.newCPRefBand("cp_MethodHandle_member", UDELTA5, CONSTANT_AnyMember); + CPRefBand cp_MethodType = cp_bands.newCPRefBand("cp_MethodType", UDELTA5, CONSTANT_Signature); + CPRefBand cp_BootstrapMethod_ref = cp_bands.newCPRefBand("cp_BootstrapMethod_ref", DELTA5, CONSTANT_MethodHandle); + IntBand cp_BootstrapMethod_arg_count = cp_bands.newIntBand("cp_BootstrapMethod_arg_count", UDELTA5); + CPRefBand cp_BootstrapMethod_arg = cp_bands.newCPRefBand("cp_BootstrapMethod_arg", DELTA5, CONSTANT_LoadableValue); + CPRefBand cp_InvokeDynamic_spec = cp_bands.newCPRefBand("cp_InvokeDynamic_spec", DELTA5, CONSTANT_BootstrapMethod); + CPRefBand cp_InvokeDynamic_desc = cp_bands.newCPRefBand("cp_InvokeDynamic_desc", UDELTA5, CONSTANT_NameandType); // bands for carrying attribute definitions: MultiBand attr_definition_bands = all_bands.newMultiBand("(attr_definition_bands)", UNSIGNED5); @@ -1481,7 +1489,7 @@ class BandStructure { IntBand field_attr_calls = field_attr_bands.newIntBand("field_attr_calls"); // bands for predefined field attributes - CPRefBand field_ConstantValue_KQ = field_attr_bands.newCPRefBand("field_ConstantValue_KQ", CONSTANT_Literal); + CPRefBand field_ConstantValue_KQ = field_attr_bands.newCPRefBand("field_ConstantValue_KQ", CONSTANT_FieldSpecific); CPRefBand field_Signature_RS = field_attr_bands.newCPRefBand("field_Signature_RS", CONSTANT_Signature); MultiBand field_metadata_bands = field_attr_bands.newMultiBand("(field_metadata_bands)", UNSIGNED5); @@ -1585,12 +1593,14 @@ class BandStructure { CPRefBand bc_longref = bc_bands.newCPRefBand("bc_longref", DELTA5, CONSTANT_Long); CPRefBand bc_doubleref = bc_bands.newCPRefBand("bc_doubleref", DELTA5, CONSTANT_Double); CPRefBand bc_stringref = bc_bands.newCPRefBand("bc_stringref", DELTA5, CONSTANT_String); + CPRefBand bc_loadablevalueref = bc_bands.newCPRefBand("bc_loadablevalueref", DELTA5, CONSTANT_LoadableValue); // nulls produced by bc_classref are taken to mean the current class CPRefBand bc_classref = bc_bands.newCPRefBand("bc_classref", UNSIGNED5, CONSTANT_Class, NULL_IS_OK); // new, *anew*, c*cast, i*of, ldc CPRefBand bc_fieldref = bc_bands.newCPRefBand("bc_fieldref", DELTA5, CONSTANT_Fieldref); // get*, put* CPRefBand bc_methodref = bc_bands.newCPRefBand("bc_methodref", CONSTANT_Methodref); // invoke[vs]* CPRefBand bc_imethodref = bc_bands.newCPRefBand("bc_imethodref", DELTA5, CONSTANT_InterfaceMethodref); // invokeinterface + CPRefBand bc_indyref = bc_bands.newCPRefBand("bc_indyref", DELTA5, CONSTANT_InvokeDynamic); // invokedynamic // _self_linker_op family CPRefBand bc_thisfield = bc_bands.newCPRefBand("bc_thisfield", CONSTANT_None); // any field within cur. class @@ -1633,7 +1643,7 @@ class BandStructure { protected void setBandIndex(CPRefBand b, byte which) { Object[] need = { b, Byte.valueOf(which) }; - if (which == CONSTANT_Literal) { + if (which == CONSTANT_FieldSpecific) { // I.e., attribute layouts KQ (no null) or KQN (null ok). allKQBands.add(b); } else if (needPredefIndex != null) { @@ -1856,12 +1866,20 @@ class BandStructure { attrClassFileVersionMask = (1< 0) Utils.log.fine("Legacy package version"); // Revoke definition of pre-1.6 attribute type. undefineAttribute(CODE_ATTR_StackMapTable, ATTR_CONTEXT_CODE); } + if (getPackageMajver() < JAVA7_PACKAGE_MAJOR_VERSION) { + if (testBit(archiveOptions, AO_HAVE_CP_EXTRAS)) + // this bit was reserved for future use in previous versions + throw new IOException("Format bits for Java 7 must be zero in previous releases"); + } + if (testBit(archiveOptions, AO_UNUSED_MBZ)) { + throw new IOException("High archive option bits are reserved and must be zero: "+Integer.toHexString(archiveOptions)); + } } protected void initAttrIndexLimit() { @@ -2323,7 +2341,9 @@ class BandStructure { return bc_methodref; case CONSTANT_InterfaceMethodref: return bc_imethodref; - case CONSTANT_Literal: + case CONSTANT_InvokeDynamic: + return bc_indyref; + case CONSTANT_LoadableValue: switch (bc) { case _ildc: case _ildc_w: return bc_intref; @@ -2333,10 +2353,12 @@ class BandStructure { return bc_longref; case _dldc2_w: return bc_doubleref; - case _aldc: case _aldc_w: + case _sldc: case _sldc_w: return bc_stringref; case _cldc: case _cldc_w: return bc_classref; + case _qldc: case _qldc_w: + return bc_loadablevalueref; } break; } @@ -2623,15 +2645,23 @@ class BandStructure { } static void printArrayTo(PrintStream ps, Entry[] cpMap, int start, int end) { + printArrayTo(ps, cpMap, start, end, false); + } + static void printArrayTo(PrintStream ps, Entry[] cpMap, int start, int end, boolean showTags) { StringBuffer buf = new StringBuffer(); int len = end-start; for (int i = 0; i < len; i++) { - String s = cpMap[start+i].stringValue(); + Entry e = cpMap[start+i]; + ps.print(start+i); ps.print("="); + if (showTags) { ps.print(e.tag); ps.print(":"); } + String s = e.stringValue(); buf.setLength(0); for (int j = 0; j < s.length(); j++) { char ch = s.charAt(j); if (!(ch < ' ' || ch > '~' || ch == '\\')) { buf.append(ch); + } else if (ch == '\\') { + buf.append("\\\\"); } else if (ch == '\n') { buf.append("\\n"); } else if (ch == '\t') { @@ -2639,7 +2669,8 @@ class BandStructure { } else if (ch == '\r') { buf.append("\\r"); } else { - buf.append("\\x"+Integer.toHexString(ch)); + String str = "000"+Integer.toHexString(ch); + buf.append("\\u"+str.substring(str.length()-4)); } } ps.println(buf); diff --git a/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java b/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java index ce29f1f3a1908fbd3ac8eaf36f345845762313fd..545faab9127347c75a37d6c8f4500d34612d7a7a 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java +++ b/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,9 @@ import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; import com.sun.java.util.jar.pack.ConstantPool.Entry; import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; +import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; +import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; +import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.InnerClass; @@ -37,6 +40,7 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Map; import static com.sun.java.util.jar.pack.Constants.*; @@ -114,6 +118,7 @@ class ClassReader { private Entry readRef(byte tag) throws IOException { Entry e = readRef(); assert(e != null); + assert(!(e instanceof UnresolvedEntry)); assert(e.tagMatches(tag)); return e; } @@ -151,6 +156,7 @@ class ClassReader { readMembers(false); // fields readMembers(true); // methods readAttributes(ATTR_CONTEXT_CLASS, cls); + fixUnresolvedEntries(); cls.finishReading(); assert(0 >= in.read(new byte[1])); ok = true; @@ -236,6 +242,7 @@ class ClassReader { // just read the refs; do not attempt to resolve while reading case CONSTANT_Class: case CONSTANT_String: + case CONSTANT_MethodType: fixups[fptr++] = i; fixups[fptr++] = tag; fixups[fptr++] = in.readUnsignedShort(); @@ -250,6 +257,18 @@ class ClassReader { fixups[fptr++] = in.readUnsignedShort(); fixups[fptr++] = in.readUnsignedShort(); break; + case CONSTANT_InvokeDynamic: + fixups[fptr++] = i; + fixups[fptr++] = tag; + fixups[fptr++] = -1 ^ in.readUnsignedShort(); // not a ref + fixups[fptr++] = in.readUnsignedShort(); + break; + case CONSTANT_MethodHandle: + fixups[fptr++] = i; + fixups[fptr++] = tag; + fixups[fptr++] = -1 ^ in.readUnsignedByte(); + fixups[fptr++] = in.readUnsignedShort(); + break; default: throw new ClassFormatException("Bad constant pool tag " + tag + " in File: " + cls.file.nameString + @@ -270,7 +289,7 @@ class ClassReader { int ref2 = fixups[fi++]; if (verbose > 3) Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}"); - if (cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) { + if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) { // Defer. fixups[fptr++] = cpi; fixups[fptr++] = tag; @@ -297,6 +316,19 @@ class ClassReader { Utf8Entry mtype = (Utf8Entry) cpMap[ref2]; cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype); break; + case CONSTANT_MethodType: + cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) cpMap[ref]); + break; + case CONSTANT_MethodHandle: + byte refKind = (byte)(-1 ^ ref); + MemberEntry memRef = (MemberEntry) cpMap[ref2]; + cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef); + break; + case CONSTANT_InvokeDynamic: + DescriptorEntry idescr = (DescriptorEntry) cpMap[ref2]; + cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr); + // Note that ref must be resolved later, using the BootstrapMethods attribute. + break; default: assert(false); } @@ -307,6 +339,50 @@ class ClassReader { cls.cpMap = cpMap; } + private /*non-static*/ + class UnresolvedEntry extends Entry { + final Object[] refsOrIndexes; + UnresolvedEntry(byte tag, Object... refsOrIndexes) { + super(tag); + this.refsOrIndexes = refsOrIndexes; + ClassReader.this.haveUnresolvedEntry = true; + } + Entry resolve() { + Class cls = ClassReader.this.cls; + Entry res; + switch (tag) { + case CONSTANT_InvokeDynamic: + BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]); + DescriptorEntry idescr = (DescriptorEntry) refsOrIndexes[1]; + res = ConstantPool.getInvokeDynamicEntry(iboots, idescr); + break; + default: + throw new AssertionError(); + } + return res; + } + private void unresolved() { throw new RuntimeException("unresolved entry has no string"); } + public int compareTo(Object x) { unresolved(); return 0; } + public boolean equals(Object x) { unresolved(); return false; } + protected int computeValueHash() { unresolved(); return 0; } + public String stringValue() { unresolved(); return toString(); } + public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; } + } + + boolean haveUnresolvedEntry; + private void fixUnresolvedEntries() { + if (!haveUnresolvedEntry) return; + Entry[] cpMap = cls.getCPMap(); + for (int i = 0; i < cpMap.length; i++) { + Entry e = cpMap[i]; + if (e instanceof UnresolvedEntry) { + cpMap[i] = e = ((UnresolvedEntry)e).resolve(); + assert(!(e instanceof UnresolvedEntry)); + } + } + haveUnresolvedEntry = false; + } + void readHeader() throws IOException { cls.flags = readUnsignedShort(); cls.thisClass = readClassRef(); @@ -416,25 +492,31 @@ class ClassReader { unknownAttrCommand); } } - if (a.layout() == Package.attrCodeEmpty || - a.layout() == Package.attrInnerClassesEmpty) { + long pos0 = inPos; // in case we want to check it + if (a.layout() == Package.attrCodeEmpty) { // These are hardwired. - long pos0 = inPos; - if ("Code".equals(a.name())) { - Class.Method m = (Class.Method) h; - m.code = new Code(m); - try { - readCode(m.code); - } catch (Instruction.FormatException iie) { - String message = iie.getMessage() + " in " + h; - throw new ClassReader.ClassFormatException(message, iie); - } - } else { - assert(h == cls); - readInnerClasses(cls); + Class.Method m = (Class.Method) h; + m.code = new Code(m); + try { + readCode(m.code); + } catch (Instruction.FormatException iie) { + String message = iie.getMessage() + " in " + h; + throw new ClassReader.ClassFormatException(message, iie); } assert(length == inPos - pos0); // Keep empty attribute a... + } else if (a.layout() == Package.attrBootstrapMethodsEmpty) { + assert(h == cls); + readBootstrapMethods(cls); + assert(length == inPos - pos0); + // Delete the attribute; it is logically part of the constant pool. + continue; + } else if (a.layout() == Package.attrInnerClassesEmpty) { + // These are hardwired also. + assert(h == cls); + readInnerClasses(cls); + assert(length == inPos - pos0); + // Keep empty attribute a... } else if (length > 0) { byte[] bytes = new byte[length]; in.readFully(bytes); @@ -467,6 +549,19 @@ class ClassReader { readAttributes(ATTR_CONTEXT_CODE, code); } + void readBootstrapMethods(Class cls) throws IOException { + BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()]; + for (int i = 0; i < bsms.length; i++) { + MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle); + Entry[] argRefs = new Entry[readUnsignedShort()]; + for (int j = 0; j < argRefs.length; j++) { + argRefs[j] = readRef(); + } + bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs); + } + cls.setBootstrapMethods(Arrays.asList(bsms)); + } + void readInnerClasses(Class cls) throws IOException { int nc = readUnsignedShort(); ArrayList ics = new ArrayList<>(nc); diff --git a/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java b/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java index 37424d1e65ca7005322226a1fe20efe50211b025..dacae44b4d36f12506e1134a8d81a1e47babecda 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java +++ b/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ package com.sun.java.util.jar.pack; import com.sun.java.util.jar.pack.ConstantPool.Entry; import com.sun.java.util.jar.pack.ConstantPool.Index; import com.sun.java.util.jar.pack.ConstantPool.NumberEntry; +import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; +import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.InnerClass; import java.io.BufferedOutputStream; @@ -49,6 +51,7 @@ class ClassWriter { Class cls; DataOutputStream out; Index cpIndex; + Index bsmIndex; ClassWriter(Class cls, OutputStream out) throws IOException { this.pkg = cls.getPackage(); @@ -57,6 +60,10 @@ class ClassWriter { this.out = new DataOutputStream(new BufferedOutputStream(out)); this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap()); this.cpIndex.flattenSigs = true; + if (cls.hasBootstrapMethods()) { + this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods", + cls.getBootstrapMethodMap()); + } if (verbose > 1) Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString())); } @@ -71,6 +78,11 @@ class ClassWriter { /** Write a 2-byte int representing a CP entry, using the local cpIndex. */ private void writeRef(Entry e) throws IOException { + writeRef(e, cpIndex); + } + + /** Write a 2-byte int representing a CP entry, using the given cpIndex. */ + private void writeRef(Entry e, Index cpIndex) throws IOException { int i = (e == null) ? 0 : cpIndex.indexOf(e); writeShort(i); } @@ -117,8 +129,7 @@ class ClassWriter { out.write(tag); switch (tag) { case CONSTANT_Signature: - assert(false); // should not reach here - break; + throw new AssertionError("CP should have Signatures remapped to Utf8"); case CONSTANT_Utf8: out.writeUTF(e.stringValue()); break; @@ -138,8 +149,14 @@ class ClassWriter { break; case CONSTANT_Class: case CONSTANT_String: + case CONSTANT_MethodType: writeRef(e.getRef(0)); break; + case CONSTANT_MethodHandle: + MethodHandleEntry mhe = (MethodHandleEntry) e; + out.writeByte(mhe.refKind); + writeRef(mhe.getRef(0)); + break; case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: @@ -147,6 +164,12 @@ class ClassWriter { writeRef(e.getRef(0)); writeRef(e.getRef(1)); break; + case CONSTANT_InvokeDynamic: + writeRef(e.getRef(0), bsmIndex); + writeRef(e.getRef(1)); + break; + case CONSTANT_BootstrapMethod: + throw new AssertionError("CP should have BootstrapMethods moved to side-table"); default: throw new IOException("Bad constant pool tag "+tag); } @@ -198,6 +221,7 @@ class ClassWriter { a.finishRefs(cpIndex); writeRef(a.getNameRef()); if (a.layout() == Package.attrCodeEmpty || + a.layout() == Package.attrBootstrapMethodsEmpty || a.layout() == Package.attrInnerClassesEmpty) { // These are hardwired. DataOutputStream savedOut = out; @@ -207,9 +231,14 @@ class ClassWriter { if ("Code".equals(a.name())) { Class.Method m = (Class.Method) h; writeCode(m.code); - } else { + } else if ("BootstrapMethods".equals(a.name())) { + assert(h == cls); + writeBootstrapMethods(cls); + } else if ("InnerClasses".equals(a.name())) { assert(h == cls); writeInnerClasses(cls); + } else { + throw new AssertionError(); } out = savedOut; if (verbose > 2) @@ -242,6 +271,18 @@ class ClassWriter { writeAttributes(ATTR_CONTEXT_CODE, code); } + void writeBootstrapMethods(Class cls) throws IOException { + List bsms = cls.getBootstrapMethods(); + writeShort(bsms.size()); + for (BootstrapMethodEntry e : bsms) { + writeRef(e.bsmRef); + writeShort(e.argRefs.length); + for (Entry argRef : e.argRefs) { + writeRef(argRef); + } + } + } + void writeInnerClasses(Class cls) throws IOException { List ics = cls.getInnerClasses(); writeShort(ics.size()); diff --git a/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java b/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java index acade5b7a3d6a53e624b558e7af2a3e16ced45b5..be4da54d2470fe27240809d362075bc47e85005a 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java +++ b/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,7 +52,7 @@ class ConstantPool { * Also used to back up more complex constant pool entries, like Class. */ public static synchronized Utf8Entry getUtf8Entry(String value) { - Map utf8Entries = Utils.getUtf8Entries(); + Map utf8Entries = Utils.getTLGlobals().getUtf8Entries(); Utf8Entry e = utf8Entries.get(value); if (e == null) { e = new Utf8Entry(value); @@ -61,8 +61,8 @@ class ConstantPool { return e; } /** Factory for Class constants. */ - public static synchronized ClassEntry getClassEntry(String name) { - Map classEntries = Utils.getClassEntries(); + public static ClassEntry getClassEntry(String name) { + Map classEntries = Utils.getTLGlobals().getClassEntries(); ClassEntry e = classEntries.get(name); if (e == null) { e = new ClassEntry(getUtf8Entry(name)); @@ -72,8 +72,8 @@ class ConstantPool { return e; } /** Factory for literal constants (String, Integer, etc.). */ - public static synchronized LiteralEntry getLiteralEntry(Comparable value) { - Map literalEntries = Utils.getLiteralEntries(); + public static LiteralEntry getLiteralEntry(Comparable value) { + Map literalEntries = Utils.getTLGlobals().getLiteralEntries(); LiteralEntry e = literalEntries.get(value); if (e == null) { if (value instanceof String) @@ -85,13 +85,13 @@ class ConstantPool { return e; } /** Factory for literal constants (String, Integer, etc.). */ - public static synchronized StringEntry getStringEntry(String value) { + public static StringEntry getStringEntry(String value) { return (StringEntry) getLiteralEntry(value); } /** Factory for signature (type) constants. */ - public static synchronized SignatureEntry getSignatureEntry(String type) { - Map signatureEntries = Utils.getSignatureEntries(); + public static SignatureEntry getSignatureEntry(String type) { + Map signatureEntries = Utils.getTLGlobals().getSignatureEntries(); SignatureEntry e = signatureEntries.get(type); if (e == null) { e = new SignatureEntry(type); @@ -106,8 +106,8 @@ class ConstantPool { } /** Factory for descriptor (name-and-type) constants. */ - public static synchronized DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) { - Map descriptorEntries = Utils.getDescriptorEntries(); + public static DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) { + Map descriptorEntries = Utils.getTLGlobals().getDescriptorEntries(); String key = DescriptorEntry.stringValueOf(nameRef, typeRef); DescriptorEntry e = descriptorEntries.get(key); if (e == null) { @@ -124,8 +124,8 @@ class ConstantPool { } /** Factory for member reference constants. */ - public static synchronized MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) { - Map memberEntries = Utils.getMemberEntries(); + public static MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) { + Map memberEntries = Utils.getTLGlobals().getMemberEntries(); String key = MemberEntry.stringValueOf(tag, classRef, descRef); MemberEntry e = memberEntries.get(key); if (e == null) { @@ -137,6 +137,61 @@ class ConstantPool { return e; } + /** Factory for MethodHandle constants. */ + public static MethodHandleEntry getMethodHandleEntry(byte refKind, MemberEntry memRef) { + Map methodHandleEntries = Utils.getTLGlobals().getMethodHandleEntries(); + String key = MethodHandleEntry.stringValueOf(refKind, memRef); + MethodHandleEntry e = methodHandleEntries.get(key); + if (e == null) { + e = new MethodHandleEntry(refKind, memRef); + assert(e.stringValue().equals(key)); + methodHandleEntries.put(key, e); + } + return e; + } + + /** Factory for MethodType constants. */ + public static MethodTypeEntry getMethodTypeEntry(SignatureEntry sigRef) { + Map methodTypeEntries = Utils.getTLGlobals().getMethodTypeEntries(); + String key = sigRef.stringValue(); + MethodTypeEntry e = methodTypeEntries.get(key); + if (e == null) { + e = new MethodTypeEntry(sigRef); + assert(e.stringValue().equals(key)); + methodTypeEntries.put(key, e); + } + return e; + } + public static MethodTypeEntry getMethodTypeEntry(Utf8Entry typeRef) { + return getMethodTypeEntry(getSignatureEntry(typeRef.stringValue())); + } + + /** Factory for InvokeDynamic constants. */ + public static InvokeDynamicEntry getInvokeDynamicEntry(BootstrapMethodEntry bssRef, DescriptorEntry descRef) { + Map invokeDynamicEntries = Utils.getTLGlobals().getInvokeDynamicEntries(); + String key = InvokeDynamicEntry.stringValueOf(bssRef, descRef); + InvokeDynamicEntry e = invokeDynamicEntries.get(key); + if (e == null) { + e = new InvokeDynamicEntry(bssRef, descRef); + assert(e.stringValue().equals(key)); + invokeDynamicEntries.put(key, e); + } + return e; + } + + /** Factory for BootstrapMethod pseudo-constants. */ + public static BootstrapMethodEntry getBootstrapMethodEntry(MethodHandleEntry bsmRef, Entry[] argRefs) { + Map bootstrapMethodEntries = Utils.getTLGlobals().getBootstrapMethodEntries(); + String key = BootstrapMethodEntry.stringValueOf(bsmRef, argRefs); + BootstrapMethodEntry e = bootstrapMethodEntries.get(key); + if (e == null) { + e = new BootstrapMethodEntry(bsmRef, argRefs); + assert(e.stringValue().equals(key)); + bootstrapMethodEntries.put(key, e); + } + return e; + } + /** Entries in the constant pool. */ public static abstract @@ -251,6 +306,10 @@ class ConstantPool { throw new RuntimeException("bad literal value "+value); } + static boolean isRefKind(byte refKind) { + return (REF_getField <= refKind && refKind <= REF_invokeInterface); + } + public static abstract class LiteralEntry extends Entry { protected LiteralEntry(byte tag) { @@ -404,7 +463,7 @@ class ConstantPool { } static String stringValueOf(Entry nameRef, Entry typeRef) { - return typeRef.stringValue()+","+nameRef.stringValue(); + return qualifiedStringValue(typeRef, nameRef); } public String prettyString() { @@ -420,6 +479,15 @@ class ConstantPool { } } + static String qualifiedStringValue(Entry e1, Entry e2) { + return qualifiedStringValue(e1.stringValue(), e2.stringValue()); + } + static String qualifiedStringValue(String s1, String s234) { + // Qualification by dot must decompose uniquely. Second string might already be qualified. + assert(s1.indexOf(".") < 0); + return s1+"."+s234; + } + public static class MemberEntry extends Entry { final ClassEntry classRef; @@ -453,8 +521,12 @@ class ConstantPool { int x = superCompareTo(o); if (x == 0) { MemberEntry that = (MemberEntry)o; + if (Utils.SORT_MEMBERS_DESCR_MAJOR) + // descRef is transmitted as UDELTA5; sort it first? + x = this.descRef.compareTo(that.descRef); // Primary key is classRef. - x = this.classRef.compareTo(that.classRef); + if (x == 0) + x = this.classRef.compareTo(that.classRef); if (x == 0) x = this.descRef.compareTo(that.descRef); } @@ -473,7 +545,7 @@ class ConstantPool { case CONSTANT_InterfaceMethodref: pfx = "IMethod:"; break; default: pfx = tag+"???"; break; } - return pfx+classRef.stringValue()+","+descRef.stringValue(); + return pfx+qualifiedStringValue(classRef, descRef); } public boolean isMethod() { @@ -581,13 +653,26 @@ class ConstantPool { } public byte getLiteralTag() { switch (formRef.stringValue().charAt(0)) { - case 'L': return CONSTANT_String; case 'I': return CONSTANT_Integer; case 'J': return CONSTANT_Long; case 'F': return CONSTANT_Float; case 'D': return CONSTANT_Double; case 'B': case 'S': case 'C': case 'Z': return CONSTANT_Integer; + case 'L': + /* + switch (classRefs[0].stringValue()) { + case "java/lang/String": + return CONSTANT_String; + case "java/lang/invoke/MethodHandle": + return CONSTANT_MethodHandle; + case "java/lang/invoke/MethodType": + return CONSTANT_MethodType; + default: // java/lang/Object, etc. + return CONSTANT_LoadableValue; + } + */ + return CONSTANT_String; // JDK 7 ConstantValue limited to String } assert(false); return CONSTANT_None; @@ -724,6 +809,218 @@ class ConstantPool { return parts; } + /** @since JDK 7, JSR 292 */ + public static + class MethodHandleEntry extends Entry { + final int refKind; + final MemberEntry memRef; + public Entry getRef(int i) { return i == 0 ? memRef : null; } + + protected int computeValueHash() { + int hc2 = refKind; + return (memRef.hashCode() + (hc2 << 8)) ^ hc2; + } + + MethodHandleEntry(byte refKind, MemberEntry memRef) { + super(CONSTANT_MethodHandle); + assert(isRefKind(refKind)); + this.refKind = refKind; + this.memRef = memRef; + hashCode(); // force computation of valueHash + } + public boolean equals(Object o) { + if (o == null || o.getClass() != MethodHandleEntry.class) { + return false; + } + MethodHandleEntry that = (MethodHandleEntry)o; + return this.refKind == that.refKind + && this.memRef.eq(that.memRef); + } + public int compareTo(Object o) { + int x = superCompareTo(o); + if (x == 0) { + MethodHandleEntry that = (MethodHandleEntry)o; + if (Utils.SORT_HANDLES_KIND_MAJOR) + // Primary key could be refKind. + x = this.refKind - that.refKind; + // Primary key is memRef, which is transmitted as UDELTA5. + if (x == 0) + x = this.memRef.compareTo(that.memRef); + if (x == 0) + x = this.refKind - that.refKind; + } + return x; + } + public static String stringValueOf(int refKind, MemberEntry memRef) { + return refKindName(refKind)+":"+memRef.stringValue(); + } + public String stringValue() { + return stringValueOf(refKind, memRef); + } + } + + /** @since JDK 7, JSR 292 */ + public static + class MethodTypeEntry extends Entry { + final SignatureEntry typeRef; + public Entry getRef(int i) { return i == 0 ? typeRef : null; } + + protected int computeValueHash() { + return typeRef.hashCode() + tag; + } + + MethodTypeEntry(SignatureEntry typeRef) { + super(CONSTANT_MethodType); + this.typeRef = typeRef; + hashCode(); // force computation of valueHash + } + public boolean equals(Object o) { + if (o == null || o.getClass() != MethodTypeEntry.class) { + return false; + } + MethodTypeEntry that = (MethodTypeEntry)o; + return this.typeRef.eq(that.typeRef); + } + public int compareTo(Object o) { + int x = superCompareTo(o); + if (x == 0) { + MethodTypeEntry that = (MethodTypeEntry)o; + x = this.typeRef.compareTo(that.typeRef); + } + return x; + } + public String stringValue() { + return typeRef.stringValue(); + } + } + + /** @since JDK 7, JSR 292 */ + public static + class InvokeDynamicEntry extends Entry { + final BootstrapMethodEntry bssRef; + final DescriptorEntry descRef; + public Entry getRef(int i) { + if (i == 0) return bssRef; + if (i == 1) return descRef; + return null; + } + protected int computeValueHash() { + int hc2 = descRef.hashCode(); + return (bssRef.hashCode() + (hc2 << 8)) ^ hc2; + } + + InvokeDynamicEntry(BootstrapMethodEntry bssRef, DescriptorEntry descRef) { + super(CONSTANT_InvokeDynamic); + this.bssRef = bssRef; + this.descRef = descRef; + hashCode(); // force computation of valueHash + } + public boolean equals(Object o) { + if (o == null || o.getClass() != InvokeDynamicEntry.class) { + return false; + } + InvokeDynamicEntry that = (InvokeDynamicEntry)o; + return this.bssRef.eq(that.bssRef) + && this.descRef.eq(that.descRef); + } + public int compareTo(Object o) { + int x = superCompareTo(o); + if (x == 0) { + InvokeDynamicEntry that = (InvokeDynamicEntry)o; + if (Utils.SORT_INDY_BSS_MAJOR) + // Primary key could be bsmRef. + x = this.bssRef.compareTo(that.bssRef); + // Primary key is descriptor, which is transmitted as UDELTA5. + if (x == 0) + x = this.descRef.compareTo(that.descRef); + if (x == 0) + x = this.bssRef.compareTo(that.bssRef); + } + return x; + } + public String stringValue() { + return stringValueOf(bssRef, descRef); + } + static + String stringValueOf(BootstrapMethodEntry bssRef, DescriptorEntry descRef) { + return "Indy:"+bssRef.stringValue()+"."+descRef.stringValue(); + } + } + + /** @since JDK 7, JSR 292 */ + public static + class BootstrapMethodEntry extends Entry { + final MethodHandleEntry bsmRef; + final Entry[] argRefs; + public Entry getRef(int i) { + if (i == 0) return bsmRef; + if (i-1 < argRefs.length) return argRefs[i-1]; + return null; + } + protected int computeValueHash() { + int hc2 = bsmRef.hashCode(); + return (Arrays.hashCode(argRefs) + (hc2 << 8)) ^ hc2; + } + + BootstrapMethodEntry(MethodHandleEntry bsmRef, Entry[] argRefs) { + super(CONSTANT_BootstrapMethod); + this.bsmRef = bsmRef; + this.argRefs = argRefs.clone(); + hashCode(); // force computation of valueHash + } + public boolean equals(Object o) { + if (o == null || o.getClass() != BootstrapMethodEntry.class) { + return false; + } + BootstrapMethodEntry that = (BootstrapMethodEntry)o; + return this.bsmRef.eq(that.bsmRef) + && Arrays.equals(this.argRefs, that.argRefs); + } + public int compareTo(Object o) { + int x = superCompareTo(o); + if (x == 0) { + BootstrapMethodEntry that = (BootstrapMethodEntry)o; + if (Utils.SORT_BSS_BSM_MAJOR) + // Primary key is bsmRef. + x = this.bsmRef.compareTo(that.bsmRef); + // Primary key is args array length, which is transmitted as UDELTA5. + if (x == 0) + x = compareArgArrays(this.argRefs, that.argRefs); + if (x == 0) + x = this.bsmRef.compareTo(that.bsmRef); + } + return x; + } + public String stringValue() { + return stringValueOf(bsmRef, argRefs); + } + static + String stringValueOf(MethodHandleEntry bsmRef, Entry[] argRefs) { + StringBuffer sb = new StringBuffer(bsmRef.stringValue()); + // Arguments are formatted as "" instead of "[foo,bar,baz]". + // This ensures there will be no confusion if "[,]" appear inside of names. + char nextSep = '<'; + boolean didOne = false; + for (Entry argRef : argRefs) { + sb.append(nextSep).append(argRef.stringValue()); + nextSep = ';'; + } + if (nextSep == '<') sb.append(nextSep); + sb.append('>'); + return sb.toString(); + } + static + int compareArgArrays(Entry[] a1, Entry[] a2) { + int x = a1.length - a2.length; + if (x != 0) return x; + for (int i = 0; i < a1.length; i++) { + x = a1[i].compareTo(a2[i]); + if (x != 0) break; + } + return x; + } + } + // Handy constants: protected static final Entry[] noRefs = {}; protected static final ClassEntry[] noClassRefs = {}; @@ -964,35 +1261,51 @@ class ConstantPool { /** Coherent group of constant pool indexes. */ public static class IndexGroup { - private Index indexUntyped; private Index[] indexByTag = new Index[CONSTANT_Limit]; + private Index[] indexByTagGroup; private int[] untypedFirstIndexByTag; - private int totalSize; + private int totalSizeQQ; private Index[][] indexByTagAndClass; /** Index of all CP entries of all types, in definition order. */ - public Index getUntypedIndex() { - if (indexUntyped == null) { + private Index makeTagGroupIndex(byte tagGroupTag, byte[] tagsInGroup) { + if (indexByTagGroup == null) + indexByTagGroup = new Index[CONSTANT_GroupLimit - CONSTANT_GroupFirst]; + int which = tagGroupTag - CONSTANT_GroupFirst; + assert(indexByTagGroup[which] == null); + int fillp = 0; + Entry[] cpMap = null; + for (int pass = 1; pass <= 2; pass++) { untypedIndexOf(null); // warm up untypedFirstIndexByTag - Entry[] cpMap = new Entry[totalSize]; - for (int tag = 0; tag < indexByTag.length; tag++) { + for (byte tag : tagsInGroup) { Index ix = indexByTag[tag]; if (ix == null) continue; int ixLen = ix.cpMap.length; if (ixLen == 0) continue; - int fillp = untypedFirstIndexByTag[tag]; - assert(cpMap[fillp] == null); - assert(cpMap[fillp+ixLen-1] == null); - System.arraycopy(ix.cpMap, 0, cpMap, fillp, ixLen); + assert(tagGroupTag == CONSTANT_All + ? fillp == untypedFirstIndexByTag[tag] + : fillp < untypedFirstIndexByTag[tag]); + if (cpMap != null) { + assert(cpMap[fillp] == null); + assert(cpMap[fillp+ixLen-1] == null); + System.arraycopy(ix.cpMap, 0, cpMap, fillp, ixLen); + } + fillp += ixLen; + } + if (cpMap == null) { + assert(pass == 1); + // get ready for pass 2 + cpMap = new Entry[fillp]; + fillp = 0; } - indexUntyped = new Index("untyped", cpMap); } - return indexUntyped; + indexByTagGroup[which] = new Index(tagName(tagGroupTag), cpMap); + return indexByTagGroup[which]; } public int untypedIndexOf(Entry e) { if (untypedFirstIndexByTag == null) { - untypedFirstIndexByTag = new int[CONSTANT_Limit]; + untypedFirstIndexByTag = new int[CONSTANT_Limit+1]; int fillp = 0; for (int i = 0; i < TAGS_IN_ORDER.length; i++) { byte tag = TAGS_IN_ORDER[i]; @@ -1002,7 +1315,7 @@ class ConstantPool { untypedFirstIndexByTag[tag] = fillp; fillp += ixLen; } - totalSize = fillp; + untypedFirstIndexByTag[CONSTANT_Limit] = fillp; } if (e == null) return -1; int tag = e.tag; @@ -1028,16 +1341,15 @@ class ConstantPool { indexByTag[tag] = ix; // decache indexes derived from this one: untypedFirstIndexByTag = null; - indexUntyped = null; + indexByTagGroup = null; if (indexByTagAndClass != null) indexByTagAndClass[tag] = null; } /** Index of all CP entries of a given tag. */ public Index getIndexByTag(byte tag) { - if (tag == CONSTANT_All) { - return getUntypedIndex(); - } + if (tag >= CONSTANT_GroupFirst) + return getIndexByTagGroup(tag); Index ix = indexByTag[tag]; if (ix == null) { // Make an empty one by default. @@ -1047,6 +1359,26 @@ class ConstantPool { return ix; } + private Index getIndexByTagGroup(byte tag) { + // pool groups: + if (indexByTagGroup != null) { + Index ix = indexByTagGroup[tag - CONSTANT_GroupFirst]; + if (ix != null) return ix; + } + switch (tag) { + case CONSTANT_All: + return makeTagGroupIndex(CONSTANT_All, TAGS_IN_ORDER); + case CONSTANT_LoadableValue: + return makeTagGroupIndex(CONSTANT_LoadableValue, LOADABLE_VALUE_TAGS); + case CONSTANT_AnyMember: + return makeTagGroupIndex(CONSTANT_AnyMember, ANY_MEMBER_TAGS); + case CONSTANT_FieldSpecific: + // This one does not have any fixed index, since it is context-specific. + return null; + } + throw new AssertionError("bad tag group "+tag); + } + /** Index of all CP entries of a given tag and class. */ public Index getMemberIndex(byte tag, ClassEntry classRef) { if (indexByTagAndClass == null) @@ -1107,16 +1439,14 @@ class ConstantPool { } public boolean haveNumbers() { - for (byte tag = CONSTANT_Integer; tag <= CONSTANT_Double; tag++) { - switch (tag) { - case CONSTANT_Integer: - case CONSTANT_Float: - case CONSTANT_Long: - case CONSTANT_Double: - break; - default: - assert(false); - } + for (byte tag : NUMBER_TAGS) { + if (getIndexByTag(tag).size() > 0) return true; + } + return false; + } + + public boolean haveExtraTags() { + for (byte tag : EXTRA_TAGS) { if (getIndexByTag(tag).size() > 0) return true; } return false; @@ -1129,8 +1459,13 @@ class ConstantPool { * by their equivalent Utf8s. * Also, discard null from cpRefs. */ + public static void completeReferencesIn(Set cpRefs, boolean flattenSigs) { + completeReferencesIn(cpRefs, flattenSigs, null); + } + public static - void completeReferencesIn(Set cpRefs, boolean flattenSigs) { + void completeReferencesIn(Set cpRefs, boolean flattenSigs, + Listbsms) { cpRefs.remove(null); for (ListIterator work = new ArrayList<>(cpRefs).listIterator(cpRefs.size()); @@ -1146,6 +1481,14 @@ class ConstantPool { cpRefs.add(ue); e = ue; // do not descend into the sig } + if (bsms != null && e.tag == CONSTANT_BootstrapMethod) { + BootstrapMethodEntry bsm = (BootstrapMethodEntry)e; + cpRefs.remove(bsm); + // move it away to the side table where it belongs + if (!bsms.contains(bsm)) + bsms.add(bsm); + // fall through to recursively add refs for this entry + } // Recursively add the refs of e to cpRefs: for (int i = 0; ; i++) { Entry re = e.getRef(i); @@ -1174,15 +1517,37 @@ class ConstantPool { case CONSTANT_Methodref: return "Methodref"; case CONSTANT_InterfaceMethodref: return "InterfaceMethodref"; case CONSTANT_NameandType: return "NameandType"; + case CONSTANT_MethodHandle: return "MethodHandle"; + case CONSTANT_MethodType: return "MethodType"; + case CONSTANT_InvokeDynamic: return "InvokeDynamic"; // pseudo-tags: - case CONSTANT_All: return "*All"; - case CONSTANT_None: return "*None"; + case CONSTANT_All: return "**All"; + case CONSTANT_None: return "**None"; + case CONSTANT_LoadableValue: return "**LoadableValue"; + case CONSTANT_AnyMember: return "**AnyMember"; + case CONSTANT_FieldSpecific: return "*FieldSpecific"; case CONSTANT_Signature: return "*Signature"; + case CONSTANT_BootstrapMethod: return "*BootstrapMethod"; } return "tag#"+tag; } + public static String refKindName(int refKind) { + switch (refKind) { + case REF_getField: return "getField"; + case REF_getStatic: return "getStatic"; + case REF_putField: return "putField"; + case REF_putStatic: return "putStatic"; + case REF_invokeVirtual: return "invokeVirtual"; + case REF_invokeStatic: return "invokeStatic"; + case REF_invokeSpecial: return "invokeSpecial"; + case REF_newInvokeSpecial: return "newInvokeSpecial"; + case REF_invokeInterface: return "invokeInterface"; + } + return "refKind#"+refKind; + } + // archive constant pool definition order static final byte TAGS_IN_ORDER[] = { CONSTANT_Utf8, @@ -1190,13 +1555,19 @@ class ConstantPool { CONSTANT_Float, CONSTANT_Long, CONSTANT_Double, - CONSTANT_String, + CONSTANT_String, // note that String=8 precedes Class=7 CONSTANT_Class, CONSTANT_Signature, CONSTANT_NameandType, // cp_Descr CONSTANT_Fieldref, // cp_Field CONSTANT_Methodref, // cp_Method - CONSTANT_InterfaceMethodref // cp_Imethod + CONSTANT_InterfaceMethodref, // cp_Imethod + + // Constants defined in JDK 7 and later: + CONSTANT_MethodHandle, + CONSTANT_MethodType, + CONSTANT_BootstrapMethod, // pseudo-tag, really stored in a class attribute + CONSTANT_InvokeDynamic }; static final byte TAG_ORDER[]; static { @@ -1211,4 +1582,45 @@ class ConstantPool { System.out.println("};"); */ } + static final byte[] NUMBER_TAGS = { + CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, CONSTANT_Double + }; + static final byte[] EXTRA_TAGS = { + CONSTANT_MethodHandle, CONSTANT_MethodType, + CONSTANT_BootstrapMethod, // pseudo-tag + CONSTANT_InvokeDynamic + }; + static final byte[] LOADABLE_VALUE_TAGS = { // for CONSTANT_LoadableValue + CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, CONSTANT_Double, + CONSTANT_String, CONSTANT_Class, + CONSTANT_MethodHandle, CONSTANT_MethodType + }; + static final byte[] ANY_MEMBER_TAGS = { // for CONSTANT_AnyMember + CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_InterfaceMethodref + }; + static final byte[] FIELD_SPECIFIC_TAGS = { // for CONSTANT_FieldSpecific + CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, CONSTANT_Double, + CONSTANT_String + }; + static { + assert( + verifyTagOrder(TAGS_IN_ORDER) && + verifyTagOrder(NUMBER_TAGS) && + verifyTagOrder(EXTRA_TAGS) && + verifyTagOrder(LOADABLE_VALUE_TAGS) && + verifyTagOrder(ANY_MEMBER_TAGS) && + verifyTagOrder(FIELD_SPECIFIC_TAGS) + ); + } + private static boolean verifyTagOrder(byte[] tags) { + int prev = -1; + for (byte tag : tags) { + int next = TAG_ORDER[tag]; + assert(next > 0) : "tag not found: "+tag; + assert(TAGS_IN_ORDER[next-1] == tag) : "tag repeated: "+tag+" => "+next+" => "+TAGS_IN_ORDER[next-1]; + assert(prev < next) : "tags not in order: "+Arrays.toString(tags)+" at "+tag; + prev = next; + } + return true; + } } diff --git a/src/share/classes/com/sun/java/util/jar/pack/Constants.java b/src/share/classes/com/sun/java/util/jar/pack/Constants.java index 8dc6560d14871343dd6cb1dbb747aafff4170a36..c7fa00e0f18bb8af5b1f433b3b171f3756ef161e 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/Constants.java +++ b/src/share/classes/com/sun/java/util/jar/pack/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,6 +65,9 @@ class Constants { public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160; public final static int JAVA6_PACKAGE_MINOR_VERSION = 1; + public final static int JAVA7_PACKAGE_MAJOR_VERSION = 170; + public final static int JAVA7_PACKAGE_MINOR_VERSION = 1; + public final static int CONSTANT_POOL_INDEX_LIMIT = 0x10000; public final static int CONSTANT_POOL_NARROW_LIMIT = 0x00100; @@ -82,14 +85,36 @@ class Constants { public final static byte CONSTANT_Methodref = 10; public final static byte CONSTANT_InterfaceMethodref = 11; public final static byte CONSTANT_NameandType = 12; + public final static byte CONSTANT_unused13 = 13; + public final static byte CONSTANT_unused14 = 14; + public final static byte CONSTANT_MethodHandle = 15; + public final static byte CONSTANT_MethodType = 16; + public final static byte CONSTANT_unused17 = 17; // unused + public final static byte CONSTANT_InvokeDynamic = 18; // pseudo-constants: public final static byte CONSTANT_None = 0; - public final static byte CONSTANT_Signature = 13; - public final static byte CONSTANT_Limit = 14; - - public final static byte CONSTANT_All = 19; // combined global map - public final static byte CONSTANT_Literal = 20; // used only for ldc fields + public final static byte CONSTANT_Signature = CONSTANT_unused13; + public final static byte CONSTANT_BootstrapMethod = CONSTANT_unused17; // used only in InvokeDynamic constants + public final static byte CONSTANT_Limit = 19; + + public final static byte CONSTANT_All = 50; // combined global map + public final static byte CONSTANT_LoadableValue = 51; // used for 'KL' and qldc operands + public final static byte CONSTANT_AnyMember = 52; // union of refs to field or (interface) method + public final static byte CONSTANT_FieldSpecific = 53; // used only for 'KQ' ConstantValue attrs + public final static byte CONSTANT_GroupFirst = CONSTANT_All; + public final static byte CONSTANT_GroupLimit = CONSTANT_FieldSpecific+1; + + // CONSTANT_MethodHandle reference kinds + public final static byte REF_getField = 1; + public final static byte REF_getStatic = 2; + public final static byte REF_putField = 3; + public final static byte REF_putStatic = 4; + public final static byte REF_invokeVirtual = 5; + public final static byte REF_invokeStatic = 6; + public final static byte REF_invokeSpecial = 7; + public final static byte REF_newInvokeSpecial = 8; + public final static byte REF_invokeInterface = 9; // pseudo-access bits public final static int ACC_IC_LONG_FORM = (1<<16); //for ic_flags @@ -133,7 +158,7 @@ class Constants { public static final int AO_HAVE_SPECIAL_FORMATS = 1<<0; public static final int AO_HAVE_CP_NUMBERS = 1<<1; public static final int AO_HAVE_ALL_CODE_FLAGS = 1<<2; - public static final int AO_3_UNUSED_MBZ = 1<<3; + public static final int AO_HAVE_CP_EXTRAS = 1<<3; public static final int AO_HAVE_FILE_HEADERS = 1<<4; public static final int AO_DEFLATE_HINT = 1<<5; public static final int AO_HAVE_FILE_MODTIME = 1<<6; @@ -143,6 +168,7 @@ class Constants { public static final int AO_HAVE_FIELD_FLAGS_HI = 1<<10; public static final int AO_HAVE_METHOD_FLAGS_HI = 1<<11; public static final int AO_HAVE_CODE_FLAGS_HI = 1<<12; + public static final int AO_UNUSED_MBZ = (-1)<<13; // option bits reserved for future use public static final int LG_AO_HAVE_XXX_FLAGS_HI = 9; @@ -357,7 +383,7 @@ class Constants { _invokespecial = 183, // 0xb7 _invokestatic = 184, // 0xb8 _invokeinterface = 185, // 0xb9 - _xxxunusedxxx = 186, // 0xba + _invokedynamic = 186, // 0xba _new = 187, // 0xbb _newarray = 188, // 0xbc _anewarray = 189, // 0xbd @@ -422,15 +448,18 @@ class Constants { // Ldc variants gain us only 0.007% improvement in compression ratio, // but they simplify the file format greatly. public final static int _xldc_op = _invokeinit_limit; - public final static int _aldc = _ldc; + public final static int _sldc = _ldc; // previously named _aldc public final static int _cldc = _xldc_op+0; public final static int _ildc = _xldc_op+1; public final static int _fldc = _xldc_op+2; - public final static int _aldc_w = _ldc_w; + public final static int _sldc_w = _ldc_w; // previously named _aldc_w public final static int _cldc_w = _xldc_op+3; public final static int _ildc_w = _xldc_op+4; public final static int _fldc_w = _xldc_op+5; public final static int _lldc2_w = _ldc2_w; public final static int _dldc2_w = _xldc_op+6; - public final static int _xldc_limit = _xldc_op+7; + // anything other than primitive, string, or class must be handled with qldc: + public final static int _qldc = _xldc_op+7; + public final static int _qldc_w = _xldc_op+8; + public final static int _xldc_limit = _xldc_op+9; } diff --git a/src/share/classes/com/sun/java/util/jar/pack/Instruction.java b/src/share/classes/com/sun/java/util/jar/pack/Instruction.java index 8d947689ae6df110add3b5e6b25d37ebde6a217b..f488bede3230e1df65c401e9a1fb1277f26aadac 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/Instruction.java +++ b/src/share/classes/com/sun/java/util/jar/pack/Instruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -451,7 +451,7 @@ class Instruction { public static byte getCPRefOpTag(int bc) { if (bc < BC_INDEX[0].length && BC_INDEX[0][bc] > 0) return BC_TAG[0][bc]; - if (bc >= _xldc_op && bc < _xldc_limit) return CONSTANT_Literal; + if (bc >= _xldc_op && bc < _xldc_limit) return CONSTANT_LoadableValue; return CONSTANT_None; } @@ -500,7 +500,7 @@ class Instruction { def("bkf", _getstatic, _putfield); // pack kf (base=Field) def("bkm", _invokevirtual, _invokestatic); // pack kn (base=Method) def("bkixx", _invokeinterface); // pack ki (base=IMethod), omit xx - def("", _xxxunusedxxx); + def("bkyxx", _invokedynamic); // pack ky (base=Any), omit xx def("bkc", _new); // pack kc def("bx", _newarray); def("bkc", _anewarray); // pack kc @@ -515,7 +515,6 @@ class Instruction { //System.out.println(i+": l="+BC_LENGTH[0][i]+" i="+BC_INDEX[0][i]); //assert(BC_LENGTH[0][i] != -1); if (BC_LENGTH[0][i] == -1) { - assert(i == _xxxunusedxxx); continue; // unknown opcode } @@ -543,7 +542,7 @@ class Instruction { "if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple if_acmpeq if_acmpne "+ "goto jsr ret tableswitch lookupswitch ireturn lreturn freturn dreturn "+ "areturn return getstatic putstatic getfield putfield invokevirtual "+ - "invokespecial invokestatic invokeinterface xxxunusedxxx new newarray "+ + "invokespecial invokestatic invokeinterface invokedynamic new newarray "+ "anewarray arraylength athrow checkcast instanceof monitorenter "+ "monitorexit wide multianewarray ifnull ifnonnull goto_w jsr_w "; for (int bc = 0; names.length() > 0; bc++) { @@ -588,6 +587,8 @@ class Instruction { case _dldc2_w: iname = "*dldc2_w"; break; case _cldc: iname = "*cldc"; break; case _cldc_w: iname = "*cldc_w"; break; + case _qldc: iname = "*qldc"; break; + case _qldc_w: iname = "*qldc_w"; break; case _byte_escape: iname = "*byte_escape"; break; case _ref_escape: iname = "*ref_escape"; break; case _end_marker: iname = "*end"; break; @@ -618,15 +619,16 @@ class Instruction { if (index > 0 && index+1 < length) { switch (fmt.charAt(index+1)) { case 'c': tag = CONSTANT_Class; break; - case 'k': tag = CONSTANT_Literal; break; + case 'k': tag = CONSTANT_LoadableValue; break; case 'f': tag = CONSTANT_Fieldref; break; case 'm': tag = CONSTANT_Methodref; break; case 'i': tag = CONSTANT_InterfaceMethodref; break; + case 'y': tag = CONSTANT_InvokeDynamic; break; } assert(tag != CONSTANT_None); } else if (index > 0 && length == 2) { assert(from_bc == _ldc); - tag = CONSTANT_Literal; // _ldc opcode only + tag = CONSTANT_LoadableValue; // _ldc opcode only } for (int bc = from_bc; bc <= to_bc; bc++) { BC_FORMAT[w][bc] = fmt; @@ -649,7 +651,7 @@ class Instruction { Instruction i = at(code, 0); while (i != null) { int opcode = i.getBC(); - if (opcode == _xxxunusedxxx || opcode < _nop || opcode > _jsr_w) { + if (opcode < _nop || opcode > _jsr_w) { String message = "illegal opcode: " + opcode + " " + i; throw new FormatException(message); } diff --git a/src/share/classes/com/sun/java/util/jar/pack/Package.java b/src/share/classes/com/sun/java/util/jar/pack/Package.java index 2abe5452249464cb9fb75ff034acec7fed9b8b22..fee97f4d9642c2b52e6e107e8215185f0972d255 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/Package.java +++ b/src/share/classes/com/sun/java/util/jar/pack/Package.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ package com.sun.java.util.jar.pack; import com.sun.java.util.jar.pack.Attribute.Layout; import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; +import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; import com.sun.java.util.jar.pack.ConstantPool.Index; import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry; import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; @@ -100,6 +101,8 @@ class Package { classes.clear(); files.clear(); BandStructure.nextSeqForDebug = 0; + package_minver = -1; // fill in later + package_majver = 0; // fill in later } int getPackageVersion() { @@ -108,6 +111,7 @@ class Package { // Special empty versions of Code and InnerClasses, used for markers. public static final Attribute.Layout attrCodeEmpty; + public static final Attribute.Layout attrBootstrapMethodsEmpty; public static final Attribute.Layout attrInnerClassesEmpty; public static final Attribute.Layout attrSourceFileSpecial; public static final Map attrDefs; @@ -115,6 +119,8 @@ class Package { Map ad = new HashMap<>(3); attrCodeEmpty = Attribute.define(ad, ATTR_CONTEXT_METHOD, "Code", "").layout(); + attrBootstrapMethodsEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS, + "BootstrapMethods", "").layout(); attrInnerClassesEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS, "InnerClasses", "").layout(); attrSourceFileSpecial = Attribute.define(ad, ATTR_CONTEXT_CLASS, @@ -153,9 +159,8 @@ class Package { package_minver = JAVA6_PACKAGE_MINOR_VERSION; } else { // Normal case. Use the newest archive format, when available - // TODO: replace the following with JAVA7* when the need arises - package_majver = JAVA6_PACKAGE_MAJOR_VERSION; - package_minver = JAVA6_PACKAGE_MINOR_VERSION; + package_majver = JAVA7_PACKAGE_MAJOR_VERSION; + package_minver = JAVA7_PACKAGE_MINOR_VERSION; } } @@ -168,13 +173,22 @@ class Package { String expMag = Integer.toHexString(JAVA_PACKAGE_MAGIC); throw new IOException("Unexpected package magic number: got "+gotMag+"; expected "+expMag); } - if ((package_majver != JAVA6_PACKAGE_MAJOR_VERSION && - package_majver != JAVA5_PACKAGE_MAJOR_VERSION) || - (package_minver != JAVA6_PACKAGE_MINOR_VERSION && - package_minver != JAVA5_PACKAGE_MINOR_VERSION)) { - + int[] majminFound = null; + for (int[] majmin : new int[][]{ + { JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION }, + { JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION }, + { JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION } + }) { + if (package_majver == majmin[0] && package_minver == majmin[1]) { + majminFound = majmin; + break; + } + } + if (majminFound == null) { String gotVer = package_majver+"."+package_minver; - String expVer = JAVA6_PACKAGE_MAJOR_VERSION+"."+JAVA6_PACKAGE_MINOR_VERSION+ + String expVer = JAVA7_PACKAGE_MAJOR_VERSION+"."+JAVA7_PACKAGE_MINOR_VERSION+ + " OR "+ + JAVA6_PACKAGE_MAJOR_VERSION+"."+JAVA6_PACKAGE_MINOR_VERSION+ " OR "+ JAVA5_PACKAGE_MAJOR_VERSION+"."+JAVA5_PACKAGE_MINOR_VERSION; throw new IOException("Unexpected package minor version: got "+gotVer+"; expected "+expVer); @@ -213,6 +227,7 @@ class Package { //ArrayList attributes; // in Attribute.Holder.this.attributes // Note that InnerClasses may be collected at the package level. ArrayList innerClasses; + ArrayList bootstrapMethods; Class(int flags, ClassEntry thisClass, ClassEntry superClass, ClassEntry[] interfaces) { this.magic = JAVA_MAGIC; @@ -313,6 +328,25 @@ class Package { this.cpMap = cpMap; } + boolean hasBootstrapMethods() { + return bootstrapMethods != null && !bootstrapMethods.isEmpty(); + } + + List getBootstrapMethods() { + return bootstrapMethods; + } + + BootstrapMethodEntry[] getBootstrapMethodMap() { + return (hasBootstrapMethods()) + ? bootstrapMethods.toArray(new BootstrapMethodEntry[bootstrapMethods.size()]) + : null; + } + + void setBootstrapMethods(Collection bsms) { + assert(bootstrapMethods == null); // do not do this twice + bootstrapMethods = new ArrayList<>(bsms); + } + boolean hasInnerClasses() { return innerClasses != null; } @@ -1283,7 +1317,8 @@ class Package { byTagU[tag] = null; // done with it } for (int i = 0; i < byTagU.length; i++) { - assert(byTagU[i] == null); // all consumed + Index ix = byTagU[i]; + assert(ix == null); // all consumed } for (int i = 0; i < ConstantPool.TAGS_IN_ORDER.length; i++) { byte tag = ConstantPool.TAGS_IN_ORDER[i]; diff --git a/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java b/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java index a6886b42c7ac0d62c07a19a9935cf4cd66aaad54..8813b5e3f253cc80ee3886d556800620931e0320 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java +++ b/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,7 @@ package com.sun.java.util.jar.pack; -import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; -import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; -import com.sun.java.util.jar.pack.ConstantPool.Entry; -import com.sun.java.util.jar.pack.ConstantPool.Index; -import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; -import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; -import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; +import com.sun.java.util.jar.pack.ConstantPool.*; import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.File; import com.sun.java.util.jar.pack.Package.InnerClass; @@ -46,6 +40,7 @@ import java.util.ArrayList; import java.util.Map; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.HashMap; @@ -266,7 +261,6 @@ class PackageReader extends BandStructure { // #band_headers_size :UNSIGNED5[1] // #attr_definition_count :UNSIGNED5[1] // - assert(AH_LENGTH == 8+(ConstantPool.TAGS_IN_ORDER.length)+6); archive_header_0.expectLength(AH_LENGTH_0); archive_header_0.readFrom(in); @@ -282,6 +276,7 @@ class PackageReader extends BandStructure { boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS); boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS); boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS); + boolean haveCPExtra = testBit(archiveOptions, AO_HAVE_CP_EXTRAS); initAttrIndexLimit(); // now we are ready to use the data: @@ -300,11 +295,11 @@ class PackageReader extends BandStructure { archive_header_S.doneDisbursing(); archiveSize0 = in.getBytesServed(); - int remainingHeaders = AH_LENGTH - AH_LENGTH_0 - AH_LENGTH_S; - if (!haveFiles) remainingHeaders -= AH_FILE_HEADER_LEN-AH_LENGTH_S; - if (!haveSpecial) remainingHeaders -= AH_SPECIAL_FORMAT_LEN; - if (!haveNumbers) remainingHeaders -= AH_CP_NUMBER_LEN; - assert(remainingHeaders >= AH_LENGTH_MIN - AH_LENGTH_0); + int remainingHeaders = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S; + if (haveFiles) remainingHeaders += AH_FILE_HEADER_LEN; + if (haveSpecial) remainingHeaders += AH_SPECIAL_FORMAT_LEN; + if (haveNumbers) remainingHeaders += AH_CP_NUMBER_LEN; + if (haveCPExtra) remainingHeaders += AH_CP_EXTRA_LEN; archive_header_1.expectLength(remainingHeaders); archive_header_1.readFrom(in); @@ -325,7 +320,7 @@ class PackageReader extends BandStructure { numAttrDefs = 0; } - readConstantPoolCounts(haveNumbers); + readConstantPoolCounts(haveNumbers, haveCPExtra); numInnerClasses = archive_header_1.getInt(); @@ -351,7 +346,7 @@ class PackageReader extends BandStructure { band_headers.doneDisbursing(); } - void readConstantPoolCounts(boolean haveNumbers) throws IOException { + void readConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException { // size the constant pools: for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { // cp_counts: @@ -364,6 +359,7 @@ class PackageReader extends BandStructure { // #cp_Field_count :UNSIGNED5[1] // #cp_Method_count :UNSIGNED5[1] // #cp_Imethod_count :UNSIGNED5[1] + // (cp_attr_counts) ** (#have_cp_attr_counts) // // cp_number_counts: // #cp_Int_count :UNSIGNED5[1] @@ -371,6 +367,12 @@ class PackageReader extends BandStructure { // #cp_Long_count :UNSIGNED5[1] // #cp_Double_count :UNSIGNED5[1] // + // cp_extra_counts: + // #cp_MethodHandle_count :UNSIGNED5[1] + // #cp_MethodType_count :UNSIGNED5[1] + // #cp_InvokeDynamic_count :UNSIGNED5[1] + // #cp_BootstrapMethod_count :UNSIGNED5[1] + // byte tag = ConstantPool.TAGS_IN_ORDER[k]; if (!haveNumbers) { // These four counts are optional. @@ -382,6 +384,16 @@ class PackageReader extends BandStructure { continue; } } + if (!haveCPExtra) { + // These four counts are optional. + switch (tag) { + case CONSTANT_MethodHandle: + case CONSTANT_MethodType: + case CONSTANT_InvokeDynamic: + case CONSTANT_BootstrapMethod: + continue; + } + } tagCount[tag] = archive_header_1.getInt(); } } @@ -401,6 +413,11 @@ class PackageReader extends BandStructure { return index; } + void checkLegacy(String bandname) { + if (this.pkg.package_majver < JAVA7_PACKAGE_MAJOR_VERSION) { + throw new RuntimeException("unexpected band " + bandname); + } + } void readConstantPool() throws IOException { // cp_bands: // cp_Utf8 @@ -533,8 +550,82 @@ class PackageReader extends BandStructure { case CONSTANT_InterfaceMethodref: readMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); break; + case CONSTANT_MethodHandle: + if (cpMap.length > 0) { + checkLegacy(cp_MethodHandle_refkind.name()); + } + cp_MethodHandle_refkind.expectLength(cpMap.length); + cp_MethodHandle_refkind.readFrom(in); + cp_MethodHandle_member.expectLength(cpMap.length); + cp_MethodHandle_member.readFrom(in); + cp_MethodHandle_member.setIndex(getCPIndex(CONSTANT_AnyMember)); + for (int i = 0; i < cpMap.length; i++) { + byte refKind = (byte) cp_MethodHandle_refkind.getInt(); + MemberEntry memRef = (MemberEntry) cp_MethodHandle_member.getRef(); + cpMap[i] = ConstantPool.getMethodHandleEntry(refKind, memRef); + } + cp_MethodHandle_refkind.doneDisbursing(); + cp_MethodHandle_member.doneDisbursing(); + break; + case CONSTANT_MethodType: + if (cpMap.length > 0) { + checkLegacy(cp_MethodType.name()); + } + cp_MethodType.expectLength(cpMap.length); + cp_MethodType.readFrom(in); + cp_MethodType.setIndex(getCPIndex(CONSTANT_Signature)); + for (int i = 0; i < cpMap.length; i++) { + SignatureEntry typeRef = (SignatureEntry) cp_MethodType.getRef(); + cpMap[i] = ConstantPool.getMethodTypeEntry(typeRef); + } + cp_MethodType.doneDisbursing(); + break; + case CONSTANT_InvokeDynamic: + if (cpMap.length > 0) { + checkLegacy(cp_InvokeDynamic_spec.name()); + } + cp_InvokeDynamic_spec.expectLength(cpMap.length); + cp_InvokeDynamic_spec.readFrom(in); + cp_InvokeDynamic_spec.setIndex(getCPIndex(CONSTANT_BootstrapMethod)); + cp_InvokeDynamic_desc.expectLength(cpMap.length); + cp_InvokeDynamic_desc.readFrom(in); + cp_InvokeDynamic_desc.setIndex(getCPIndex(CONSTANT_NameandType)); + for (int i = 0; i < cpMap.length; i++) { + BootstrapMethodEntry bss = (BootstrapMethodEntry) cp_InvokeDynamic_spec.getRef(); + DescriptorEntry descr = (DescriptorEntry) cp_InvokeDynamic_desc.getRef(); + cpMap[i] = ConstantPool.getInvokeDynamicEntry(bss, descr); + } + cp_InvokeDynamic_spec.doneDisbursing(); + cp_InvokeDynamic_desc.doneDisbursing(); + break; + case CONSTANT_BootstrapMethod: + if (cpMap.length > 0) { + checkLegacy(cp_BootstrapMethod_ref.name()); + } + cp_BootstrapMethod_ref.expectLength(cpMap.length); + cp_BootstrapMethod_ref.readFrom(in); + cp_BootstrapMethod_ref.setIndex(getCPIndex(CONSTANT_MethodHandle)); + cp_BootstrapMethod_arg_count.expectLength(cpMap.length); + cp_BootstrapMethod_arg_count.readFrom(in); + int totalArgCount = cp_BootstrapMethod_arg_count.getIntTotal(); + cp_BootstrapMethod_arg.expectLength(totalArgCount); + cp_BootstrapMethod_arg.readFrom(in); + cp_BootstrapMethod_arg.setIndex(getCPIndex(CONSTANT_LoadableValue)); + for (int i = 0; i < cpMap.length; i++) { + MethodHandleEntry bsm = (MethodHandleEntry) cp_BootstrapMethod_ref.getRef(); + int argc = cp_BootstrapMethod_arg_count.getInt(); + Entry[] argRefs = new Entry[argc]; + for (int j = 0; j < argc; j++) { + argRefs[j] = cp_BootstrapMethod_arg.getRef(); + } + cpMap[i] = ConstantPool.getBootstrapMethodEntry(bsm, argRefs); + } + cp_BootstrapMethod_ref.doneDisbursing(); + cp_BootstrapMethod_arg_count.doneDisbursing(); + cp_BootstrapMethod_arg.doneDisbursing(); + break; default: - assert(false); + throw new AssertionError("unexpected CP tag in package"); } Index index = initCPIndex(tag, cpMap); @@ -548,6 +639,21 @@ class PackageReader extends BandStructure { cp_bands.doneDisbursing(); + if (optDumpBands || verbose > 1) { + for (byte tag = CONSTANT_GroupFirst; tag < CONSTANT_GroupLimit; tag++) { + Index index = pkg.cp.getIndexByTag(tag); + if (index == null || index.isEmpty()) continue; + Entry[] cpMap = index.cpMap; + if (verbose > 1) + Utils.log.info("Index group "+ConstantPool.tagName(tag)+" contains "+cpMap.length+" entries."); + if (optDumpBands) { + try (PrintStream ps = new PrintStream(getDumpStream(index.debugName, tag, ".gidx", index))) { + printArrayTo(ps, cpMap, 0, cpMap.length, true); + } + } + } + } + setBandIndexes(); } @@ -1056,8 +1162,16 @@ class PackageReader extends BandStructure { // look for constant pool entries: cls.visitRefs(VRM_CLASSIC, cpRefs); + ArrayList bsms = new ArrayList<>(); + /* + * BootstrapMethod(BSMs) are added here before InnerClasses(ICs), + * so as to ensure the order. Noting that the BSMs may be + * removed if they are not found in the CP, after the ICs expansion. + */ + cls.addAttribute(Package.attrBootstrapMethodsEmpty.canonicalInstance()); + // flesh out the local constant pool - ConstantPool.completeReferencesIn(cpRefs, true); + ConstantPool.completeReferencesIn(cpRefs, true, bsms); // Now that we know all our local class references, // compute the InnerClasses attribute. @@ -1074,14 +1188,23 @@ class PackageReader extends BandStructure { } // flesh out the local constant pool, again - ConstantPool.completeReferencesIn(cpRefs, true); + ConstantPool.completeReferencesIn(cpRefs, true, bsms); + } + + // remove the attr previously set, otherwise add the bsm and + // references as required + if (bsms.isEmpty()) { + cls.attributes.remove(Package.attrBootstrapMethodsEmpty.canonicalInstance()); + } else { + cpRefs.add(Package.getRefString("BootstrapMethods")); + Collections.sort(bsms); + cls.setBootstrapMethods(bsms); } // construct a local constant pool int numDoubles = 0; for (Entry e : cpRefs) { if (e.isDoubleWord()) numDoubles++; - assert(e.tag != CONSTANT_Signature) : (e); } Entry[] cpMap = new Entry[1+numDoubles+cpRefs.size()]; int fillp = 1; @@ -1154,7 +1277,8 @@ class PackageReader extends BandStructure { int totalNM = class_method_count.getIntTotal(); field_descr.expectLength(totalNF); method_descr.expectLength(totalNM); - if (verbose > 1) Utils.log.fine("expecting #fields="+totalNF+" and #methods="+totalNM+" in #classes="+numClasses); + if (verbose > 1) Utils.log.fine("expecting #fields="+totalNF+ + " and #methods="+totalNM+" in #classes="+numClasses); List fields = new ArrayList<>(totalNF); field_descr.readFrom(in); @@ -1393,7 +1517,8 @@ class PackageReader extends BandStructure { MultiBand xxx_attr_bands = attrBands[ctype]; long flagMask = attrFlagMask[ctype]; if (verbose > 1) { - Utils.log.fine("scanning flags and attrs for "+Attribute.contextName(ctype)+"["+holders.size()+"]"); + Utils.log.fine("scanning flags and attrs for "+ + Attribute.contextName(ctype)+"["+holders.size()+"]"); } // Fetch the attribute layout definitions which govern the bands @@ -1751,8 +1876,10 @@ class PackageReader extends BandStructure { bc_local, bc_label, bc_intref, bc_floatref, bc_longref, bc_doubleref, bc_stringref, + bc_loadablevalueref, bc_classref, bc_fieldref, bc_methodref, bc_imethodref, + bc_indyref, bc_thisfield, bc_superfield, bc_thismethod, bc_supermethod, bc_initref, @@ -2099,7 +2226,8 @@ class PackageReader extends BandStructure { case _ildc: case _cldc: case _fldc: - case _aldc: + case _sldc: + case _qldc: origBC = _ldc; size = 1; ldcRefSet.add(ref); @@ -2107,7 +2235,8 @@ class PackageReader extends BandStructure { case _ildc_w: case _cldc_w: case _fldc_w: - case _aldc_w: + case _sldc_w: + case _qldc_w: origBC = _ldc_w; break; case _lldc2_w: @@ -2136,6 +2265,9 @@ class PackageReader extends BandStructure { int argSize = ((MemberEntry)ref).descRef.typeRef.computeSize(true); buf[pc++] = (byte)( 1 + argSize ); buf[pc++] = 0; + } else if (origBC == _invokedynamic) { + buf[pc++] = 0; + buf[pc++] = 0; } assert(Instruction.opLength(origBC) == (pc - curPC)); continue; diff --git a/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java b/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java index 0f7f51a7ec3c0e2945ed1aa51703888753baecb0..d40a6d3889ee67cea3343b77adc65d08c2dcb567 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java +++ b/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,7 @@ package com.sun.java.util.jar.pack; -import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; -import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; -import com.sun.java.util.jar.pack.ConstantPool.Entry; -import com.sun.java.util.jar.pack.ConstantPool.Index; -import com.sun.java.util.jar.pack.ConstantPool.IndexGroup; -import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; -import com.sun.java.util.jar.pack.ConstantPool.NumberEntry; -import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; -import com.sun.java.util.jar.pack.ConstantPool.StringEntry; +import com.sun.java.util.jar.pack.ConstantPool.*; import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.File; import com.sun.java.util.jar.pack.Package.InnerClass; @@ -281,7 +273,7 @@ class PackageWriter extends BandStructure { void writeArchiveHeader() throws IOException { // for debug only: number of words optimized away - int headerDiscountForDebug = 0; + int headerSizeForDebug = AH_LENGTH_MIN; // AO_HAVE_SPECIAL_FORMATS is set if non-default // coding techniques are used, or if there are @@ -293,8 +285,8 @@ class PackageWriter extends BandStructure { if (haveSpecial) archiveOptions |= AO_HAVE_SPECIAL_FORMATS; } - if (!haveSpecial) - headerDiscountForDebug += AH_SPECIAL_FORMAT_LEN; + if (haveSpecial) + headerSizeForDebug += AH_SPECIAL_FORMAT_LEN; // AO_HAVE_FILE_HEADERS is set if there is any // file or segment envelope information present. @@ -305,8 +297,8 @@ class PackageWriter extends BandStructure { if (haveFiles) archiveOptions |= AO_HAVE_FILE_HEADERS; } - if (!haveFiles) - headerDiscountForDebug += AH_FILE_HEADER_LEN; + if (haveFiles) + headerSizeForDebug += AH_FILE_HEADER_LEN; // AO_HAVE_CP_NUMBERS is set if there are any numbers // in the global constant pool. (Numbers are in 15% of classes.) @@ -316,8 +308,19 @@ class PackageWriter extends BandStructure { if (haveNumbers) archiveOptions |= AO_HAVE_CP_NUMBERS; } - if (!haveNumbers) - headerDiscountForDebug += AH_CP_NUMBER_LEN; + if (haveNumbers) + headerSizeForDebug += AH_CP_NUMBER_LEN; + + // AO_HAVE_CP_EXTRAS is set if there are constant pool entries + // beyond the Java 6 version of the class file format. + boolean haveCPExtra = testBit(archiveOptions, AO_HAVE_CP_EXTRAS); + if (!haveCPExtra) { + haveCPExtra |= pkg.cp.haveExtraTags(); + if (haveCPExtra) + archiveOptions |= AO_HAVE_CP_EXTRAS; + } + if (haveCPExtra) + headerSizeForDebug += AH_CP_EXTRA_LEN; assert(pkg.package_majver > 0); // caller must specify! archive_header_0.putInt(pkg.package_minver); @@ -355,18 +358,18 @@ class PackageWriter extends BandStructure { assert(attrDefsWritten.length == 0); } - writeConstantPoolCounts(haveNumbers); + writeConstantPoolCounts(haveNumbers, haveCPExtra); archive_header_1.putInt(pkg.getAllInnerClasses().size()); archive_header_1.putInt(pkg.default_class_minver); archive_header_1.putInt(pkg.default_class_majver); archive_header_1.putInt(pkg.classes.size()); - // Sanity: Make sure we came out to 26 (less optional fields): + // Sanity: Make sure we came out to 29 (less optional fields): assert(archive_header_0.length() + archive_header_S.length() + archive_header_1.length() - == AH_LENGTH - headerDiscountForDebug); + == headerSizeForDebug); // Figure out all the sizes now, first cut: archiveSize0 = 0; @@ -394,9 +397,8 @@ class PackageWriter extends BandStructure { assert(all_bands.outputSize() == archiveSize0+archiveSize1); } - void writeConstantPoolCounts(boolean haveNumbers) throws IOException { - for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { - byte tag = ConstantPool.TAGS_IN_ORDER[k]; + void writeConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException { + for (byte tag : ConstantPool.TAGS_IN_ORDER) { int count = pkg.cp.getIndexByTag(tag).size(); switch (tag) { case CONSTANT_Utf8: @@ -416,6 +418,17 @@ class PackageWriter extends BandStructure { continue; } break; + + case CONSTANT_MethodHandle: + case CONSTANT_MethodType: + case CONSTANT_InvokeDynamic: + case CONSTANT_BootstrapMethod: + // Omit counts for newer entities if possible. + if (!haveCPExtra) { + assert(count == 0); + continue; + } + break; } archive_header_1.putInt(count); } @@ -449,8 +462,7 @@ class PackageWriter extends BandStructure { if (verbose > 0) Utils.log.info("Writing CP"); - for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { - byte tag = ConstantPool.TAGS_IN_ORDER[k]; + for (byte tag : ConstantPool.TAGS_IN_ORDER) { Index index = cp.getIndexByTag(tag); Entry[] cpMap = index.cpMap; @@ -530,8 +542,52 @@ class PackageWriter extends BandStructure { case CONSTANT_InterfaceMethodref: writeMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); break; + case CONSTANT_MethodHandle: + for (int i = 0; i < cpMap.length; i++) { + MethodHandleEntry e = (MethodHandleEntry) cpMap[i]; + cp_MethodHandle_refkind.putInt(e.refKind); + cp_MethodHandle_member.putRef(e.memRef); + } + break; + case CONSTANT_MethodType: + for (int i = 0; i < cpMap.length; i++) { + MethodTypeEntry e = (MethodTypeEntry) cpMap[i]; + cp_MethodType.putRef(e.typeRef); + } + break; + case CONSTANT_InvokeDynamic: + for (int i = 0; i < cpMap.length; i++) { + InvokeDynamicEntry e = (InvokeDynamicEntry) cpMap[i]; + cp_InvokeDynamic_spec.putRef(e.bssRef); + cp_InvokeDynamic_desc.putRef(e.descRef); + } + break; + case CONSTANT_BootstrapMethod: + for (int i = 0; i < cpMap.length; i++) { + BootstrapMethodEntry e = (BootstrapMethodEntry) cpMap[i]; + cp_BootstrapMethod_ref.putRef(e.bsmRef); + cp_BootstrapMethod_arg_count.putInt(e.argRefs.length); + for (Entry argRef : e.argRefs) { + cp_BootstrapMethod_arg.putRef(argRef); + } + } + break; default: - assert(false); + throw new AssertionError("unexpected CP tag in package"); + } + } + if (optDumpBands || verbose > 1) { + for (byte tag = CONSTANT_GroupFirst; tag < CONSTANT_GroupLimit; tag++) { + Index index = cp.getIndexByTag(tag); + if (index == null || index.isEmpty()) continue; + Entry[] cpMap = index.cpMap; + if (verbose > 1) + Utils.log.info("Index group "+ConstantPool.tagName(tag)+" contains "+cpMap.length+" entries."); + if (optDumpBands) { + try (PrintStream ps = new PrintStream(getDumpStream(index.debugName, tag, ".gidx", index))) { + printArrayTo(ps, cpMap, 0, cpMap.length, true); + } + } } } } @@ -988,6 +1044,8 @@ class PackageWriter extends BandStructure { for (Class cls : pkg.classes) { // Replace "obvious" SourceFile attrs by null. cls.minimizeSourceFile(); + // BootstrapMethods should never have been inserted. + assert(cls.getAttribute(Package.attrBootstrapMethodsEmpty) == null); } } @@ -1325,9 +1383,7 @@ class PackageWriter extends BandStructure { // %%% Add a stress mode which issues _ref/_byte_escape. if (verbose > 3) Utils.log.fine(i.toString()); - if (i.isNonstandard() - && (!p200.getBoolean(Utils.COM_PREFIX+"invokedynamic") - || i.getBC() != _xxxunusedxxx)) { + if (i.isNonstandard()) { // Crash and burn with a complaint if there are funny // bytecodes in this class file. String complaint = code.getMethod() @@ -1427,24 +1483,6 @@ class PackageWriter extends BandStructure { continue; } - switch (bc) { - case _xxxunusedxxx: // %%% pretend this is invokedynamic - { - i.setNonstandardLength(3); - int refx = i.getShortAt(1); - Entry ref = (refx == 0)? null: curCPMap[refx]; - // transmit the opcode, carefully: - bc_codes.putByte(_byte_escape); - bc_escsize.putInt(1); // one byte of opcode - bc_escbyte.putByte(bc); // the opcode - // transmit the CP reference, carefully: - bc_codes.putByte(_ref_escape); - bc_escrefsize.putInt(2); // two bytes of ref - bc_escref.putRef(ref); // the ref - continue; - } - } - int branch = i.getBranchLabel(); if (branch >= 0) { bc_codes.putByte(bc); @@ -1458,7 +1496,7 @@ class PackageWriter extends BandStructure { CPRefBand bc_which; int vbc = bc; switch (i.getCPTag()) { - case CONSTANT_Literal: + case CONSTANT_LoadableValue: switch (ref.tag) { case CONSTANT_Integer: bc_which = bc_intref; @@ -1489,8 +1527,8 @@ class PackageWriter extends BandStructure { case CONSTANT_String: bc_which = bc_stringref; switch (bc) { - case _ldc: vbc = _aldc; break; - case _ldc_w: vbc = _aldc_w; break; + case _ldc: vbc = _sldc; break; + case _ldc_w: vbc = _sldc_w; break; default: assert(false); } break; @@ -1503,8 +1541,16 @@ class PackageWriter extends BandStructure { } break; default: - bc_which = null; - assert(false); + // CONSTANT_MethodHandle, etc. + if (getPackageMajver() < JAVA7_PACKAGE_MAJOR_VERSION) { + throw new IOException("bad package major version for Java 7 ldc"); + } + bc_which = bc_loadablevalueref; + switch (bc) { + case _ldc: vbc = _qldc; break; + case _ldc_w: vbc = _qldc_w; break; + default: assert(false); + } } break; case CONSTANT_Class: @@ -1517,6 +1563,8 @@ class PackageWriter extends BandStructure { bc_which = bc_methodref; break; case CONSTANT_InterfaceMethodref: bc_which = bc_imethodref; break; + case CONSTANT_InvokeDynamic: + bc_which = bc_indyref; break; default: bc_which = null; assert(false); @@ -1532,6 +1580,12 @@ class PackageWriter extends BandStructure { assert(i.getLength() == 5); // Make sure the discarded bytes are sane: assert(i.getConstant() == (1+((MemberEntry)ref).descRef.typeRef.computeSize(true)) << 8); + } else if (bc == _invokedynamic) { + if (getPackageMajver() < JAVA7_PACKAGE_MAJOR_VERSION) { + throw new IOException("bad package major version for Java 7 invokedynamic"); + } + assert(i.getLength() == 5); + assert(i.getConstant() == 0); // last 2 bytes MBZ } else { // Make sure there is nothing else to write. assert(i.getLength() == ((bc == _ldc)?2:3)); diff --git a/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java b/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java index fc9d09988702b3081b6f833833d12270de65ed00..12316768bb2d98f985f033c0339f9ccb88462572 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java +++ b/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,10 @@ import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry; import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; +import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry; +import com.sun.java.util.jar.pack.ConstantPool.MethodTypeEntry; +import com.sun.java.util.jar.pack.ConstantPool.InvokeDynamicEntry; +import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry; import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; import java.util.HashMap; @@ -56,6 +60,10 @@ class TLGlobals { private final Map signatureEntries; private final Map descriptorEntries; private final Map memberEntries; + private final Map methodHandleEntries; + private final Map methodTypeEntries; + private final Map invokeDynamicEntries; + private final Map bootstrapMethodEntries; TLGlobals() { utf8Entries = new HashMap<>(); @@ -64,6 +72,10 @@ class TLGlobals { signatureEntries = new HashMap<>(); descriptorEntries = new HashMap<>(); memberEntries = new HashMap<>(); + methodHandleEntries = new HashMap<>(); + methodTypeEntries = new HashMap<>(); + invokeDynamicEntries = new HashMap<>(); + bootstrapMethodEntries = new HashMap<>(); props = new PropMap(); } @@ -94,4 +106,20 @@ class TLGlobals { Map getMemberEntries() { return memberEntries; } + + Map getMethodHandleEntries() { + return methodHandleEntries; + } + + Map getMethodTypeEntries() { + return methodTypeEntries; + } + + Map getInvokeDynamicEntries() { + return invokeDynamicEntries; + } + + Map getBootstrapMethodEntries() { + return bootstrapMethodEntries; + } } diff --git a/src/share/classes/com/sun/java/util/jar/pack/Utils.java b/src/share/classes/com/sun/java/util/jar/pack/Utils.java index f9d26eb306a301a33735dc437e03f48a39657981..06ecaef5bbfd10c06b31184f9eb876adb131b06b 100644 --- a/src/share/classes/com/sun/java/util/jar/pack/Utils.java +++ b/src/share/classes/com/sun/java/util/jar/pack/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,7 +60,7 @@ class Utils { * If >3, print tons of comments (e.g., processing of references). * (installer only) */ - static final String DEBUG_VERBOSE = Utils.COM_PREFIX+"verbose"; + static final String DEBUG_VERBOSE = COM_PREFIX+"verbose"; /* * Disables use of native code, prefers the Java-coded implementation. @@ -134,35 +134,11 @@ class Utils { // to the engine code, especially the native code. static final ThreadLocal currentInstance = new ThreadLocal<>(); - // convenience methods to access the TL globals + // convenience method to access the TL globals static TLGlobals getTLGlobals() { return currentInstance.get(); } - static Map getUtf8Entries() { - return getTLGlobals().getUtf8Entries(); - } - - static Map getClassEntries() { - return getTLGlobals().getClassEntries(); - } - - static Map getLiteralEntries() { - return getTLGlobals().getLiteralEntries(); - } - - static Map getDescriptorEntries() { - return getTLGlobals().getDescriptorEntries(); - } - - static Map getSignatureEntries() { - return getTLGlobals().getSignatureEntries(); - } - - static Map getMemberEntries() { - return getTLGlobals().getMemberEntries(); - } - static PropMap currentPropMap() { Object obj = currentInstance.get(); if (obj instanceof PackerImpl) @@ -173,8 +149,19 @@ class Utils { } static final boolean nolog - = Boolean.getBoolean(Utils.COM_PREFIX+"nolog"); + = Boolean.getBoolean(COM_PREFIX+"nolog"); + + static final boolean SORT_MEMBERS_DESCR_MAJOR + = Boolean.getBoolean(COM_PREFIX+"sort.members.descr.major"); + + static final boolean SORT_HANDLES_KIND_MAJOR + = Boolean.getBoolean(COM_PREFIX+"sort.handles.kind.major"); + + static final boolean SORT_INDY_BSS_MAJOR + = Boolean.getBoolean(COM_PREFIX+"sort.indy.bss.major"); + static final boolean SORT_BSS_BSM_MAJOR + = Boolean.getBoolean(COM_PREFIX+"sort.bss.bsm.major"); static class Pack200Logger { private final String name; diff --git a/src/share/native/com/sun/java/util/jar/pack/bands.cpp b/src/share/native/com/sun/java/util/jar/pack/bands.cpp index 88955faec343d99ce3843ad538d86fe7c65863b2..08cdc8d3e76030218928e39f0261daa6450acf57 100644 --- a/src/share/native/com/sun/java/util/jar/pack/bands.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/bands.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -188,9 +188,13 @@ void band::setIndexByTag(byte tag) { entry* band::getRefCommon(cpindex* ix_, bool nullOKwithCaller) { CHECK_0; assert(ix_->ixTag == ixTag - || (ixTag == CONSTANT_Literal - && ix_->ixTag >= CONSTANT_Integer - && ix_->ixTag <= CONSTANT_String)); + || ((ixTag == CONSTANT_All || + ixTag == CONSTANT_LoadableValue || + ixTag == CONSTANT_AnyMember) + || (ixTag == CONSTANT_FieldSpecific && + ix_->ixTag >= CONSTANT_Integer && + ix_->ixTag <= CONSTANT_String)) + ); int n = vs[0].getInt() - nullOK; // Note: band-local nullOK means null encodes as 0. // But nullOKwithCaller means caller is willing to tolerate a null. @@ -270,22 +274,15 @@ int band::getIntCount(int tag) { #define NO_INDEX 0 struct band_init { -#ifndef PRODUCT int bn; const char* name; -#endif int defc; int index; }; -#ifdef PRODUCT -#define BAND_INIT(name, cspec, ix) \ - { cspec, ix } -#else #define BAND_INIT(name, cspec, ix) \ { e_##name, #name, /*debug only*/ \ cspec, ix } -#endif const band_init all_band_inits[] = { //BAND_INIT(archive_magic, BYTE1_spec, 0), @@ -314,6 +311,14 @@ const band_init all_band_inits[] = { BAND_INIT(cp_Method_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), BAND_INIT(cp_Imethod_class, DELTA5_spec, INDEX(CONSTANT_Class)), BAND_INIT(cp_Imethod_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), + BAND_INIT(cp_MethodHandle_refkind, DELTA5_spec, 0), + BAND_INIT(cp_MethodHandle_member, UDELTA5_spec, INDEX(CONSTANT_AnyMember)), + BAND_INIT(cp_MethodType, UDELTA5_spec, INDEX(CONSTANT_Signature)), + BAND_INIT(cp_BootstrapMethod_ref, DELTA5_spec, INDEX(CONSTANT_MethodHandle)), + BAND_INIT(cp_BootstrapMethod_arg_count, UDELTA5_spec, 0), + BAND_INIT(cp_BootstrapMethod_arg, DELTA5_spec, INDEX(CONSTANT_LoadableValue)), + BAND_INIT(cp_InvokeDynamic_spec, DELTA5_spec, INDEX(CONSTANT_BootstrapMethod)), + BAND_INIT(cp_InvokeDynamic_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), BAND_INIT(attr_definition_headers, BYTE1_spec, 0), BAND_INIT(attr_definition_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), BAND_INIT(attr_definition_layout, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), @@ -333,7 +338,7 @@ const band_init all_band_inits[] = { BAND_INIT(field_attr_count, UNSIGNED5_spec, 0), BAND_INIT(field_attr_indexes, UNSIGNED5_spec, 0), BAND_INIT(field_attr_calls, UNSIGNED5_spec, 0), - BAND_INIT(field_ConstantValue_KQ, UNSIGNED5_spec, INDEX(CONSTANT_Literal)), + BAND_INIT(field_ConstantValue_KQ, UNSIGNED5_spec, INDEX(CONSTANT_FieldSpecific)), BAND_INIT(field_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)), BAND_INIT(field_metadata_bands, -1, -1), BAND_INIT(field_attr_bands, -1, -1), @@ -415,10 +420,12 @@ const band_init all_band_inits[] = { BAND_INIT(bc_longref, DELTA5_spec, INDEX(CONSTANT_Long)), BAND_INIT(bc_doubleref, DELTA5_spec, INDEX(CONSTANT_Double)), BAND_INIT(bc_stringref, DELTA5_spec, INDEX(CONSTANT_String)), + BAND_INIT(bc_loadablevalueref, DELTA5_spec, INDEX(CONSTANT_LoadableValue)), BAND_INIT(bc_classref, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)), BAND_INIT(bc_fieldref, DELTA5_spec, INDEX(CONSTANT_Fieldref)), BAND_INIT(bc_methodref, UNSIGNED5_spec, INDEX(CONSTANT_Methodref)), BAND_INIT(bc_imethodref, DELTA5_spec, INDEX(CONSTANT_InterfaceMethodref)), + BAND_INIT(bc_indyref, DELTA5_spec, INDEX(CONSTANT_InvokeDynamic)), BAND_INIT(bc_thisfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)), BAND_INIT(bc_superfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)), BAND_INIT(bc_thismethod, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)), @@ -471,7 +478,7 @@ void band::initIndexes(unpacker* u) { for (int i = 0; i < BAND_LIMIT; i++) { band* scan = &tmp_all_bands[i]; uint tag = scan->ixTag; // Cf. #define INDEX(tag) above - if (tag != 0 && tag != CONSTANT_Literal && (tag & SUBINDEX_BIT) == 0) { + if (tag != 0 && tag != CONSTANT_FieldSpecific && (tag & SUBINDEX_BIT) == 0) { scan->setIndex(u->cp.getIndex(tag)); } } diff --git a/src/share/native/com/sun/java/util/jar/pack/bands.h b/src/share/native/com/sun/java/util/jar/pack/bands.h index 8e15dcb011121f46c11e67a31b94524e9180db60..b8e322aa1db7ec65f4e2a0b0e466999be4fa2047 100644 --- a/src/share/native/com/sun/java/util/jar/pack/bands.h +++ b/src/share/native/com/sun/java/util/jar/pack/bands.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,9 +29,7 @@ struct cpindex; struct unpacker; struct band { -#ifndef PRODUCT const char* name; -#endif int bn; // band_number of this band coding* defc; // default coding method cpindex* ix; // CP entry mapping, if CPRefBand @@ -162,6 +160,14 @@ enum band_number { e_cp_Method_desc, e_cp_Imethod_class, e_cp_Imethod_desc, + e_cp_MethodHandle_refkind, + e_cp_MethodHandle_member, + e_cp_MethodType, + e_cp_BootstrapMethod_ref, + e_cp_BootstrapMethod_arg_count, + e_cp_BootstrapMethod_arg, + e_cp_InvokeDynamic_spec, + e_cp_InvokeDynamic_desc, // bands which define transmission of attributes e_attr_definition_headers, @@ -284,11 +290,13 @@ enum band_number { e_bc_longref, e_bc_doubleref, e_bc_stringref, + e_bc_loadablevalueref, e_bc_classref, e_bc_fieldref, e_bc_methodref, e_bc_imethodref, + e_bc_indyref, // _self_linker_op family e_bc_thisfield, @@ -343,6 +351,14 @@ enum band_number { #define cp_Method_desc all_bands[e_cp_Method_desc] #define cp_Imethod_class all_bands[e_cp_Imethod_class] #define cp_Imethod_desc all_bands[e_cp_Imethod_desc] +#define cp_MethodHandle_refkind all_bands[e_cp_MethodHandle_refkind] +#define cp_MethodHandle_member all_bands[e_cp_MethodHandle_member] +#define cp_MethodType all_bands[e_cp_MethodType] +#define cp_BootstrapMethod_ref all_bands[e_cp_BootstrapMethod_ref] +#define cp_BootstrapMethod_arg_count all_bands[e_cp_BootstrapMethod_arg_count] +#define cp_BootstrapMethod_arg all_bands[e_cp_BootstrapMethod_arg] +#define cp_InvokeDynamic_spec all_bands[e_cp_InvokeDynamic_spec] +#define cp_InvokeDynamic_desc all_bands[e_cp_InvokeDynamic_desc] #define attr_definition_headers all_bands[e_attr_definition_headers] #define attr_definition_name all_bands[e_attr_definition_name] #define attr_definition_layout all_bands[e_attr_definition_layout] @@ -437,10 +453,12 @@ enum band_number { #define bc_longref all_bands[e_bc_longref] #define bc_doubleref all_bands[e_bc_doubleref] #define bc_stringref all_bands[e_bc_stringref] +#define bc_loadablevalueref all_bands[e_bc_loadablevalueref] #define bc_classref all_bands[e_bc_classref] #define bc_fieldref all_bands[e_bc_fieldref] #define bc_methodref all_bands[e_bc_methodref] #define bc_imethodref all_bands[e_bc_imethodref] +#define bc_indyref all_bands[e_bc_indyref] #define bc_thisfield all_bands[e_bc_thisfield] #define bc_superfield all_bands[e_bc_superfield] #define bc_thismethod all_bands[e_bc_thismethod] diff --git a/src/share/native/com/sun/java/util/jar/pack/constants.h b/src/share/native/com/sun/java/util/jar/pack/constants.h index 22a6b6c3e58f131e9b268113afffd3429f55ae3e..a3a8c553368c923b7026e29ac8b96566c7c10a97 100644 --- a/src/share/native/com/sun/java/util/jar/pack/constants.h +++ b/src/share/native/com/sun/java/util/jar/pack/constants.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,61 +49,82 @@ #define JAVA6_PACKAGE_MAJOR_VERSION 160 #define JAVA6_PACKAGE_MINOR_VERSION 1 +#define JAVA7_PACKAGE_MAJOR_VERSION 170 +#define JAVA7_PACKAGE_MINOR_VERSION 1 // magic number for gzip streams (for processing pack200-gzip data) #define GZIP_MAGIC 0x1F8B0800 #define GZIP_MAGIC_MASK 0xFFFFFF00 // last byte is variable "flg" field enum { - CONSTANT_None, - CONSTANT_Utf8, - CONSTANT_unused2, /* unused, was Unicode */ - CONSTANT_Integer, - CONSTANT_Float, - CONSTANT_Long, - CONSTANT_Double, - CONSTANT_Class, - CONSTANT_String, - CONSTANT_Fieldref, - CONSTANT_Methodref, - CONSTANT_InterfaceMethodref, - CONSTANT_NameandType, + CONSTANT_None = 0, + CONSTANT_Utf8 = 1, + CONSTANT_unused = 2, /* unused, was Unicode */ + CONSTANT_Integer = 3, + CONSTANT_Float = 4, + CONSTANT_Long = 5, + CONSTANT_Double = 6, + CONSTANT_Class = 7, + CONSTANT_String = 8, + CONSTANT_Fieldref = 9, + CONSTANT_Methodref = 10, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_NameandType = 12, + CONSTANT_unused13 = 13, + CONSTANT_unused14 = 14, + CONSTANT_MethodHandle = 15, + CONSTANT_MethodType = 16, + CONSTANT_unused17 = 17, + CONSTANT_InvokeDynamic = 18, + CONSTANT_Limit = 19, + CONSTANT_Signature = CONSTANT_unused13, + CONSTANT_BootstrapMethod = CONSTANT_unused17, // used only for InvokeDynamic + CONSTANT_All = 50, // combined global map + CONSTANT_LoadableValue = 51, // used for 'KL' and qldc operands + CONSTANT_AnyMember = 52, // union of refs to field or (interface) method + CONSTANT_FieldSpecific = 53, // used only for 'KQ' ConstantValue attrs + CONSTANT_GroupFirst = CONSTANT_All, // start group marker + CONSTANT_GroupLimit = 54, // end group marker - CONSTANT_Signature = 13, - CONSTANT_All = 14, - CONSTANT_Limit = 15, - CONSTANT_NONE = 0, - - CONSTANT_Literal = 20, //pseudo-tag for debugging - CONSTANT_Member = 21, //pseudo-tag for debugging + // CONSTANT_MethodHandle reference kinds + REF_getField = 1, + REF_getStatic = 2, + REF_putField = 3, + REF_putStatic = 4, + REF_invokeVirtual = 5, + REF_invokeStatic = 6, + REF_invokeSpecial = 7, + REF_newInvokeSpecial = 8, + REF_invokeInterface = 9, SUBINDEX_BIT = 64, // combined with CONSTANT_xxx for ixTag ACC_STATIC = 0x0008, ACC_IC_LONG_FORM = (1<<16), //for ic_flags - CLASS_ATTR_SourceFile = 17, - CLASS_ATTR_EnclosingMethod = 18, - CLASS_ATTR_InnerClasses = 23, - CLASS_ATTR_ClassFile_version = 24, - FIELD_ATTR_ConstantValue = 17, - METHOD_ATTR_Code = 17, - METHOD_ATTR_Exceptions = 18, - METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23, + CLASS_ATTR_SourceFile = 17, + CLASS_ATTR_EnclosingMethod = 18, + CLASS_ATTR_InnerClasses = 23, + CLASS_ATTR_ClassFile_version = 24, + CLASS_ATTR_BootstrapMethods = 25, + FIELD_ATTR_ConstantValue = 17, + METHOD_ATTR_Code = 17, + METHOD_ATTR_Exceptions = 18, + METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23, METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24, - METHOD_ATTR_AnnotationDefault = 25, - CODE_ATTR_StackMapTable = 0, - CODE_ATTR_LineNumberTable = 1, - CODE_ATTR_LocalVariableTable = 2, + METHOD_ATTR_AnnotationDefault = 25, + CODE_ATTR_StackMapTable = 0, + CODE_ATTR_LineNumberTable = 1, + CODE_ATTR_LocalVariableTable = 2, CODE_ATTR_LocalVariableTypeTable = 3, //X_ATTR_Synthetic = 12, // ACC_SYNTHETIC; not predefined - X_ATTR_Signature = 19, - X_ATTR_Deprecated = 20, - X_ATTR_RuntimeVisibleAnnotations = 21, + X_ATTR_Signature = 19, + X_ATTR_Deprecated = 20, + X_ATTR_RuntimeVisibleAnnotations = 21, X_ATTR_RuntimeInvisibleAnnotations = 22, - X_ATTR_OVERFLOW = 16, - X_ATTR_LIMIT_NO_FLAGS_HI = 32, - X_ATTR_LIMIT_FLAGS_HI = 63, + X_ATTR_OVERFLOW = 16, + X_ATTR_LIMIT_NO_FLAGS_HI = 32, + X_ATTR_LIMIT_FLAGS_HI = 63, #define O_ATTR_DO(F) \ F(X_ATTR_OVERFLOW,01) \ @@ -121,6 +142,7 @@ enum { F(CLASS_ATTR_InnerClasses,InnerClasses) \ F(CLASS_ATTR_EnclosingMethod,EnclosingMethod) \ F(CLASS_ATTR_ClassFile_version,02) \ + F(CLASS_ATTR_BootstrapMethods,BootstrapMethods) \ /*(end)*/ #define FIELD_ATTR_DO(F) \ F(FIELD_ATTR_ConstantValue,ConstantValue) \ @@ -175,7 +197,7 @@ enum { AO_HAVE_SPECIAL_FORMATS = 1<<0, AO_HAVE_CP_NUMBERS = 1<<1, AO_HAVE_ALL_CODE_FLAGS = 1<<2, - AO_3_UNUSED_MBZ = 1<<3, + AO_HAVE_CP_EXTRAS = 1<<3, AO_HAVE_FILE_HEADERS = 1<<4, AO_DEFLATE_HINT = 1<<5, AO_HAVE_FILE_MODTIME = 1<<6, @@ -185,11 +207,13 @@ enum { AO_HAVE_FIELD_FLAGS_HI = 1<<10, AO_HAVE_METHOD_FLAGS_HI = 1<<11, AO_HAVE_CODE_FLAGS_HI = 1<<12, + AO_UNUSED_MBZ = (-1)<<13, // options bits reserved for future use. + #define ARCHIVE_BIT_DO(F) \ F(AO_HAVE_SPECIAL_FORMATS) \ F(AO_HAVE_CP_NUMBERS) \ F(AO_HAVE_ALL_CODE_FLAGS) \ - /*F(AO_3_UNUSED_MBZ)*/ \ + F(AO_HAVE_CP_EXTRAS) \ F(AO_HAVE_FILE_HEADERS) \ F(AO_DEFLATE_HINT) \ F(AO_HAVE_FILE_MODTIME) \ @@ -215,14 +239,14 @@ enum { NO_MODTIME = 0, // null modtime value // meta-coding - _meta_default = 0, + _meta_default = 0, _meta_canon_min = 1, _meta_canon_max = 115, - _meta_arb = 116, - _meta_run = 117, - _meta_pop = 141, - _meta_limit = 189, - _meta_error = 255, + _meta_arb = 116, + _meta_run = 117, + _meta_pop = 141, + _meta_limit = 189, + _meta_error = 255, _xxx_1_end }; @@ -416,7 +440,7 @@ enum { bc_invokespecial = 183, // 0xb7 bc_invokestatic = 184, // 0xb8 bc_invokeinterface = 185, // 0xb9 - bc_xxxunusedxxx = 186, // 0xba + bc_invokedynamic = 186, // 0xba bc_new = 187, // 0xbb bc_newarray = 188, // 0xbc bc_anewarray = 189, // 0xbd @@ -455,17 +479,19 @@ enum { _invokeinit_limit = _invokeinit_op+3, _xldc_op = _invokeinit_limit, - bc_aldc = bc_ldc, + bc_sldc = bc_ldc, // previously named bc_aldc bc_cldc = _xldc_op+0, bc_ildc = _xldc_op+1, bc_fldc = _xldc_op+2, - bc_aldc_w = bc_ldc_w, + bc_sldc_w = bc_ldc_w, // previously named bc_aldc_w bc_cldc_w = _xldc_op+3, bc_ildc_w = _xldc_op+4, bc_fldc_w = _xldc_op+5, bc_lldc2_w = bc_ldc2_w, bc_dldc2_w = _xldc_op+6, - _xldc_limit = _xldc_op+7, - + // anything other primitive, string, or class must be handled with qldc: + bc_qldc = _xldc_op+7, + bc_qldc_w = _xldc_op+8, + _xldc_limit = _xldc_op+9, _xxx_3_end }; diff --git a/src/share/native/com/sun/java/util/jar/pack/defines.h b/src/share/native/com/sun/java/util/jar/pack/defines.h index e49cade19b880ba612d066a9baabc3ee9aabce72..7fe36ffe5760a1565ade008e42cbd75812a83ede 100644 --- a/src/share/native/com/sun/java/util/jar/pack/defines.h +++ b/src/share/native/com/sun/java/util/jar/pack/defines.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -109,7 +109,8 @@ typedef DWORDLONG julong; #define dup2(a,b) _dup2(a,b) #define strcasecmp(s1, s2) _stricmp(s1,s2) #define tempname _tempname -#define sleep Sleep +#define sleep Sleep +#define snprintf _snprintf #else typedef signed char byte; #ifdef _LP64 diff --git a/src/share/native/com/sun/java/util/jar/pack/unpack.cpp b/src/share/native/com/sun/java/util/jar/pack/unpack.cpp index cee146d4db44e8e88893172882189c4481cf6e9d..d7c51978ded07e0d34212ea2f94630f4a70fb376 100644 --- a/src/share/native/com/sun/java/util/jar/pack/unpack.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/unpack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,7 +81,12 @@ static const byte TAGS_IN_ORDER[] = { CONSTANT_NameandType, CONSTANT_Fieldref, CONSTANT_Methodref, - CONSTANT_InterfaceMethodref + CONSTANT_InterfaceMethodref, + // constants defined as of JDK 7 + CONSTANT_MethodHandle, + CONSTANT_MethodType, + CONSTANT_BootstrapMethod, + CONSTANT_InvokeDynamic }; #define N_TAGS_IN_ORDER (sizeof TAGS_IN_ORDER) @@ -101,6 +106,11 @@ static const char* TAG_NAME[] = { "InterfaceMethodref", "NameandType", "*Signature", + "unused14", + "MethodHandle", + "MethodType", + "*BootstrapMethod", + "InvokeDynamic", 0 }; @@ -114,9 +124,13 @@ static const char* ATTR_CONTEXT_NAME[] = { // match ATTR_CONTEXT_NAME, etc. #endif - -// REQUESTED must be -2 for u2 and REQUESTED_LDC must be -1 for u1 -enum { NOT_REQUESTED = 0, REQUESTED = -2, REQUESTED_LDC = -1 }; +// Note that REQUESTED_LDC comes first, then the normal REQUESTED, +// in the regular constant pool. +enum { REQUESTED_NONE = -1, + // The codes below REQUESTED_NONE are in constant pool output order, + // for the sake of outputEntry_cmp: + REQUESTED_LDC = -99, REQUESTED +}; #define NO_INORD ((uint)-1) @@ -146,7 +160,7 @@ struct entry { void requestOutputIndex(cpool& cp, int req = REQUESTED); int getOutputIndex() { - assert(outputIndex > NOT_REQUESTED); + assert(outputIndex > REQUESTED_NONE); return outputIndex; } @@ -167,12 +181,12 @@ struct entry { } entry* memberClass() { - assert(tagMatches(CONSTANT_Member)); + assert(tagMatches(CONSTANT_AnyMember)); return ref(0); } entry* memberDescr() { - assert(tagMatches(CONSTANT_Member)); + assert(tagMatches(CONSTANT_AnyMember)); return ref(1); } @@ -199,9 +213,9 @@ struct entry { return (tag2 == tag) || (tag2 == CONSTANT_Utf8 && tag == CONSTANT_Signature) #ifndef PRODUCT - || (tag2 == CONSTANT_Literal + || (tag2 == CONSTANT_FieldSpecific && tag >= CONSTANT_Integer && tag <= CONSTANT_String && tag != CONSTANT_Class) - || (tag2 == CONSTANT_Member + || (tag2 == CONSTANT_AnyMember && tag >= CONSTANT_Fieldref && tag <= CONSTANT_InterfaceMethodref) #endif ; @@ -309,6 +323,7 @@ void unpacker::free() { code_fixup_offset.free(); code_fixup_source.free(); requested_ics.free(); + cp.requested_bsms.free(); cur_classfile_head.free(); cur_classfile_tail.free(); for (i = 0; i < ATTR_CONTEXT_LIMIT; i++) @@ -448,12 +463,12 @@ maybe_inline int unpacker::putref_index(entry* e, int size) { if (e == null) return 0; - else if (e->outputIndex > NOT_REQUESTED) + else if (e->outputIndex > REQUESTED_NONE) return e->outputIndex; else if (e->tag == CONSTANT_Signature) return putref_index(e->ref(0), size); else { - e->requestOutputIndex(cp, -size); + e->requestOutputIndex(cp, (size == 1 ? REQUESTED_LDC : REQUESTED)); // Later on we'll fix the bits. class_fixup_type.addByte(size); class_fixup_offset.add((int)wpoffset()); @@ -515,28 +530,33 @@ void unpacker::saveTo(bytes& b, byte* ptr, size_t len) { b.copyFrom(ptr, len); } +bool testBit(int archive_options, int bitMask) { + return (archive_options & bitMask) != 0; +} + // Read up through band_headers. // Do the archive_size dance to set the size of the input mega-buffer. void unpacker::read_file_header() { // Read file header to determine file type and total size. enum { MAGIC_BYTES = 4, - AH_LENGTH_0 = 3, //minver, majver, options are outside of archive_size + AH_LENGTH_0 = 3, // archive_header_0 = {minver, majver, options} + AH_LENGTH_MIN = 15, // observed in spec {header_0[3], cp_counts[8], class_counts[4]} AH_LENGTH_0_MAX = AH_LENGTH_0 + 1, // options might have 2 bytes - AH_LENGTH = 26, //maximum archive header length (w/ all fields) + AH_LENGTH = 30, //maximum archive header length (w/ all fields) // Length contributions from optional header fields: - AH_FILE_HEADER_LEN = 5, // sizehi/lo/next/modtime/files - AH_ARCHIVE_SIZE_LEN = 2, // sizehi/lo only; part of AH_FILE_HEADER_LEN - AH_CP_NUMBER_LEN = 4, // int/float/long/double - AH_SPECIAL_FORMAT_LEN = 2, // layouts/band-headers - AH_LENGTH_MIN = AH_LENGTH - -(AH_FILE_HEADER_LEN+AH_SPECIAL_FORMAT_LEN+AH_CP_NUMBER_LEN), - ARCHIVE_SIZE_MIN = AH_LENGTH_MIN - (AH_LENGTH_0 + AH_ARCHIVE_SIZE_LEN), + AH_LENGTH_S = 2, // archive_header_S = optional {size_hi, size_lo} + AH_ARCHIVE_SIZE_HI = 0, // offset in archive_header_S + AH_ARCHIVE_SIZE_LO = 1, // offset in archive_header_S + AH_FILE_HEADER_LEN = 5, // file_counts = {{size_hi, size_lo), next, modtile, files} + AH_SPECIAL_FORMAT_LEN = 2, // special_count = {layouts, band_headers} + AH_CP_NUMBER_LEN = 4, // cp_number_counts = {int, float, long, double} + AH_CP_EXTRA_LEN = 4, // cp_attr_counts = {MH, MT, InDy, BSM} + ARCHIVE_SIZE_MIN = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S, FIRST_READ = MAGIC_BYTES + AH_LENGTH_MIN }; assert(AH_LENGTH_MIN == 15); // # of UNSIGNED5 fields required after archive_magic - assert(ARCHIVE_SIZE_MIN == 10); // # of UNSIGNED5 fields required after archive_size // An absolute minimum null archive is magic[4], {minver,majver,options}[3], // archive_size[0], cp_counts[8], class_counts[4], for a total of 19 bytes. // (Note that archive_size is optional; it may be 0..10 bytes in length.) @@ -622,23 +642,32 @@ void unpacker::read_file_header() { // Read the first 3 values from the header. value_stream hdr; int hdrVals = 0; - int hdrValsSkipped = 0; // debug only + int hdrValsSkipped = 0; // for assert hdr.init(rp, rplimit, UNSIGNED5_spec); minver = hdr.getInt(); majver = hdr.getInt(); hdrVals += 2; - if (magic != (int)JAVA_PACKAGE_MAGIC || - (majver != JAVA5_PACKAGE_MAJOR_VERSION && - majver != JAVA6_PACKAGE_MAJOR_VERSION) || - (minver != JAVA5_PACKAGE_MINOR_VERSION && - minver != JAVA6_PACKAGE_MINOR_VERSION)) { + int majmin[3][2] = { + {JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION}, + {JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION}, + {JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION} + }; + int majminfound = false; + for (int i = 0 ; i < 3 ; i++) { + if (majver == majmin[i][0] && minver == majmin[i][1]) { + majminfound = true; + break; + } + } + if (majminfound == null) { char message[200]; sprintf(message, "@" ERROR_FORMAT ": magic/ver = " - "%08X/%d.%d should be %08X/%d.%d OR %08X/%d.%d\n", + "%08X/%d.%d should be %08X/%d.%d OR %08X/%d.%d OR %08X/%d.%d\n", magic, majver, minver, JAVA_PACKAGE_MAGIC, JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION, - JAVA_PACKAGE_MAGIC, JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION); + JAVA_PACKAGE_MAGIC, JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION, + JAVA_PACKAGE_MAGIC, JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION); abort(message); } CHECK; @@ -646,18 +675,26 @@ void unpacker::read_file_header() { archive_options = hdr.getInt(); hdrVals += 1; assert(hdrVals == AH_LENGTH_0); // first three fields only - -#define ORBIT(bit) |(bit) - int OPTION_LIMIT = (0 ARCHIVE_BIT_DO(ORBIT)); -#undef ORBIT - if ((archive_options & ~OPTION_LIMIT) != 0) { - fprintf(errstrm, "Warning: Illegal archive options 0x%x\n", - archive_options); - abort("illegal archive options"); + bool haveSizeHi = testBit(archive_options, AO_HAVE_FILE_SIZE_HI); + bool haveModTime = testBit(archive_options, AO_HAVE_FILE_MODTIME); + bool haveFileOpt = testBit(archive_options, AO_HAVE_FILE_OPTIONS); + + bool haveSpecial = testBit(archive_options, AO_HAVE_SPECIAL_FORMATS); + bool haveFiles = testBit(archive_options, AO_HAVE_FILE_HEADERS); + bool haveNumbers = testBit(archive_options, AO_HAVE_CP_NUMBERS); + bool haveCPExtra = testBit(archive_options, AO_HAVE_CP_EXTRAS); + + if (majver < JAVA7_PACKAGE_MAJOR_VERSION) { + if (haveCPExtra) { + abort("Format bits for Java 7 must be zero in previous releases"); + return; + } + } + if (testBit(archive_options, AO_UNUSED_MBZ)) { + abort("High archive option bits are reserved and must be zero"); return; } - - if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) { + if (haveFiles) { uint hi = hdr.getInt(); uint lo = hdr.getInt(); julong x = band::makeLong(hi, lo); @@ -738,13 +775,23 @@ void unpacker::read_file_header() { return; } - // read the rest of the header fields - ensure_input((AH_LENGTH-AH_LENGTH_0) * B_MAX); + // read the rest of the header fields int assertSkipped = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S; + int remainingHeaders = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S; + if (haveSpecial) + remainingHeaders += AH_SPECIAL_FORMAT_LEN; + if (haveFiles) + remainingHeaders += AH_FILE_HEADER_LEN; + if (haveNumbers) + remainingHeaders += AH_CP_NUMBER_LEN; + if (haveCPExtra) + remainingHeaders += AH_CP_EXTRA_LEN; + + ensure_input(remainingHeaders * B_MAX); CHECK; hdr.rp = rp; hdr.rplimit = rplimit; - if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) { + if (haveFiles) { archive_next_count = hdr.getInt(); CHECK_COUNT(archive_next_count); archive_modtime = hdr.getInt(); @@ -755,7 +802,7 @@ void unpacker::read_file_header() { hdrValsSkipped += 3; } - if ((archive_options & AO_HAVE_SPECIAL_FORMATS) != 0) { + if (haveSpecial) { band_headers_size = hdr.getInt(); CHECK_COUNT(band_headers_size); attr_definition_count = hdr.getInt(); @@ -767,7 +814,7 @@ void unpacker::read_file_header() { int cp_counts[N_TAGS_IN_ORDER]; for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) { - if (!(archive_options & AO_HAVE_CP_NUMBERS)) { + if (!haveNumbers) { switch (TAGS_IN_ORDER[k]) { case CONSTANT_Integer: case CONSTANT_Float: @@ -778,6 +825,17 @@ void unpacker::read_file_header() { continue; } } + if (!haveCPExtra) { + switch(TAGS_IN_ORDER[k]) { + case CONSTANT_MethodHandle: + case CONSTANT_MethodType: + case CONSTANT_InvokeDynamic: + case CONSTANT_BootstrapMethod: + cp_counts[k] = 0; + hdrValsSkipped += 1; + continue; + } + } cp_counts[k] = hdr.getInt(); CHECK_COUNT(cp_counts[k]); hdrVals += 1; @@ -791,36 +849,26 @@ void unpacker::read_file_header() { CHECK_COUNT(class_count); hdrVals += 4; - // done with archive_header + // done with archive_header, time to reconcile to ensure + // we have read everything correctly hdrVals += hdrValsSkipped; assert(hdrVals == AH_LENGTH); -#ifndef PRODUCT - int assertSkipped = AH_LENGTH - AH_LENGTH_MIN; - if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) - assertSkipped -= AH_FILE_HEADER_LEN; - if ((archive_options & AO_HAVE_SPECIAL_FORMATS) != 0) - assertSkipped -= AH_SPECIAL_FORMAT_LEN; - if ((archive_options & AO_HAVE_CP_NUMBERS) != 0) - assertSkipped -= AH_CP_NUMBER_LEN; - assert(hdrValsSkipped == assertSkipped); -#endif //PRODUCT - rp = hdr.rp; if (rp > rplimit) abort("EOF reading archive header"); // Now size the CP. #ifndef PRODUCT - bool x = (N_TAGS_IN_ORDER == cpool::NUM_COUNTS); - assert(x); + // bool x = (N_TAGS_IN_ORDER == CONSTANT_Limit); + // assert(x); #endif //PRODUCT cp.init(this, cp_counts); CHECK; default_file_modtime = archive_modtime; - if (default_file_modtime == 0 && !(archive_options & AO_HAVE_FILE_MODTIME)) + if (default_file_modtime == 0 && haveModTime) default_file_modtime = DEFAULT_ARCHIVE_MODTIME; // taken from driver - if ((archive_options & AO_DEFLATE_HINT) != 0) + if (testBit(archive_options, AO_DEFLATE_HINT)) default_file_options |= FO_DEFLATE_HINT; // meta-bytes, if any, immediately follow archive header @@ -876,7 +924,7 @@ void unpacker::finish() { // Cf. PackageReader.readConstantPoolCounts -void cpool::init(unpacker* u_, int counts[NUM_COUNTS]) { +void cpool::init(unpacker* u_, int counts[CONSTANT_Limit]) { this->u = u_; // Fill-pointer for CP. @@ -924,13 +972,16 @@ void cpool::init(unpacker* u_, int counts[NUM_COUNTS]) { first_extra_entry = &entries[nentries]; // Initialize the standard indexes. - tag_count[CONSTANT_All] = nentries; - tag_base[ CONSTANT_All] = 0; for (int tag = 0; tag < CONSTANT_Limit; tag++) { entry* cpMap = &entries[tag_base[tag]]; tag_index[tag].init(tag_count[tag], cpMap, tag); } + // Initialize *all* our entries once + for (int i = 0 ; i < maxentries ; i++) + entries[i].outputIndex = REQUESTED_NONE; + + initGroupIndexes(); // Initialize hashTab to a generous power-of-two size. uint pow2 = 1; uint target = maxentries + maxentries/2; // 60% full @@ -1281,6 +1332,70 @@ void unpacker::read_signature_values(entry* cpMap, int len) { //cp_Signature_classes.done(); } +maybe_inline +void unpacker::checkLegacy(const char* name) { + if (u->majver < JAVA7_PACKAGE_MAJOR_VERSION) { + char message[100]; + snprintf(message, 99, "unexpected band %s\n", name); + abort(message); + } +} + +maybe_inline +void unpacker::read_method_handle(entry* cpMap, int len) { + if (len > 0) { + checkLegacy(cp_MethodHandle_refkind.name); + } + cp_MethodHandle_refkind.readData(len); + cp_MethodHandle_member.setIndexByTag(CONSTANT_AnyMember); + cp_MethodHandle_member.readData(len); + for (int i = 0 ; i < len ; i++) { + entry& e = cpMap[i]; + e.value.i = cp_MethodHandle_refkind.getInt(); + e.refs = U_NEW(entry*, e.nrefs = 1); + e.refs[0] = cp_MethodHandle_member.getRef(); + CHECK; + } +} + +maybe_inline +void unpacker::read_method_type(entry* cpMap, int len) { + if (len > 0) { + checkLegacy(cp_MethodType.name); + } + cp_MethodType.setIndexByTag(CONSTANT_Signature); + cp_MethodType.readData(len); + for (int i = 0 ; i < len ; i++) { + entry& e = cpMap[i]; + e.refs = U_NEW(entry*, e.nrefs = 1); + e.refs[0] = cp_MethodType.getRef(); + } +} + +maybe_inline +void unpacker::read_bootstrap_methods(entry* cpMap, int len) { + if (len > 0) { + checkLegacy(cp_BootstrapMethod_ref.name); + } + cp_BootstrapMethod_ref.setIndexByTag(CONSTANT_MethodHandle); + cp_BootstrapMethod_ref.readData(len); + + cp_BootstrapMethod_arg_count.readData(len); + int totalArgCount = cp_BootstrapMethod_arg_count.getIntTotal(); + cp_BootstrapMethod_arg.setIndexByTag(CONSTANT_LoadableValue); + cp_BootstrapMethod_arg.readData(totalArgCount); + for (int i = 0; i < len; i++) { + entry& e = cpMap[i]; + int argc = cp_BootstrapMethod_arg_count.getInt(); + e.value.i = argc; + e.refs = U_NEW(entry*, e.nrefs = argc + 1); + e.refs[0] = cp_BootstrapMethod_ref.getRef(); + for (int j = 1 ; j < e.nrefs ; j++) { + e.refs[j] = cp_BootstrapMethod_arg.getRef(); + CHECK; + } + } +} // Cf. PackageReader.readConstantPool void unpacker::read_cp() { byte* rp0 = rp; @@ -1298,6 +1413,14 @@ void unpacker::read_cp() { cpMap[i].tag = tag; cpMap[i].inord = i; } + // Initialize the tag's CP index right away, since it might be needed + // in the next pass to initialize the CP for another tag. +#ifndef PRODUCT + cpindex* ix = &cp.tag_index[tag]; + assert(ix->ixTag == tag); + assert((int)ix->len == len); + assert(ix->base1 == cpMap); +#endif switch (tag) { case CONSTANT_Utf8: @@ -1344,19 +1467,27 @@ void unpacker::read_cp() { CONSTANT_Class, CONSTANT_NameandType, cpMap, len); break; + case CONSTANT_MethodHandle: + // consumes cp_MethodHandle_refkind and cp_MethodHandle_member + read_method_handle(cpMap, len); + break; + case CONSTANT_MethodType: + // consumes cp_MethodType + read_method_type(cpMap, len); + break; + case CONSTANT_InvokeDynamic: + read_double_refs(cp_InvokeDynamic_spec, CONSTANT_BootstrapMethod, + CONSTANT_NameandType, + cpMap, len); + break; + case CONSTANT_BootstrapMethod: + // consumes cp_BootstrapMethod_ref, cp_BootstrapMethod_arg_count and cp_BootstrapMethod_arg + read_bootstrap_methods(cpMap, len); + break; default: assert(false); break; } - - // Initialize the tag's CP index right away, since it might be needed - // in the next pass to initialize the CP for another tag. -#ifndef PRODUCT - cpindex* ix = &cp.tag_index[tag]; - assert(ix->ixTag == tag); - assert((int)ix->len == len); - assert(ix->base1 == cpMap); -#endif CHECK; } @@ -1791,7 +1922,12 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res, case 'F': ixTag = CONSTANT_Float; break; case 'D': ixTag = CONSTANT_Double; break; case 'S': ixTag = CONSTANT_String; break; - case 'Q': ixTag = CONSTANT_Literal; break; + case 'Q': ixTag = CONSTANT_FieldSpecific; break; + + // new in 1.7 + case 'M': ixTag = CONSTANT_MethodHandle; break; + case 'T': ixTag = CONSTANT_MethodType; break; + case 'L': ixTag = CONSTANT_LoadableValue; break; } } else { switch (*lp++) { @@ -1803,6 +1939,11 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res, case 'I': ixTag = CONSTANT_InterfaceMethodref; break; case 'U': ixTag = CONSTANT_Utf8; break; //utf8_ref case 'Q': ixTag = CONSTANT_All; break; //untyped_ref + + // new in 1.7 + case 'Y': ixTag = CONSTANT_InvokeDynamic; break; + case 'B': ixTag = CONSTANT_BootstrapMethod; break; + case 'N': ixTag = CONSTANT_AnyMember; break; } } if (ixTag == CONSTANT_None) { @@ -1873,13 +2014,13 @@ void unpacker::read_attr_defs() { // Decide whether bands for the optional high flag words are present. attr_defs[ATTR_CONTEXT_CLASS] - .setHaveLongFlags((archive_options & AO_HAVE_CLASS_FLAGS_HI) != 0); + .setHaveLongFlags(testBit(archive_options, AO_HAVE_CLASS_FLAGS_HI)); attr_defs[ATTR_CONTEXT_FIELD] - .setHaveLongFlags((archive_options & AO_HAVE_FIELD_FLAGS_HI) != 0); + .setHaveLongFlags(testBit(archive_options, AO_HAVE_FIELD_FLAGS_HI)); attr_defs[ATTR_CONTEXT_METHOD] - .setHaveLongFlags((archive_options & AO_HAVE_METHOD_FLAGS_HI) != 0); + .setHaveLongFlags(testBit(archive_options, AO_HAVE_METHOD_FLAGS_HI)); attr_defs[ATTR_CONTEXT_CODE] - .setHaveLongFlags((archive_options & AO_HAVE_CODE_FLAGS_HI) != 0); + .setHaveLongFlags(testBit(archive_options, AO_HAVE_CODE_FLAGS_HI)); // Set up built-in attrs. // (The simple ones are hard-coded. The metadata layouts are not.) @@ -2579,7 +2720,7 @@ void unpacker::putlayout(band** body) { // It has data, so unparse an element. if (b.ixTag != CONSTANT_None) { assert(le_kind == EK_REF); - if (b.ixTag == CONSTANT_Literal) + if (b.ixTag == CONSTANT_FieldSpecific) e = b.getRefUsing(cp.getKQIndex()); else e = b.getRefN(); @@ -2653,13 +2794,13 @@ void unpacker::putlayout(band** body) { void unpacker::read_files() { file_name.readData(file_count); - if ((archive_options & AO_HAVE_FILE_SIZE_HI) != 0) + if (testBit(archive_options, AO_HAVE_FILE_SIZE_HI)) file_size_hi.readData(file_count); file_size_lo.readData(file_count); - if ((archive_options & AO_HAVE_FILE_MODTIME) != 0) + if (testBit(archive_options, AO_HAVE_FILE_MODTIME)) file_modtime.readData(file_count); int allFiles = file_count + class_count; - if ((archive_options & AO_HAVE_FILE_OPTIONS) != 0) { + if (testBit(archive_options, AO_HAVE_FILE_OPTIONS)) { file_options.readData(file_count); // FO_IS_CLASS_STUB might be set, causing overlap between classes and files for (int i = 0; i < file_count; i++) { @@ -2703,7 +2844,7 @@ void unpacker::get_code_header(int& max_stack, max_stack = sc % mod; max_na_locals = sc / mod; // caller must add static, siglen handler_count = nh; - if ((archive_options & AO_HAVE_ALL_CODE_FLAGS) != 0) + if (testBit(archive_options, AO_HAVE_ALL_CODE_FLAGS)) cflags = -1; else cflags = 0; // this one has no attributes @@ -2777,12 +2918,14 @@ band* unpacker::ref_band_for_op(int bc) { return &bc_longref; case bc_dldc2_w: return &bc_doubleref; - case bc_aldc: - case bc_aldc_w: + case bc_sldc: + case bc_sldc_w: return &bc_stringref; case bc_cldc: case bc_cldc_w: return &bc_classref; + case bc_qldc: case bc_qldc_w: + return &bc_loadablevalueref; case bc_getstatic: case bc_putstatic: @@ -2796,6 +2939,8 @@ band* unpacker::ref_band_for_op(int bc) { return &bc_methodref; case bc_invokeinterface: return &bc_imethodref; + case bc_invokedynamic: + return &bc_indyref; case bc_new: case bc_anewarray: @@ -3131,6 +3276,71 @@ void cpool::expandSignatures() { } } +bool isLoadableValue(int tag) { + switch(tag) { + case CONSTANT_Integer: + case CONSTANT_Float: + case CONSTANT_Long: + case CONSTANT_Double: + case CONSTANT_String: + case CONSTANT_Class: + case CONSTANT_MethodHandle: + case CONSTANT_MethodType: + return true; + default: + return false; + } +} +/* + * this method can be used to size an array using null as the parameter, + * thereafter can be reused to initialize the array using a valid pointer + * as a parameter. + */ +int cpool::initLoadableValues(entry** loadable_entries) { + int loadable_count = 0; + for (int i = 0; i < (int)N_TAGS_IN_ORDER; i++) { + int tag = TAGS_IN_ORDER[i]; + if (!isLoadableValue(tag)) + continue; + if (loadable_entries != NULL) { + for (int n = 0 ; n < tag_count[tag] ; n++) { + loadable_entries[loadable_count + n] = &entries[tag_base[tag] + n]; + } + } + loadable_count += tag_count[tag]; + } + return loadable_count; +} + +// Initialize various views into the constant pool. +void cpool::initGroupIndexes() { + // Initialize All + int all_count = 0; + for (int tag = CONSTANT_None ; tag < CONSTANT_Limit ; tag++) { + all_count += tag_count[tag]; + } + entry* all_entries = &entries[tag_base[CONSTANT_None]]; + tag_group_count[CONSTANT_All - CONSTANT_All] = all_count; + tag_group_index[CONSTANT_All - CONSTANT_All].init(all_count, all_entries, CONSTANT_All); + + // Initialize LoadableValues + int loadable_count = initLoadableValues(NULL); + entry** loadable_entries = U_NEW(entry*, loadable_count); + initLoadableValues(loadable_entries); + tag_group_count[CONSTANT_LoadableValue - CONSTANT_All] = loadable_count; + tag_group_index[CONSTANT_LoadableValue - CONSTANT_All].init(loadable_count, + loadable_entries, CONSTANT_LoadableValue); + +// Initialize AnyMembers + int any_count = tag_count[CONSTANT_Fieldref] + + tag_count[CONSTANT_Methodref] + + tag_count[CONSTANT_InterfaceMethodref]; + entry *any_entries = &entries[tag_base[CONSTANT_Fieldref]]; + tag_group_count[CONSTANT_AnyMember - CONSTANT_All] = any_count; + tag_group_index[CONSTANT_AnyMember - CONSTANT_All].init(any_count, + any_entries, CONSTANT_AnyMember); +} + void cpool::initMemberIndexes() { // This function does NOT refer to any class schema. // It is totally internal to the cpool. @@ -3238,13 +3448,13 @@ void cpool::initMemberIndexes() { } void entry::requestOutputIndex(cpool& cp, int req) { - assert(outputIndex <= NOT_REQUESTED); // must not have assigned indexes yet + assert(outputIndex <= REQUESTED_NONE); // must not have assigned indexes yet if (tag == CONSTANT_Signature) { ref(0)->requestOutputIndex(cp, req); return; } assert(req == REQUESTED || req == REQUESTED_LDC); - if (outputIndex != NOT_REQUESTED) { + if (outputIndex != REQUESTED_NONE) { if (req == REQUESTED_LDC) outputIndex = req; // this kind has precedence return; @@ -3252,31 +3462,52 @@ void entry::requestOutputIndex(cpool& cp, int req) { outputIndex = req; //assert(!cp.outputEntries.contains(this)); assert(tag != CONSTANT_Signature); - cp.outputEntries.add(this); + // The BSMs are jetisoned to a side table, however all references + // that the BSMs refer to, need to be considered. + if (tag == CONSTANT_BootstrapMethod) { + // this is a a pseudo-op entry; an attribute will be generated later on + cp.requested_bsms.add(this); + } else { + // all other tag types go into real output file CP: + cp.outputEntries.add(this); + } for (int j = 0; j < nrefs; j++) { ref(j)->requestOutputIndex(cp); } } void cpool::resetOutputIndexes() { - int i; - int noes = outputEntries.length(); + /* + * reset those few entries that are being used in the current class + * (Caution since this method is called after every class written, a loop + * over every global constant pool entry would be a quadratic cost.) + */ + + int noes = outputEntries.length(); entry** oes = (entry**) outputEntries.base(); - for (i = 0; i < noes; i++) { + for (int i = 0 ; i < noes ; i++) { entry& e = *oes[i]; - e.outputIndex = NOT_REQUESTED; + e.outputIndex = REQUESTED_NONE; + } + + // do the same for bsms and reset them if required + int nbsms = requested_bsms.length(); + entry** boes = (entry**) requested_bsms.base(); + for (int i = 0 ; i < nbsms ; i++) { + entry& e = *boes[i]; + e.outputIndex = REQUESTED_NONE; } outputIndexLimit = 0; outputEntries.empty(); #ifndef PRODUCT - // they must all be clear now - for (i = 0; i < (int)nentries; i++) - assert(entries[i].outputIndex == NOT_REQUESTED); + // ensure things are cleared out + for (int i = 0; i < (int)maxentries; i++) + assert(entries[i].outputIndex == REQUESTED_NONE); #endif } static const byte TAG_ORDER[CONSTANT_Limit] = { - 0, 1, 0, 2, 3, 4, 5, 7, 6, 10, 11, 12, 9, 8 + 0, 1, 0, 2, 3, 4, 5, 7, 6, 10, 11, 12, 9, 8, 0, 13, 14, 15, 16 }; extern "C" @@ -3323,10 +3554,18 @@ void cpool::computeOutputIndexes() { if (nentries > 100) checkStep = nentries / 100; for (i = (int)(checkStart++ % checkStep); i < (int)nentries; i += checkStep) { entry& e = entries[i]; - if (e.outputIndex != NOT_REQUESTED) { - assert(outputEntries.contains(&e)); + if (e.tag == CONSTANT_BootstrapMethod) { + if (e.outputIndex != REQUESTED_NONE) { + assert(requested_bsms.contains(&e)); + } else { + assert(!requested_bsms.contains(&e)); + } } else { - assert(!outputEntries.contains(&e)); + if (e.outputIndex != REQUESTED_NONE) { + assert(outputEntries.contains(&e)); + } else { + assert(!outputEntries.contains(&e)); + } } } @@ -3348,7 +3587,7 @@ void cpool::computeOutputIndexes() { int nextIndex = 1; // always skip index #0 in output cpool for (i = 0; i < noes; i++) { entry& e = *oes[i]; - assert(e.outputIndex == REQUESTED || e.outputIndex == REQUESTED_LDC); + assert(e.outputIndex >= REQUESTED_LDC); e.outputIndex = nextIndex++; if (e.isDoubleWord()) nextIndex++; // do not use the next index } @@ -3396,7 +3635,7 @@ char* entry::string() { default: if (nrefs == 0) { buf = getbuf(20); - sprintf((char*)buf.ptr, "", tag); + sprintf((char*)buf.ptr, TAG_NAME[tag]); } else if (nrefs == 1) { return refs[0]->string(); } else { @@ -3674,6 +3913,7 @@ void unpacker::reset_cur_classfile() { class_fixup_offset.empty(); class_fixup_ref.empty(); requested_ics.empty(); + cp.requested_bsms.empty(); } cpindex* cpool::getKQIndex() { @@ -3931,13 +4171,15 @@ void unpacker::write_bc_ops() { case bc_ildc: case bc_cldc: case bc_fldc: - case bc_aldc: + case bc_sldc: + case bc_qldc: origBC = bc_ldc; break; case bc_ildc_w: case bc_cldc_w: case bc_fldc_w: - case bc_aldc_w: + case bc_sldc_w: + case bc_qldc_w: origBC = bc_ldc_w; break; case bc_lldc2_w: @@ -3962,6 +4204,10 @@ void unpacker::write_bc_ops() { int argSize = ref->memberDescr()->descrType()->typeSize(); putu1_fast(1 + argSize); putu1_fast(0); + } else if (origBC == bc_invokedynamic) { + // pad the next two byte + putu1_fast(0); + putu1_fast(0); } continue; } @@ -4353,49 +4599,12 @@ int raw_address_cmp(const void* p1p, const void* p2p) { return (p1 > p2)? 1: (p1 < p2)? -1: 0; } -void unpacker::write_classfile_tail() { - cur_classfile_tail.empty(); - set_output(&cur_classfile_tail); - - int i, num; - - attr_definitions& ad = attr_defs[ATTR_CONTEXT_CLASS]; - - bool haveLongFlags = ad.haveLongFlags(); - julong kflags = class_flags_hi.getLong(class_flags_lo, haveLongFlags); - julong indexMask = ad.flagIndexMask(); - - cur_class = class_this.getRef(); - cur_super = class_super.getRef(); - - CHECK; - - if (cur_super == cur_class) cur_super = null; - // special representation for java/lang/Object - - putu2((ushort)(kflags & ~indexMask)); - putref(cur_class); - putref(cur_super); - - putu2(num = class_interface_count.getInt()); - for (i = 0; i < num; i++) { - putref(class_interface.getRef()); - } - - write_members(class_field_count.getInt(), ATTR_CONTEXT_FIELD); - write_members(class_method_count.getInt(), ATTR_CONTEXT_METHOD); - CHECK; - - cur_class_has_local_ics = false; // may be set true by write_attrs - - - int naOffset = (int)wpoffset(); - int na = write_attrs(ATTR_CONTEXT_CLASS, (kflags & indexMask)); - - - // at the very last, choose which inner classes (if any) pertain to k: +/* + * writes the InnerClass attributes and returns the updated attribute + */ +int unpacker::write_ics(int naOffset, int na) { #ifdef ASSERT - for (i = 0; i < ic_count; i++) { + for (int i = 0; i < ic_count; i++) { assert(!ics[i].requested); } #endif @@ -4416,7 +4625,7 @@ void unpacker::write_classfile_tail() { // include it and all its outers. int noes = cp.outputEntries.length(); entry** oes = (entry**) cp.outputEntries.base(); - for (i = 0; i < noes; i++) { + for (int i = 0; i < noes; i++) { entry& e = *oes[i]; if (e.tag != CONSTANT_Class) continue; // wrong sort for (inner_class* ic = cp.getIC(&e); @@ -4442,10 +4651,10 @@ void unpacker::write_classfile_tail() { // Note: extra_ics will be freed up by next call to get_next_file(). } } - for (i = 0; i < num_extra_ics; i++) { + for (int i = 0; i < num_extra_ics; i++) { inner_class& extra_ic = extra_ics[i]; extra_ic.inner = class_InnerClasses_RC.getRef(); - CHECK; + CHECK_0; // Find the corresponding equivalent global IC: inner_class* global_ic = cp.getIC(extra_ic.inner); int flags = class_InnerClasses_F.getInt(); @@ -4493,7 +4702,7 @@ void unpacker::write_classfile_tail() { putu2(local_ics); PTRLIST_QSORT(requested_ics, raw_address_cmp); int num_global_ics = requested_ics.length(); - for (i = -num_global_ics; i < num_extra_ics; i++) { + for (int i = -num_global_ics; i < num_extra_ics; i++) { inner_class* ic; if (i < 0) ic = (inner_class*) requested_ics.get(num_global_ics+i); @@ -4512,17 +4721,99 @@ void unpacker::write_classfile_tail() { } // Tidy up global 'requested' bits: - for (i = requested_ics.length(); --i >= 0; ) { + for (int i = requested_ics.length(); --i >= 0; ) { inner_class* ic = (inner_class*) requested_ics.get(i); ic->requested = false; } requested_ics.empty(); + return na; +} +/* + * Writes the BootstrapMethods attribute and returns the updated attribute count + */ +int unpacker::write_bsms(int naOffset, int na) { + cur_class_local_bsm_count = cp.requested_bsms.length(); + if (cur_class_local_bsm_count > 0) { + int noes = cp.outputEntries.length(); + entry** oes = (entry**) cp.outputEntries.base(); + PTRLIST_QSORT(cp.requested_bsms, outputEntry_cmp); + // append the BootstrapMethods attribute (after the InnerClasses attr): + putref(cp.sym[cpool::s_BootstrapMethods]); + int sizeOffset = (int)wpoffset(); + byte* sizewp = wp; + putu4(-99); // attr size will be patched + putu2(cur_class_local_bsm_count); + int written_bsms = 0; + for (int i = 0 ; i < cur_class_local_bsm_count ; i++) { + entry* e = (entry*)cp.requested_bsms.get(i); + assert(e->outputIndex != REQUESTED_NONE); + // output index is the index within the array + e->outputIndex = i; + putref(e->refs[0]); // bsm + putu2(e->nrefs-1); // number of args after bsm + for (int j = 1; j < e->nrefs; j++) { + putref(e->refs[j]); + } + written_bsms += 1; + } + assert(written_bsms == cur_class_local_bsm_count); // else insane + putu4_at(sizewp, (int)(wp - (sizewp+4))); // size of code attr + putu2_at(wp_at(naOffset), ++na); // increment class attr count + } + return na; +} + +void unpacker::write_classfile_tail() { + + cur_classfile_tail.empty(); + set_output(&cur_classfile_tail); + + int i, num; + + attr_definitions& ad = attr_defs[ATTR_CONTEXT_CLASS]; + + bool haveLongFlags = ad.haveLongFlags(); + julong kflags = class_flags_hi.getLong(class_flags_lo, haveLongFlags); + julong indexMask = ad.flagIndexMask(); + + cur_class = class_this.getRef(); + cur_super = class_super.getRef(); CHECK; + + if (cur_super == cur_class) cur_super = null; + // special representation for java/lang/Object + + putu2((ushort)(kflags & ~indexMask)); + putref(cur_class); + putref(cur_super); + + putu2(num = class_interface_count.getInt()); + for (i = 0; i < num; i++) { + putref(class_interface.getRef()); + } + + write_members(class_field_count.getInt(), ATTR_CONTEXT_FIELD); + write_members(class_method_count.getInt(), ATTR_CONTEXT_METHOD); + CHECK; + + cur_class_has_local_ics = false; // may be set true by write_attrs + + int naOffset = (int)wpoffset(); // note the attr count location + int na = write_attrs(ATTR_CONTEXT_CLASS, (kflags & indexMask)); + CHECK; + + na = write_bsms(naOffset, na); + CHECK; + + // choose which inner classes (if any) pertain to k: + na = write_ics(naOffset, na); + CHECK; + close_output(); + cp.computeOutputIndexes(); // rewrite CP references in the tail - cp.computeOutputIndexes(); int nextref = 0; for (i = 0; i < (int)class_fixup_type.size(); i++) { int type = class_fixup_type.getByte(i); @@ -4579,9 +4870,18 @@ void unpacker::write_classfile_head() { case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: case CONSTANT_NameandType: + case CONSTANT_InvokeDynamic: putu2(e.refs[0]->getOutputIndex()); putu2(e.refs[1]->getOutputIndex()); break; + case CONSTANT_MethodHandle: + putu1(e.value.i); + putu2(e.refs[0]->getOutputIndex()); + break; + case CONSTANT_MethodType: + putu2(e.refs[0]->getOutputIndex()); + break; + case CONSTANT_BootstrapMethod: // should not happen default: abort(ERROR_INTERNAL); } @@ -4620,11 +4920,11 @@ unpacker::file* unpacker::get_next_file() { entry* e = file_name.getRef(); CHECK_0; cur_file.name = e->utf8String(); - bool haveLongSize = ((archive_options & AO_HAVE_FILE_SIZE_HI) != 0); + bool haveLongSize = (testBit(archive_options, AO_HAVE_FILE_SIZE_HI)); cur_file.size = file_size_hi.getLong(file_size_lo, haveLongSize); - if ((archive_options & AO_HAVE_FILE_MODTIME) != 0) + if (testBit(archive_options, AO_HAVE_FILE_MODTIME)) cur_file.modtime += file_modtime.getInt(); //relative to archive modtime - if ((archive_options & AO_HAVE_FILE_OPTIONS) != 0) + if (testBit(archive_options, AO_HAVE_FILE_OPTIONS)) cur_file.options |= file_options.getInt() & ~suppress_file_options; } else if (classes_written < class_count) { // there is a class for a missing file record diff --git a/src/share/native/com/sun/java/util/jar/pack/unpack.h b/src/share/native/com/sun/java/util/jar/pack/unpack.h index 0f66b7ef6f50c0abd81d9f38e825649af52ed2fa..cec7a88b24e13947368b68087a69d7596f6e477a 100644 --- a/src/share/native/com/sun/java/util/jar/pack/unpack.h +++ b/src/share/native/com/sun/java/util/jar/pack/unpack.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,6 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - - - // Global Structures struct jar; struct gunzip; @@ -70,6 +67,9 @@ struct cpool { cpindex tag_index[CONSTANT_Limit]; ptrlist tag_extras[CONSTANT_Limit]; + int tag_group_count[CONSTANT_GroupLimit - CONSTANT_GroupFirst]; + cpindex tag_group_index[CONSTANT_GroupLimit - CONSTANT_GroupFirst]; + cpindex* member_indexes; // indexed by 2*CONSTANT_Class.inord cpindex* getFieldIndex(entry* classRef); cpindex* getMethodIndex(entry* classRef); @@ -82,6 +82,7 @@ struct cpool { int outputIndexLimit; // index limit after renumbering ptrlist outputEntries; // list of entry* needing output idx assigned + ptrlist requested_bsms; // which bsms need output? entry** hashTab; uint hashTabLength; @@ -100,24 +101,36 @@ struct cpool { entry* sym[s_LIMIT]; // read counts from hdr, allocate main arrays - enum { NUM_COUNTS = 12 }; - void init(unpacker* u, int counts[NUM_COUNTS]); + void init(unpacker* u, int counts[CONSTANT_Limit]); // pointer to outer unpacker, for error checks etc. unpacker* u; int getCount(byte tag) { - assert((uint)tag < CONSTANT_Limit); - return tag_count[tag]; + if ((uint)tag >= CONSTANT_GroupFirst) { + assert((uint)tag < CONSTANT_GroupLimit); + return tag_group_count[(uint)tag - CONSTANT_GroupFirst]; + } else { + assert((uint)tag < CONSTANT_Limit); + return tag_count[(uint)tag]; + } } cpindex* getIndex(byte tag) { - assert((uint)tag < CONSTANT_Limit); - return &tag_index[tag]; + if ((uint)tag >= CONSTANT_GroupFirst) { + assert((uint)tag < CONSTANT_GroupLimit); + return &tag_group_index[(uint)tag - CONSTANT_GroupFirst]; + } else { + assert((uint)tag < CONSTANT_Limit); + return &tag_index[(uint)tag]; + } } + cpindex* getKQIndex(); // uses cur_descr void expandSignatures(); + void initGroupIndexes(); void initMemberIndexes(); + int initLoadableValues(entry** loadable_entries); void computeOutputOrder(); void computeOutputIndexes(); @@ -234,6 +247,7 @@ struct unpacker { int cur_descr_flags; // flags corresponding to cur_descr int cur_class_minver, cur_class_majver; bool cur_class_has_local_ics; + int cur_class_local_bsm_count; fillbytes cur_classfile_head; fillbytes cur_classfile_tail; int files_written; // also tells which file we're working on @@ -412,7 +426,7 @@ struct unpacker { void abort(const char* s = null); bool aborting() { return abort_message != null; } static unpacker* current(); // find current instance - + void checkLegacy(const char* name); // Output management void set_output(fillbytes* which) { assert(wp == null); @@ -464,6 +478,8 @@ struct unpacker { void write_bc_ops(); void write_members(int num, int attrc); // attrc=ATTR_CONTEXT_FIELD/METHOD int write_attrs(int attrc, julong indexBits); + int write_ics(int naOffset, int na); + int write_bsms(int naOffset, int na); // The readers void read_bands(); @@ -484,6 +500,9 @@ struct unpacker { void read_single_refs(band& cp_band, byte refTag, entry* cpMap, int len); void read_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, entry* cpMap, int len); void read_signature_values(entry* cpMap, int len); + void read_method_handle(entry* cpMap, int len); + void read_method_type(entry* cpMap, int len); + void read_bootstrap_methods(entry* cpMap, int len); }; inline void cpool::abort(const char* msg) { u->abort(msg); } diff --git a/test/tools/pack200/AttributeTests.java b/test/tools/pack200/AttributeTests.java index e7107996cc8b28caef2a85cbaffbc82d04214e34..695470830257bd72d65ba757db0874c57e929a86 100644 --- a/test/tools/pack200/AttributeTests.java +++ b/test/tools/pack200/AttributeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ import java.util.Arrays; import java.util.List; /* * @test - * @bug 6982312 + * @bug 6746111 * @summary tests various classfile format and attribute handling by pack200 * @compile -XDignore.symbol.file Utils.java AttributeTests.java * @run main AttributeTests @@ -36,40 +36,8 @@ import java.util.List; public class AttributeTests { public static void main(String... args) throws Exception { - test6982312(); test6746111(); } - /* - * This is an interim test, which ensures pack200 handles JSR-292 related - * classfile changes seamlessly, until all the classfile changes in jdk7 - * and jdk8 are fully supported. At that time this test should be jettisoned, - * along with the associated jar file. - * - * The jar file contains sources and classes noting the classes were - * derived by using the javac from the lambda project, - * see http://openjdk.java.net/projects/lambda/. - * Therefore the classes contained in the jar cannot be compiled, using - * the standard jdk7's javac compiler. - */ - static void test6982312() throws IOException { - String pack200Cmd = Utils.getPack200Cmd(); - File dynJar = new File(".", "dyn.jar"); - Utils.copyFile(new File(Utils.TEST_SRC_DIR, "dyn.jar"), dynJar); - File testJar = new File(".", "test.jar"); - List cmds = new ArrayList(); - cmds.add(pack200Cmd); - cmds.add("--repack"); - cmds.add(testJar.getAbsolutePath()); - cmds.add(dynJar.getAbsolutePath()); - Utils.runExec(cmds); - /* - * compare the repacked jar bit-wise, as all the files - * should be transmitted "as-is". - */ - Utils.doCompareBitWise(dynJar.getAbsoluteFile(), testJar.getAbsoluteFile()); - testJar.delete(); - dynJar.delete(); - } /* * this test checks to see if we get the expected strings for output diff --git a/test/tools/pack200/PackageVersionTest.java b/test/tools/pack200/PackageVersionTest.java index 538b92ecafc7bd043e3be5b8a4af1374a06d9650..8569781a6ec26ce3e7399d9522483727dfb0cde9 100644 --- a/test/tools/pack200/PackageVersionTest.java +++ b/test/tools/pack200/PackageVersionTest.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,9 @@ public class PackageVersionTest { public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160; public final static int JAVA6_PACKAGE_MINOR_VERSION = 1; + public final static int JAVA7_PACKAGE_MAJOR_VERSION = 170; + public final static int JAVA7_PACKAGE_MINOR_VERSION = 1; + public static void main(String... args) { if (!javaHome.getName().endsWith("jre")) { throw new RuntimeException("Error: requires an SDK to run"); @@ -68,9 +71,8 @@ public class PackageVersionTest { verifyPack("Test6.class", JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION); - // TODO: change this to the java7 package version as needed. - verifyPack("Test7.class", JAVA6_PACKAGE_MAJOR_VERSION, - JAVA6_PACKAGE_MINOR_VERSION); + verifyPack("Test7.class", JAVA7_PACKAGE_MAJOR_VERSION, + JAVA7_PACKAGE_MINOR_VERSION); // test for resource file, ie. no class files verifyPack("Test6.java", JAVA5_PACKAGE_MAJOR_VERSION, diff --git a/test/tools/pack200/Utils.java b/test/tools/pack200/Utils.java index 8158f486ca2b63d7502df903a6c4b70a694e721a..0e2269822f5e1f70e3e3ecb8218d25f5ee95c3b6 100644 --- a/test/tools/pack200/Utils.java +++ b/test/tools/pack200/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,8 +129,10 @@ class Utils { init(); List cmds = new ArrayList(); cmds.add(getJavaCmd()); - cmds.add("-jar"); - cmds.add(VerifierJar.getName()); + cmds.add("-cp"); + cmds.add(Utils.locateJar("tools.jar") + + System.getProperty("path.separator") + VerifierJar.getName()); + cmds.add("sun.tools.pack.verify.Main"); cmds.add(reference.getAbsolutePath()); cmds.add(specimen.getAbsolutePath()); cmds.add("-O"); @@ -142,8 +144,10 @@ class Utils { init(); List cmds = new ArrayList(); cmds.add(getJavaCmd()); - cmds.add("-jar"); - cmds.add(VerifierJar.getName()); + cmds.add("-cp"); + cmds.add(Utils.locateJar("tools.jar") + + System.getProperty("path.separator") + VerifierJar.getName()); + cmds.add("sun.tools.pack.verify.Main"); cmds.add(reference.getName()); cmds.add(specimen.getName()); cmds.add("-O"); diff --git a/test/tools/pack200/dyn.jar b/test/tools/pack200/dyn.jar deleted file mode 100644 index b04c2a9e825b1201cbdcd2f863478587356ce7ec..0000000000000000000000000000000000000000 Binary files a/test/tools/pack200/dyn.jar and /dev/null differ diff --git a/test/tools/pack200/pack200-verifier/data/README b/test/tools/pack200/pack200-verifier/data/README index 64278ab13d6fdc417f7307e658dff1af465b7829..be39004f162253d8896921e8c3f164baafa8bef4 100644 --- a/test/tools/pack200/pack200-verifier/data/README +++ b/test/tools/pack200/pack200-verifier/data/README @@ -2,19 +2,19 @@ The files contained in the golden.jar have been harvested from many different sources, some are hand-crafted invalid class files (odds directory), or from random JDK builds. -Generally these files serve to ensure the integrity of the packer and unpacker -by, - 1. maximizing the test coverage. - 2. exercising all the Bands in the pack200 specification. - 2. testing the behavior of the packer with invalid classes. - 3. testing the archive integrity, ordering and description (date, sizes, +Generally these files serve to ensure the integrity of the packer and +unpacker by, + * maximizing the test coverage. + * exercising all the Bands in the pack200 specification. + * testing the behavior of the packer with invalid classes. + * testing the archive integrity, ordering and description (date, sizes, CRC etc.) Build: To rebuild this JAR follow these steps: 1. unzip the golden.jar to some directory lets call it "example" 2. now we can add any directories with files into example. - 2. run the script BUILDME.sh as + 3. run the script BUILDME.sh as % sh BUILDME.sh example Note: the BUILDME.sh is known to work on all Unix platforms as well as Windows @@ -32,7 +32,7 @@ Test: Basic: % pack200 --repack test.jar golden.jar - Advanced: + Advanced: inspection of band contents Create a pack.conf as follows: % cat pack.conf com.sun.java.util.jar.pack.dump.bands=true @@ -41,5 +41,6 @@ Test: --verbose golden.jar.pack golden.jar This command will dump the Bands in a unique directory BD_XXXXXX, - one can inspect the directory to ensure all of the bands are being - generated. Familiarity of the Pack200 specification is suggested. \ No newline at end of file + one can then inspect the directory to ensure all of the bands are being + generated. Familiarity of the Pack200 specification is strongly + suggested. diff --git a/test/tools/pack200/pack200-verifier/data/golden.jar b/test/tools/pack200/pack200-verifier/data/golden.jar index 53b77c41dac4f10e09634c63f3264b693c177dd9..913fe14340e22ba8b47f4fdceb2e0b1919cbd370 100644 Binary files a/test/tools/pack200/pack200-verifier/data/golden.jar and b/test/tools/pack200/pack200-verifier/data/golden.jar differ diff --git a/test/tools/pack200/pack200-verifier/make/build.xml b/test/tools/pack200/pack200-verifier/make/build.xml index 76e5f72cf89b23a635c1936c863e9efa2125c97b..b783ffda881d673fb4106ee5f65ed04bb5f923fc 100644 --- a/test/tools/pack200/pack200-verifier/make/build.xml +++ b/test/tools/pack200/pack200-verifier/make/build.xml @@ -1,5 +1,5 @@ - + @@ -22,7 +22,7 @@ diff --git a/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java b/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java index 4a3af66344e8607fbf28e8269ed624a96ee505dc..0579b5b99cdbfbb846367bb3b5c91f101e550c0d 100644 --- a/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java +++ b/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,16 +24,58 @@ */ package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- +import com.sun.tools.classfile.AccessFlags; +import com.sun.tools.classfile.Annotation; +import com.sun.tools.classfile.Annotation.*; +import com.sun.tools.classfile.AnnotationDefault_attribute; +import com.sun.tools.classfile.Attribute; +import com.sun.tools.classfile.Attributes; +import com.sun.tools.classfile.BootstrapMethods_attribute; +import com.sun.tools.classfile.CharacterRangeTable_attribute; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.CompilationID_attribute; +import com.sun.tools.classfile.ConstantPool; +import com.sun.tools.classfile.ConstantPool.*; +import com.sun.tools.classfile.ConstantPoolException; +import com.sun.tools.classfile.ConstantValue_attribute; +import com.sun.tools.classfile.DefaultAttribute; +import com.sun.tools.classfile.Deprecated_attribute; +import com.sun.tools.classfile.Descriptor.InvalidDescriptor; +import com.sun.tools.classfile.EnclosingMethod_attribute; +import com.sun.tools.classfile.Exceptions_attribute; +import com.sun.tools.classfile.Field; +import com.sun.tools.classfile.InnerClasses_attribute; +import com.sun.tools.classfile.InnerClasses_attribute.Info; +import com.sun.tools.classfile.Instruction; +import com.sun.tools.classfile.Instruction.TypeKind; +import com.sun.tools.classfile.LineNumberTable_attribute; +import com.sun.tools.classfile.LocalVariableTable_attribute; +import com.sun.tools.classfile.LocalVariableTypeTable_attribute; +import com.sun.tools.classfile.Method; +import com.sun.tools.classfile.Opcode; +import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; +import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute; +import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute; +import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute; +import com.sun.tools.classfile.Signature_attribute; +import com.sun.tools.classfile.SourceDebugExtension_attribute; +import com.sun.tools.classfile.SourceFile_attribute; +import com.sun.tools.classfile.SourceID_attribute; +import com.sun.tools.classfile.StackMapTable_attribute; +import com.sun.tools.classfile.StackMapTable_attribute.*; +import com.sun.tools.classfile.StackMap_attribute; +import com.sun.tools.classfile.Synthetic_attribute; import java.util.*; -import java.util.jar.*; -import java.lang.reflect.*; import java.io.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import xmlkit.XMLKit.Element; /* - * @author jrose + * @author jrose, ksrini */ -public class ClassReader extends ClassSyntax { +public class ClassReader { private static final CommandLineParser CLP = new CommandLineParser("" + "-source: +> = \n" @@ -41,23 +83,24 @@ public class ClassReader extends ClassSyntax { + "-encoding: +> = \n" + "-jcov $ \n -nojcov !-jcov \n" + "-verbose $ \n -noverbose !-verbose \n" - + "-pretty $ \n -nopretty !-pretty \n" + "-keepPath $ \n -nokeepPath !-keepPath \n" + "-keepCP $ \n -nokeepCP !-keepCP \n" - + "-keepBytes $ \n -nokeepBytes !-keepBytes \n" - + "-parseBytes $ \n -noparseBytes !-parseBytes \n" - + "-resolveRefs $ \n -noresolveRefs !-resolveRefs \n" + "-keepOrder $ \n -nokeepOrder !-keepOrder \n" - + "-keepSizes $ \n -nokeepSizes !-keepSizes \n" + "-continue $ \n -nocontinue !-continue \n" - + "-attrDef & \n" + "-@ >-@ . \n" + "- +? \n" + "\n"); + + // Protected state for representing the class file. + protected Element cfile; // + protected Element cpool; // + protected Element klass; // + protected List thePool; // stringified flattened Constant Pool + public static void main(String[] ava) throws IOException { - ArrayList av = new ArrayList(Arrays.asList(ava)); - HashMap props = new HashMap(); + ArrayList av = new ArrayList<>(Arrays.asList(ava)); + HashMap props = new HashMap<>(); props.put("-encoding:", "UTF8"); // default props.put("-keepOrder", null); // CLI default props.put("-pretty", "1"); // CLI default @@ -80,7 +123,7 @@ public class ClassReader extends ClassSyntax { } */ if (av.isEmpty()) { - av.add("doit"); //to enter this loop + av.add(""); //to enter this loop } boolean readList = false; for (String a : av) { @@ -119,7 +162,7 @@ public class ClassReader extends ClassSyntax { private static void doFile(String a, File source, File dest, ClassReader options, String encoding, - boolean contError) throws IOException { + boolean contError) throws IOException { if (!contError) { doFile(a, source, dest, options, encoding); } else { @@ -127,40 +170,49 @@ public class ClassReader extends ClassSyntax { doFile(a, source, dest, options, encoding); } catch (Exception ee) { System.out.println("Error processing " + source + ": " + ee); + ee.printStackTrace(); } } } - private static void doJar(String a, File source, File dest, ClassReader options, - String encoding, Boolean contError) throws IOException { + private static void doJar(String a, File source, File dest, + ClassReader options, String encoding, + Boolean contError) throws IOException { try { JarFile jf = new JarFile(source); - for (JarEntry je : Collections.list((Enumeration) jf.entries())) { + for (JarEntry je : Collections.list(jf.entries())) { String name = je.getName(); if (!name.endsWith(".class")) { continue; } - doStream(name, jf.getInputStream(je), dest, options, encoding); + try { + doStream(name, jf.getInputStream(je), dest, options, encoding); + } catch (Exception e) { + if (contError) { + System.out.println("Error processing " + source + ": " + e); + e.printStackTrace(); + continue; + } + } } } catch (IOException ioe) { - if (contError) { - System.out.println("Error processing " + source + ": " + ioe); - } else { - throw ioe; - } + throw ioe; } } private static void doStream(String a, InputStream in, File dest, - ClassReader options, String encoding) throws IOException { + ClassReader options, String encoding) throws IOException { File f = new File(a); ClassReader cr = new ClassReader(options); - Element e = cr.readFrom(in); + Element e; + if (options.verbose) { + System.out.println("Reading " + f); + } + e = cr.readFrom(in); OutputStream out; if (dest == null) { - //System.out.println(e.prettyString()); out = System.out; } else { File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); @@ -202,20 +254,8 @@ public class ClassReader extends ClassSyntax { } - public static BufferedReader makeReader(InputStream in, String encoding) throws IOException { - // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name - if (encoding.equals("8BIT")) { - encoding = EIGHT_BIT_CHAR_ENCODING; - } - if (encoding.equals("UTF8")) { - encoding = UTF8_ENCODING; - } - if (encoding.equals("DEFAULT")) { - encoding = null; - } - if (encoding.equals("-")) { - encoding = null; - } + public static BufferedReader makeReader(InputStream in, + String encoding) throws IOException { Reader inw; in = new BufferedInputStream(in); // add buffering if (encoding == null) { @@ -226,20 +266,8 @@ public class ClassReader extends ClassSyntax { return new BufferedReader(inw); // add buffering } - public static Writer makeWriter(OutputStream out, String encoding) throws IOException { - // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name - if (encoding.equals("8BIT")) { - encoding = EIGHT_BIT_CHAR_ENCODING; - } - if (encoding.equals("UTF8")) { - encoding = UTF8_ENCODING; - } - if (encoding.equals("DEFAULT")) { - encoding = null; - } - if (encoding.equals("-")) { - encoding = null; - } + public static Writer makeWriter(OutputStream out, + String encoding) throws IOException { Writer outw; if (encoding == null) { outw = new OutputStreamWriter(out); @@ -252,12 +280,9 @@ public class ClassReader extends ClassSyntax { public Element result() { return cfile; } + protected InputStream in; protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024); - protected byte cpTag[]; - protected String cpName[]; - protected String[] callables; // varies - public static final String REF_PREFIX = "#"; // input options public boolean pretty = false; public boolean verbose = false; @@ -270,7 +295,7 @@ public class ClassReader extends ClassSyntax { public boolean keepSizes = false; public ClassReader() { - super.cfile = new Element("ClassFile"); + cfile = new Element("ClassFile"); } public ClassReader(ClassReader options) { @@ -283,12 +308,7 @@ public class ClassReader extends ClassSyntax { verbose = options.verbose; keepPath = options.keepPath; keepCP = options.keepCP; - keepBytes = options.keepBytes; - parseBytes = options.parseBytes; - resolveRefs = options.resolveRefs; - keepSizes = options.keepSizes; keepOrder = options.keepOrder; - attrTypes = options.attrTypes; } public void copyOptionsFrom(Map options) { @@ -304,274 +324,177 @@ public class ClassReader extends ClassSyntax { if (options.containsKey("-keepCP")) { keepCP = (options.get("-keepCP") != null); } - if (options.containsKey("-keepBytes")) { - keepBytes = (options.get("-keepBytes") != null); - } - if (options.containsKey("-parseBytes")) { - parseBytes = (options.get("-parseBytes") != null); - } - if (options.containsKey("-resolveRefs")) { - resolveRefs = (options.get("-resolveRefs") != null); - } - if (options.containsKey("-keepSizes")) { - keepSizes = (options.get("-keepSizes") != null); - } if (options.containsKey("-keepOrder")) { keepOrder = (options.get("-keepOrder") != null); } - if (options.containsKey("-attrDef")) { - addAttrTypes(options.get("-attrDef").split(" ")); - } - if (options.get("-jcov") != null) { - addJcovAttrTypes(); - } + } + + protected String getCpString(int i) { + return thePool.get(i); } public Element readFrom(InputStream in) throws IOException { - this.in = in; - // read the file header - int magic = u4(); - if (magic != 0xCAFEBABE) { - throw new RuntimeException("bad magic number " + Integer.toHexString(magic)); - } - cfile.setAttr("magic", "" + magic); - int minver = u2(); - int majver = u2(); - cfile.setAttr("minver", "" + minver); - cfile.setAttr("majver", "" + majver); - readCP(); - readClass(); - return result(); + try { + this.in = in; + ClassFile c = ClassFile.read(in); + // read the file header + if (c.magic != 0xCAFEBABE) { + throw new RuntimeException("bad magic number " + + Integer.toHexString(c.magic)); + } + cfile.setAttr("magic", "" + c.magic); + int minver = c.minor_version; + int majver = c.major_version; + cfile.setAttr("minver", "" + minver); + cfile.setAttr("majver", "" + majver); + readCP(c); + readClass(c); + return result(); + } catch (InvalidDescriptor | ConstantPoolException ex) { + throw new IOException("Fatal error", ex); + } } public Element readFrom(File file) throws IOException { - InputStream in = null; - try { - in = new FileInputStream(file); - Element e = readFrom(new BufferedInputStream(in)); + try (InputStream strm = new FileInputStream(file)) { + Element e = readFrom(new BufferedInputStream(strm)); if (keepPath) { e.setAttr("path", file.toString()); } return e; - } finally { - if (in != null) { - in.close(); - } } } - private void readClass() throws IOException { + private void readClass(ClassFile c) throws IOException, + ConstantPoolException, + InvalidDescriptor { klass = new Element("Class"); cfile.add(klass); - int flags = u2(); - String thisk = cpRef(); - String superk = cpRef(); + String thisk = c.getName(); + klass.setAttr("name", thisk); - boolean flagsSync = ((flags & Modifier.SYNCHRONIZED) != 0); - flags &= ~Modifier.SYNCHRONIZED; - String flagString = flagString(flags, klass); - if (!flagsSync) { - if (flagString.length() > 0) { - flagString += " "; - } - flagString += "!synchronized"; + + AccessFlags af = new AccessFlags(c.access_flags.flags); + klass.setAttr("flags", flagString(af, klass)); + if (!"java/lang/Object".equals(thisk)) { + klass.setAttr("super", c.getSuperclassName()); + } + for (int i : c.interfaces) { + klass.add(new Element("Interface", "name", getCpString(i))); + } + readFields(c, klass); + readMethods(c, klass); + readAttributesFor(c, c.attributes, klass); + klass.trimToSize(); + } + + private void readFields(ClassFile c, Element klass) throws IOException { + int len = c.fields.length; + Element fields = new Element(len); + for (Field f : c.fields) { + Element field = new Element("Field"); + field.setAttr("name", getCpString(f.name_index)); + field.setAttr("type", getCpString(f.descriptor.index)); + field.setAttr("flags", flagString(f.access_flags.flags, field)); + readAttributesFor(c, f.attributes, field); + + field.trimToSize(); + fields.add(field); } - klass.setAttr("flags", flagString); - klass.setAttr("super", superk); - for (int len = u2(), i = 0; i < len; i++) { - String interk = cpRef(); - klass.add(new Element("Interface", "name", interk)); + if (!keepOrder) { + fields.sort(); } - Element fields = readMembers("Field"); klass.addAll(fields); - Element methods = readMembers("Method"); + } + + + private void readMethods(ClassFile c, Element klass) throws IOException { + int len = c.methods.length; + Element methods = new Element(len); + for (Method m : c.methods) { + Element member = new Element("Method"); + member.setAttr("name", getCpString(m.name_index)); + member.setAttr("type", getCpString(m.descriptor.index)); + member.setAttr("flags", flagString(m.access_flags.flags, member)); + readAttributesFor(c, m.attributes, member); + + member.trimToSize(); + methods.add(member); + } if (!keepOrder) { methods.sort(); } klass.addAll(methods); - readAttributesFor(klass); - klass.trimToSize(); - if (keepSizes) { - attachTo(cfile, formatAttrSizes()); - } - if (paddingSize != 0) { - cfile.setAttr("padding", "" + paddingSize); - } - } - - private Element readMembers(String kind) throws IOException { - int len = u2(); - Element members = new Element(len); - for (int i = 0; i < len; i++) { - Element member = new Element(kind); - int flags = u2(); - String name = cpRef(); - String type = cpRef(); - member.setAttr("name", name); - member.setAttr("type", type); - member.setAttr("flags", flagString(flags, member)); - readAttributesFor(member); - member.trimToSize(); - members.add(member); + } + + private AccessFlags.Kind getKind(Element e) { + switch(e.getName()) { + case "Class": + return AccessFlags.Kind.Class; + case "InnerClass": + return AccessFlags.Kind.InnerClass; + case "Field": + return AccessFlags.Kind.Field ; + case "Method": + return AccessFlags.Kind.Method; + default: throw new RuntimeException("should not reach here"); } - return members; } protected String flagString(int flags, Element holder) { - // Superset of Modifier.toString. - int kind = 0; - if (holder.getName() == "Field") { - kind = 1; - } - if (holder.getName() == "Method") { - kind = 2; - } - StringBuffer sb = new StringBuffer(); - for (int i = 0; flags != 0; i++, flags >>>= 1) { - if ((flags & 1) != 0) { - if (sb.length() > 0) { - sb.append(' '); - } - if (i < modifierNames.length) { - String[] names = modifierNames[i]; - String name = (kind < names.length) ? names[kind] : null; - for (String name2 : names) { - if (name != null) { - break; - } - name = name2; - } - sb.append(name); - } else { - sb.append("#").append(1 << i); - } - } - } - return sb.toString(); + return flagString(new AccessFlags(flags), holder); + } + protected String flagString(AccessFlags af, Element holder) { + return flagString(af, holder.getName()); + } + protected String flagString(int flags, String kind) { + return flagString(new AccessFlags(flags), kind); + } + protected String flagString(AccessFlags af, String kind) { + Set mods = null; + switch (kind) { + case "Class": + mods = af.getClassFlags(); + break; + case "InnerClass": + mods = af.getInnerClassFlags(); + break; + case "Field": + mods = af.getFieldFlags(); + break; + case "Method": + mods = af.getMethodFlags(); + break; + default: + throw new RuntimeException("should not reach here"); + } + StringBuilder sb = new StringBuilder(); + for (String x : mods) { + sb.append(x.substring(x.indexOf('_') + 1).toLowerCase()).append(" "); + } + return sb.toString().trim(); } - private void readAttributesFor(Element x) throws IOException { - Element prevCurrent; - Element y = new Element(); - if (x.getName() == "Code") { - prevCurrent = currentCode; - currentCode = x; - } else { - prevCurrent = currentMember; - currentMember = x; - } - for (int len = u2(), i = 0; i < len; i++) { - int ref = u2(); - String uname = cpName(ref).intern(); - String refName = uname; - if (!resolveRefs) { - refName = (REF_PREFIX + ref).intern(); - } - String qname = (x.getName() + "." + uname).intern(); - String wname = ("*." + uname).intern(); - String type = attrTypes.get(qname); - if (type == null || "".equals(type)) { - type = attrTypes.get(wname); - } - if ("".equals(type)) { - type = null; - } - int size = u4(); - int[] countVar = attrSizes.get(qname); - if (countVar == null) { - attrSizes.put(qname, countVar = new int[2]); - } - countVar[0] += 1; - countVar[1] += size; - buf.reset(); - for (int j = 0; j < size; j++) { - buf.write(u1()); - } - if (type == null && size == 0) { - y.add(new Element(uname)); // , etc. - } else if (type == null) { - //System.out.println("Warning: No attribute type description: "+qname); - // write cdata attribute - Element a = new Element("Attribute", - new String[]{"Name", refName}, - buf.toString(EIGHT_BIT_CHAR_ENCODING)); - a.addContent(getCPDigest()); - y.add(a); - } else if (type.equals("")) { - // ignore this attribute... - } else { - InputStream in0 = in; - int fileSize0 = fileSize; - ByteArrayInputStream in1 = new ByteArrayInputStream(buf.toByteArray()); - boolean ok = false; - try { - in = in1; - // parse according to type desc. - Element aval; - if (type.equals("...")) { - // delve into Code attribute - aval = readCode(); - } else if (type.equals("...")) { - // delve into StackMap attribute - aval = readStackMap(false); - } else if (type.equals("...")) { - // delve into StackMap attribute - aval = readStackMap(true); - } else if (type.startsWith("[")) { - aval = readAttributeCallables(type); - } else { - aval = readAttribute(type); - } - //System.out.println("attachTo 1 "+y+" <- "+aval); - attachTo(y, aval); - if (false - && in1.available() != 0) { - throw new RuntimeException("extra bytes in " + qname + " :" + in1.available()); - } - ok = true; - } finally { - in = in0; - fileSize = fileSize0; - if (!ok) { - System.out.println("*** Failed to read " + type); - } - } - } - } - if (x.getName() == "Code") { - currentCode = prevCurrent; - } else { - currentMember = prevCurrent; + + protected void readAttributesFor(ClassFile c, Attributes attrs, Element x) { + Element container = new Element(); + AttributeVisitor av = new AttributeVisitor(this, c); + for (Attribute a : attrs) { + av.visit(a, container); } if (!keepOrder) { - y.sort(); - y.sortAttrs(); + container.sort(); } - //System.out.println("attachTo 2 "+x+" <- "+y); - attachTo(x, y); + x.addAll(container); } + private int fileSize = 0; - private int paddingSize = 0; - private HashMap attrSizes = new HashMap(); - - private Element formatAttrSizes() { - Element e = new Element("Sizes"); - e.setAttr("fileSize", "" + fileSize); - for (Map.Entry ie : attrSizes.entrySet()) { - int[] countVar = ie.getValue(); - e.add(new Element("AttrSize", - "name", ie.getKey().toString(), - "count", "" + countVar[0], - "size", "" + countVar[1])); - } - return e; - } + private HashMap attrSizes = new HashMap<>(); private void attachTo(Element x, Object aval0) { if (aval0 == null) { return; } - //System.out.println("attachTo "+x+" : "+aval0); if (!(aval0 instanceof Element)) { x.add(aval0); return; @@ -589,7 +512,6 @@ public class ClassReader extends ClassSyntax { } private void attachAttrTo(Element x, String aname, String aval) { - //System.out.println("attachAttrTo "+x+" : "+aname+"="+aval); String aval0 = x.getAttr(aname); if (aval0 != null) { aval = aval0 + " " + aval; @@ -597,407 +519,1003 @@ public class ClassReader extends ClassSyntax { x.setAttr(aname, aval); } - private Element readAttributeCallables(String type) throws IOException { - assert (callables == null); - callables = getBodies(type); - Element res = readAttribute(callables[0]); - callables = null; - return res; - } - - private Element readAttribute(String type) throws IOException { - //System.out.println("readAttribute "+type); - Element aval = new Element(); - String nextAttrName = null; - for (int len = type.length(), next, i = 0; i < len; i = next) { - String value; - switch (type.charAt(i)) { - case '<': - assert (nextAttrName == null); - next = type.indexOf('>', ++i); - String form = type.substring(i, next++); - if (form.indexOf('=') < 0) { - // elem_placement = '<' elemname '>' - assert (aval.attrSize() == 0); - assert (aval.isAnonymous()); - aval.setName(form.intern()); - } else { - // attr_placement = '<' attrname '=' (value)? '>' - int eqPos = form.indexOf('='); - nextAttrName = form.substring(0, eqPos).intern(); - if (eqPos != form.length() - 1) { - value = form.substring(eqPos + 1); - attachAttrTo(aval, nextAttrName, value); - nextAttrName = null; - } - // ...else subsequent type parsing will find the attr value - // and add it as "nextAttrName". - } - continue; - case '(': - next = type.indexOf(')', ++i); - int callee = Integer.parseInt(type.substring(i, next++)); - attachTo(aval, readAttribute(callables[callee])); - continue; - case 'N': // replication = 'N' int '[' type ... ']' - { - int count = getInt(type.charAt(i + 1), false); - assert (count >= 0); - next = i + 2; - String type1 = getBody(type, next); - next += type1.length() + 2; // skip body and brackets - for (int j = 0; j < count; j++) { - attachTo(aval, readAttribute(type1)); - } - } - continue; - case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']' - int tagValue; - if (type.charAt(++i) == 'S') { - tagValue = getInt(type.charAt(++i), true); - } else { - tagValue = getInt(type.charAt(i), false); + private void readCP(ClassFile c) throws IOException { + cpool = new Element("ConstantPool", c.constant_pool.size()); + ConstantPoolVisitor cpv = new ConstantPoolVisitor(cpool, c, + c.constant_pool.size()); + for (int i = 1 ; i < c.constant_pool.size() ; i++) { + try { + cpv.visit(c.constant_pool.get(i), i); + } catch (InvalidIndex ex) { + // can happen periodically when accessing doubles etc. ignore it + // ex.printStackTrace(); + } + } + thePool = cpv.getPoolList(); + if (verbose) { + for (int i = 0; i < thePool.size(); i++) { + System.out.println("[" + i + "]: " + thePool.get(i)); + } + } + if (keepCP) { + cfile.add(cpool); + } + } +} + +class ConstantPoolVisitor implements ConstantPool.Visitor { + final List slist; + final Element xpool; + final ClassFile cf; + final ConstantPool cfpool; + final List bsmlist; + + + public ConstantPoolVisitor(Element xpool, ClassFile cf, int size) { + slist = new ArrayList<>(size); + for (int i = 0 ; i < size; i++) { + slist.add(null); + } + this.xpool = xpool; + this.cf = cf; + this.cfpool = cf.constant_pool; + bsmlist = readBSM(); + } + + public List getPoolList() { + return Collections.unmodifiableList(slist); + } + + public List getBSMList() { + return Collections.unmodifiableList(bsmlist); + } + + public String visit(CPInfo c, int index) { + return c.accept(this, index); + } + + private List readBSM() { + BootstrapMethods_attribute bsmAttr = + (BootstrapMethods_attribute) cf.getAttribute(Attribute.BootstrapMethods); + if (bsmAttr != null) { + List out = + new ArrayList<>(bsmAttr.bootstrap_method_specifiers.length); + for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsms : + bsmAttr.bootstrap_method_specifiers) { + int index = bsms.bootstrap_method_ref; + try { + String value = slist.get(index); + String bsmStr = value; + if (value == null) { + value = visit(cfpool.get(index), index); + slist.set(index, value); } - attachAttrTo(aval, "tag", "" + tagValue); // always named "tag" - ++i; // skip the int type char - // union_case = '(' uc_tag (',' uc_tag)* ')' '[' body ']' - // uc_tag = ('-')? digit+ - for (boolean foundCase = false;; i = next) { - assert (type.charAt(i) == '('); - next = type.indexOf(')', ++i); - assert (next >= i); - if (type.charAt(next - 1) == '\\' - && type.charAt(next - 2) != '\\') // Skip an escaped paren. - { - next = type.indexOf(')', next + 1); - } - String caseStr = type.substring(i, next++); - String type1 = getBody(type, next); - next += type1.length() + 2; // skip body and brackets - boolean lastCase = (caseStr.length() == 0); - if (!foundCase - && (lastCase || matchTag(tagValue, caseStr))) { - foundCase = true; - // Execute this body. - attachTo(aval, readAttribute(type1)); - } - if (lastCase) { - break; + bsmStr = value; + for (int idx : bsms.bootstrap_arguments) { + value = slist.get(idx); + if (value == null) { + value = visit(cfpool.get(idx), idx); + slist.set(idx, value); } + bsmStr = bsmStr.concat("," + value); } - continue; - case 'B': - case 'H': - case 'I': // int = oneof "BHI" - next = i + 1; - value = "" + getInt(type.charAt(i), false); - break; - case 'K': - assert ("IJFDLQ".indexOf(type.charAt(i + 1)) >= 0); - assert (type.charAt(i + 2) == 'H'); // only H works for now - next = i + 3; - value = cpRef(); - break; - case 'R': - assert ("CSDFMIU?".indexOf(type.charAt(i + 1)) >= 0); - assert (type.charAt(i + 2) == 'H'); // only H works for now - next = i + 3; - value = cpRef(); - break; - case 'P': // bci = 'P' int - next = i + 2; - value = "" + getInt(type.charAt(i + 1), false); - break; - case 'S': // signed_int = 'S' int - next = i + 2; - value = "" + getInt(type.charAt(i + 1), true); - break; - case 'F': - next = i + 2; - value = flagString(getInt(type.charAt(i + 1), false), currentMember); - break; - default: - throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type); - } - // store the value - if (nextAttrName != null) { - attachAttrTo(aval, nextAttrName, value); - nextAttrName = null; - } else { - attachTo(aval, value); + out.add(bsmStr); + } catch (InvalidIndex ex) { + ex.printStackTrace(); + } } + return out; } - //System.out.println("readAttribute => "+aval); - assert (nextAttrName == null); - return aval; + return new ArrayList<>(0); } - private int getInt(char ch, boolean signed) throws IOException { - if (signed) { - switch (ch) { - case 'B': - return (byte) u1(); - case 'H': - return (short) u2(); - case 'I': - return (int) u4(); + @Override + public String visitClass(CONSTANT_Class_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = visit(cfpool.get(c.name_index), c.name_index); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Class", + new String[]{"id", p.toString()}, + value)); + } catch (ConstantPoolException ex) { + ex.printStackTrace(); } - } else { - switch (ch) { - case 'B': - return u1(); - case 'H': - return u2(); - case 'I': - return u4(); + } + return value; + } + + @Override + public String visitDouble(CONSTANT_Double_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + value = Double.toString(c.value); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Double", + new String[]{"id", p.toString()}, + value)); + } + return value; + } + + @Override + public String visitFieldref(CONSTANT_Fieldref_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = visit(cfpool.get(c.class_index), c.class_index); + value = value.concat(" " + visit(cfpool.get(c.name_and_type_index), + c.name_and_type_index)); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Fieldref", + new String[]{"id", p.toString()}, + value)); + } catch (ConstantPoolException ex) { + ex.printStackTrace(); } } - assert ("BHIJ".indexOf(ch) >= 0); - return 0; - } - - private Element readCode() throws IOException { - int stack = u2(); - int local = u2(); - int length = u4(); - StringBuilder sb = new StringBuilder(length); - for (int i = 0; i < length; i++) { - sb.append((char) u1()); - } - String bytecodes = sb.toString(); - Element e = new Element("Code", - "stack", "" + stack, - "local", "" + local); - Element bytes = new Element("Bytes", (String[]) null, bytecodes); - if (keepBytes) { - e.add(bytes); - } - if (parseBytes) { - e.add(parseByteCodes(bytecodes)); - } - for (int len = u2(), i = 0; i < len; i++) { - int start = u2(); - int end = u2(); - int catsh = u2(); - String clasz = cpRef(); - e.add(new Element("Handler", - "start", "" + start, - "end", "" + end, - "catch", "" + catsh, - "class", clasz)); + return value; + } + + @Override + public String visitFloat(CONSTANT_Float_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + value = Float.toString(c.value); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Float", + new String[]{"id", p.toString()}, + value)); + } + return value; + } + + @Override + public String visitInteger(CONSTANT_Integer_info cnstnt, Integer p) { + String value = slist.get(p); + if (value == null) { + value = Integer.toString(cnstnt.value); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Integer", + new String[]{"id", p.toString()}, + value)); + } + return value; + } + + @Override + public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info c, + Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = visit(cfpool.get(c.class_index), c.class_index); + value = value.concat(" " + + visit(cfpool.get(c.name_and_type_index), + c.name_and_type_index)); + slist.set(p, value); + xpool.add(new Element("CONSTANT_InterfaceMethodref", + new String[]{"id", p.toString()}, + value)); + + } catch (ConstantPoolException ex) { + ex.printStackTrace(); + } } - readAttributesFor(e); - e.trimToSize(); - return e; + return value; } - private Element parseByteCodes(String bytecodes) { - Element e = InstructionSyntax.parse(bytecodes); - for (Element ins : e.elements()) { - Number ref = ins.getAttrNumber("ref"); - if (ref != null && resolveRefs) { - int id = ref.intValue(); - String val = cpName(id); - if (ins.getName().startsWith("ldc")) { - // Yuck: Arb. string cannot be an XML attribute. - ins.add(val); - val = ""; - byte tag = (id >= 0 && id < cpTag.length) ? cpTag[id] : 0; - if (tag != 0) { - ins.setAttrLong("tag", tag); - } - } - if (ins.getName() == "invokeinterface" - && computeInterfaceNum(val) == ins.getAttrLong("num")) { - ins.setAttr("num", null); // garbage bytes - } - ins.setAttr("ref", null); - ins.setAttr("val", val); + @Override + public String visitInvokeDynamic(CONSTANT_InvokeDynamic_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = bsmlist.get(c.bootstrap_method_attr_index) + " " + + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index); + slist.set(p, value); + xpool.add(new Element("CONSTANT_InvokeDynamic", + new String[]{"id", p.toString()}, + value)); + + } catch (ConstantPoolException ex) { + ex.printStackTrace(); } } - return e; + return value; } - private Element readStackMap(boolean hasXOption) throws IOException { - Element result = new Element(); - Element bytes = currentCode.findElement("Bytes"); - assert (bytes != null && bytes.size() == 1); - int byteLength = ((String) bytes.get(0)).length(); - boolean uoffsetIsU4 = (byteLength >= (1 << 16)); - boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); - boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); - if (hasXOption || uoffsetIsU4 || ulocalvarIsU4 || ustackIsU4) { - Element flags = new Element("StackMapFlags"); - if (hasXOption) { - flags.setAttr("hasXOption", "true"); + @Override + public String visitLong(CONSTANT_Long_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + value = Long.toString(c.value); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Long", + new String[]{"id", p.toString()}, + value)); + } + return value; + } + + @Override + public String visitNameAndType(CONSTANT_NameAndType_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = visit(cfpool.get(c.name_index), c.name_index); + value = value.concat(" " + + visit(cfpool.get(c.type_index), c.type_index)); + slist.set(p, value); + xpool.add(new Element("CONSTANT_NameAndType", + new String[]{"id", p.toString()}, + value)); + } catch (InvalidIndex ex) { + ex.printStackTrace(); } - if (uoffsetIsU4) { - flags.setAttr("uoffsetIsU4", "true"); + } + return value; + } + + @Override + public String visitMethodref(CONSTANT_Methodref_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = visit(cfpool.get(c.class_index), c.class_index); + value = value.concat(" " + + visit(cfpool.get(c.name_and_type_index), + c.name_and_type_index)); + slist.set(p, value); + xpool.add(new Element("CONSTANT_Methodref", + new String[]{"id", p.toString()}, + value)); + + } catch (ConstantPoolException ex) { + ex.printStackTrace(); } - if (ulocalvarIsU4) { - flags.setAttr("ulocalvarIsU4", "true"); + } + return value; + } + + @Override + public String visitMethodHandle(CONSTANT_MethodHandle_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = c.reference_kind.name(); + value = value.concat(" " + + visit(cfpool.get(c.reference_index), c.reference_index)); + slist.set(p, value); + xpool.add(new Element("CONSTANT_MethodHandle", + new String[]{"id", p.toString()}, + value)); + + } catch (ConstantPoolException ex) { + ex.printStackTrace(); } - if (ustackIsU4) { - flags.setAttr("ustackIsU4", "true"); + } + return value; + } + + @Override + public String visitMethodType(CONSTANT_MethodType_info c, Integer p) { + String value = slist.get(p); + if (value == null) { + try { + value = visit(cfpool.get(c.descriptor_index), c.descriptor_index); + slist.set(p, value); + xpool.add(new Element("CONSTANT_MethodType", + new String[]{"id", p.toString()}, + value)); + } catch (ConstantPoolException ex) { + ex.printStackTrace(); } - currentCode.add(flags); - } - int frame_count = (uoffsetIsU4 ? u4() : u2()); - for (int i = 0; i < frame_count; i++) { - int bci = (uoffsetIsU4 ? u4() : u2()); - int flags = (hasXOption ? u1() : 0); - Element frame = new Element("Frame"); - result.add(frame); - if (flags != 0) { - frame.setAttr("flags", "" + flags); + } + return value; + } + + @Override + public String visitString(CONSTANT_String_info c, Integer p) { + try { + + String value = slist.get(p); + if (value == null) { + value = c.getString(); + slist.set(p, value); + xpool.add(new Element("CONSTANT_String", + new String[]{"id", p.toString()}, + value)); } - frame.setAttr("bci", "" + bci); - // Scan local and stack types in this frame: - final int LOCALS = 0, STACK = 1; - for (int j = LOCALS; j <= STACK; j++) { - int typeSize; - if (j == LOCALS) { - typeSize = (ulocalvarIsU4 ? u4() : u2()); - } else { // STACK - typeSize = (ustackIsU4 ? u4() : u2()); - } - Element types = new Element(j == LOCALS ? "Local" : "Stack"); - for (int k = 0; k < typeSize; k++) { - int tag = u1(); - Element type = new Element(itemTagName(tag)); - types.add(type); - switch (tag) { - case ITEM_Object: - type.setAttr("class", cpRef()); - break; - case ITEM_Uninitialized: - case ITEM_ReturnAddress: - type.setAttr("bci", "" + (uoffsetIsU4 ? u4() : u2())); - break; - } + return value; + } catch (ConstantPoolException ex) { + throw new RuntimeException("Fatal error", ex); + } + } + + @Override + public String visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p) { + String value = slist.get(p); + if (value == null) { + value = cnstnt.value; + slist.set(p, value); + xpool.add(new Element("CONSTANT_Utf8", + new String[]{"id", p.toString()}, + value)); + } + return value; + + } +} + +class AttributeVisitor implements Attribute.Visitor { + final ClassFile cf; + final ClassReader x; + final AnnotationsElementVisitor aev; + final InstructionVisitor iv; + + public AttributeVisitor(ClassReader x, ClassFile cf) { + this.x = x; + this.cf = cf; + iv = new InstructionVisitor(x, cf); + aev = new AnnotationsElementVisitor(x, cf); + } + + public void visit(Attribute a, Element parent) { + a.accept(this, parent); + } + + @Override + public Element visitBootstrapMethods(BootstrapMethods_attribute bm, Element p) { + Element e = new Element(x.getCpString(bm.attribute_name_index)); + for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsm : bm.bootstrap_method_specifiers) { + Element be = new Element("BootstrapMethodSpecifier"); + be.setAttr("ref", x.getCpString(bsm.bootstrap_method_ref)); + if (bsm.bootstrap_arguments.length > 0) { + Element bme = new Element("MethodArguments"); + for (int index : bsm.bootstrap_arguments) { + bme.add(x.getCpString(index)); } - if (types.size() > 0) { - frame.add(types); + bme.trimToSize(); + be.add(bme); + } + be.trimToSize(); + e.add(be); + } + e.trimToSize(); + if (!x.keepOrder) { + e.sort(); + } + p.add(e); + return null; + } + + @Override + public Element visitDefault(DefaultAttribute da, Element p) { + Element e = new Element(x.getCpString(da.attribute_name_index)); + StringBuilder sb = new StringBuilder(); + for (byte x : da.info) { + sb.append("0x").append(Integer.toHexString(x)).append(" "); + } + e.setAttr("bytes", sb.toString().trim()); + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitAnnotationDefault(AnnotationDefault_attribute ad, Element p) { + Element e = new Element(x.getCpString(ad.attribute_name_index)); + e.setAttr("tag", "" + ad.default_value.tag); + Element child = aev.visit(ad.default_value, e); + if (child != null) { + e.add(child); + } + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitCharacterRangeTable(CharacterRangeTable_attribute crt, + Element p) { + Element e = new Element(x.getCpString(crt.attribute_name_index)); + for (CharacterRangeTable_attribute.Entry ce : crt.character_range_table) { + e.setAttr("start_pc", "" + ce.start_pc); + e.setAttr("end_pc", "" + ce.end_pc); + e.setAttr("range_start", "" + ce.character_range_start); + e.setAttr("range_end", "" + ce.character_range_end); + e.setAttr("flags", x.flagString(ce.flags, "Method")); + } + e.trimToSize(); + p.add(e); + return null; + } + + private Element instructions(Element code, Code_attribute c) { + Element ielement = new Element("Instructions"); + for (Instruction ins : c.getInstructions()) { + ielement.add(iv.visit(ins)); + } + ielement.trimToSize(); + return ielement; + } + + @Override + public Element visitCode(Code_attribute c, Element p) { + Element e = null; + + e = new Element(x.getCpString(c.attribute_name_index), + "stack", "" + c.max_stack, + "local", "" + c.max_locals); + + e.add(instructions(e, c)); + + for (Code_attribute.Exception_data edata : c.exception_table) { + e.add(new Element("Handler", + "start", "" + edata.start_pc, + "end", "" + edata.end_pc, + "catch", "" + edata.handler_pc, + "class", x.getCpString(edata.catch_type))); + + } + this.x.readAttributesFor(cf, c.attributes, e); + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitCompilationID(CompilationID_attribute cid, Element p) { + Element e = new Element(x.getCpString(cid.attribute_name_index), + x.getCpString(cid.compilationID_index)); + p.add(e); + return null; + } + + @Override + public Element visitConstantValue(ConstantValue_attribute cv, Element p) { + Element e = new Element(x.getCpString(cv.attribute_name_index)); + e.add(x.getCpString(cv.constantvalue_index)); + p.add(e); + return null; + } + + @Override + public Element visitDeprecated(Deprecated_attribute d, Element p) { + Element e = new Element(x.getCpString(d.attribute_name_index)); + p.add(e); + return null; + } + + @Override + public Element visitEnclosingMethod(EnclosingMethod_attribute em, Element p) { + Element e = new Element(x.getCpString(em.attribute_name_index)); + e.setAttr("class", x.getCpString(em.class_index)); + e.setAttr("desc", x.getCpString(em.method_index)); + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitExceptions(Exceptions_attribute e, Element p) { + Element ee = new Element(x.getCpString(e.attribute_name_index)); + for (int idx : e.exception_index_table) { + Element n = new Element("Item"); + n.setAttr("class", x.getCpString(idx)); + ee.add(n); + } + ee.trimToSize(); + p.add(ee); + return null; + } + + @Override + public Element visitInnerClasses(InnerClasses_attribute ic, Element p) { + for (Info info : ic.classes) { + Element e = new Element(x.getCpString(ic.attribute_name_index)); + e.setAttr("class", x.getCpString(info.inner_class_info_index)); + e.setAttr("outer", x.getCpString(info.outer_class_info_index)); + e.setAttr("name", x.getCpString(info.inner_name_index)); + e.setAttr("flags", x.flagString(info.inner_class_access_flags, + "InnerClass")); + e.trimToSize(); + p.add(e); + } + return null; + } + + @Override + public Element visitLineNumberTable(LineNumberTable_attribute lnt, Element p) { + String name = x.getCpString(lnt.attribute_name_index); + for (LineNumberTable_attribute.Entry e : lnt.line_number_table) { + Element l = new Element(name); + l.setAttr("bci", "" + e.start_pc); + l.setAttr("line", "" + e.line_number); + l.trimToSize(); + p.add(l); + } + return null; // already added to parent + } + + @Override + public Element visitLocalVariableTable(LocalVariableTable_attribute lvt, + Element p) { + String name = x.getCpString(lvt.attribute_name_index); + for (LocalVariableTable_attribute.Entry e : lvt.local_variable_table) { + Element l = new Element(name); + l.setAttr("bci", "" + e.start_pc); + l.setAttr("span", "" + e.length); + l.setAttr("name", x.getCpString(e.name_index)); + l.setAttr("type", x.getCpString(e.descriptor_index)); + l.setAttr("slot", "" + e.index); + l.trimToSize(); + p.add(l); + } + return null; // already added to parent + } + + @Override + public Element visitLocalVariableTypeTable(LocalVariableTypeTable_attribute lvtt, + Element p) { + String name = x.getCpString(lvtt.attribute_name_index); + for (LocalVariableTypeTable_attribute.Entry e : lvtt.local_variable_table) { + Element l = new Element(name); + l.setAttr("bci", "" + e.start_pc); + l.setAttr("span", "" + e.length); + l.setAttr("name", x.getCpString(e.name_index)); + l.setAttr("type", x.getCpString(e.signature_index)); + l.setAttr("slot", "" + e.index); + l.trimToSize(); + p.add(l); + } + return null; // already added to parent + } + + private void parseAnnotations(Annotation[] ra, Element p) { + for (Annotation anno : ra) { + Element ea = new Element("Member"); + ea.setAttr("name", "" + x.getCpString(anno.type_index)); + for (Annotation.element_value_pair evp : anno.element_value_pairs) { + Element evpe = new Element("Element"); + evpe.setAttr("tag", "" + evp.value.tag); + evpe.setAttr("value", x.getCpString(evp.element_name_index)); + Element child = aev.visit(evp.value, evpe); + if (child != null) { + evpe.add(child); } + ea.add(evpe); } + ea.trimToSize(); + p.add(ea); } - return result; - } - - private void readCP() throws IOException { - int cpLen = u2(); - cpTag = new byte[cpLen]; - cpName = new String[cpLen]; - int cpTem[][] = new int[cpLen][]; - for (int i = 1; i < cpLen; i++) { - cpTag[i] = (byte) u1(); - switch (cpTag[i]) { - case CONSTANT_Utf8: - buf.reset(); - for (int len = u2(), j = 0; j < len; j++) { - buf.write(u1()); - } - cpName[i] = buf.toString(UTF8_ENCODING); - break; - case CONSTANT_Integer: - cpName[i] = String.valueOf((int) u4()); + } + + @Override + public Element visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva, + Element p) { + Element e = new Element(x.getCpString(rva.attribute_name_index)); + parseAnnotations(rva.annotations, e); + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria, + Element p) { + Element e = new Element(x.getCpString(ria.attribute_name_index)); + parseAnnotations(ria.annotations, e); + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute rvpa, + Element p) { + Element e = new Element(x.getCpString(rvpa.attribute_name_index)); + for (Annotation[] pa : rvpa.parameter_annotations) { + parseAnnotations(pa, e); + } + p.add(e); + return null; + } + + @Override + public Element visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute ripa, + Element p) { + Element e = new Element(x.getCpString(ripa.attribute_name_index)); + for (Annotation[] pa : ripa.parameter_annotations) { + parseAnnotations(pa, e); + } + p.add(e); + return null; + } + + @Override + public Element visitSignature(Signature_attribute s, Element p) { + String aname = x.getCpString(s.attribute_name_index); + String sname = x.getCpString(s.signature_index); + Element se = new Element(aname); + se.add(sname); + se.trimToSize(); + p.add(se); + return null; + } + + @Override + public Element visitSourceDebugExtension(SourceDebugExtension_attribute sde, + Element p) { + String aname = x.getCpString(sde.attribute_name_index); + Element se = new Element(aname); + se.setAttr("val", sde.getValue()); + se.trimToSize(); + p.add(se); + return null; + } + + @Override + public Element visitSourceFile(SourceFile_attribute sf, Element p) { + String aname = x.getCpString(sf.attribute_name_index); + String sname = x.getCpString(sf.sourcefile_index); + Element se = new Element(aname); + se.add(sname); + se.trimToSize(); + p.add(se); + return null; + } + + @Override + public Element visitSourceID(SourceID_attribute sid, Element p) { + Element e = new Element(x.getCpString(sid.attribute_name_index)); + e.add(x.getCpString(sid.sourceID_index)); + e.trimToSize(); + p.add(e); + return null; + } + + @Override + public Element visitStackMap(StackMap_attribute sm, Element p) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Element visitStackMapTable(StackMapTable_attribute smt, Element p) { + Element stackmap = new Element(x.getCpString(smt.attribute_name_index)); + for (StackMapTable_attribute.stack_map_frame f : smt.entries) { + StackMapVisitor smv = new StackMapVisitor(x, cf, stackmap); + stackmap.add(smv.visit(f)); + } + stackmap.trimToSize(); + p.add(stackmap); + return null; + } + + @Override + public Element visitSynthetic(Synthetic_attribute s, Element p) { + Element e = new Element(x.getCpString(s.attribute_name_index)); + e.trimToSize(); + p.add(e); + return null; + } +} + +class StackMapVisitor implements StackMapTable_attribute.stack_map_frame.Visitor { + + final ClassFile cf; + final ClassReader x; + final Element parent; + + public StackMapVisitor(ClassReader x, ClassFile cf, Element parent) { + this.x = x; + this.cf = cf; + this.parent = parent; + } + + public Element visit(StackMapTable_attribute.stack_map_frame frame) { + return frame.accept(this, null); + } + + @Override + public Element visit_same_frame(same_frame sm_frm, Void p) { + Element e = new Element("SameFrame"); + e.setAttr("tag", "" + sm_frm.frame_type); + return e; + } + + @Override + public Element visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p) { + Element e = new Element("SameLocals1StackItemFrame"); + e.setAttr("tag", "" + s.frame_type); + e.addAll(getVerificationTypeInfo("Stack", s.stack)); + e.trimToSize(); + return e; + } + + @Override + public Element visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p) { + Element e = new Element("SameLocals1StackItemFrameExtended"); + e.setAttr("tag", "" + s.frame_type); + e.addAll(getVerificationTypeInfo("Stack", s.stack)); + e.trimToSize(); + return e; + } + + @Override + public Element visit_chop_frame(chop_frame c, Void p) { + Element e = new Element("Chop" + (251 - c.frame_type)); + e.setAttr("tag", "" + c.frame_type); + e.setAttr("offset", "" + c.offset_delta); + return e; + } + + @Override + public Element visit_same_frame_extended(same_frame_extended s, Void p) { + Element e = new Element("SameFrameExtended"); + e.setAttr("tag", "" + s.frame_type); + e.setAttr("offset", "" + s.offset_delta); + return e; + } + + @Override + public Element visit_append_frame(append_frame a, Void p) { + Element e = new Element("AppendFrame" + (a.frame_type - 251)); + e.setAttr("tag", "" + a.frame_type); + e.addAll(getVerificationTypeInfo("Local", a.locals)); + e.trimToSize(); + return e; + } + + @Override + public Element visit_full_frame(full_frame fl_frm, Void p) { + Element e = new Element("FullFrame"); + e.setAttr("tag", "" + fl_frm.frame_type); + e.addAll(getVerificationTypeInfo("Local", fl_frm.locals)); + e.trimToSize(); + return e; + } + + private Element getVerificationTypeInfo(String kind, + StackMapTable_attribute.verification_type_info velems[]) { + Element container = new Element(velems.length); + for (StackMapTable_attribute.verification_type_info v : velems) { + Element ve = null; + int offset = 0; + int index = 0; + switch (v.tag) { + case StackMapTable_attribute.verification_type_info.ITEM_Top: + ve = new Element("ITEM_Top"); break; - case CONSTANT_Float: - cpName[i] = String.valueOf(Float.intBitsToFloat(u4())); + case StackMapTable_attribute.verification_type_info.ITEM_Integer: + ve = new Element("ITEM_Integer"); break; - case CONSTANT_Long: - cpName[i] = String.valueOf(u8()); - i += 1; + case StackMapTable_attribute.verification_type_info.ITEM_Float: + ve = new Element("ITEM_Float"); break; - case CONSTANT_Double: - cpName[i] = String.valueOf(Double.longBitsToDouble(u8())); - i += 1; + case StackMapTable_attribute.verification_type_info.ITEM_Long: + ve = new Element("ITEM_Long"); break; - case CONSTANT_Class: - case CONSTANT_String: - cpTem[i] = new int[]{u2()}; + case StackMapTable_attribute.verification_type_info.ITEM_Double: + ve = new Element("ITEM_Double"); break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - case CONSTANT_NameAndType: - cpTem[i] = new int[]{u2(), u2()}; + case StackMapTable_attribute.verification_type_info.ITEM_Null: + ve = new Element("ITEM_Null"); break; - } - } - for (int i = 1; i < cpLen; i++) { - switch (cpTag[i]) { - case CONSTANT_Class: - case CONSTANT_String: - cpName[i] = cpName[cpTem[i][0]]; + case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized: + ve = new Element("ITEM_Uninitialized"); + offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset; + ve.setAttr("offset", "" + offset); break; - case CONSTANT_NameAndType: - cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]]; + case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis: + ve = new Element("ITEM_UnitializedtThis"); break; - } - } - // do fieldref et al after nameandtype are all resolved - for (int i = 1; i < cpLen; i++) { - switch (cpTag[i]) { - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]]; + case StackMapTable_attribute.verification_type_info.ITEM_Object: + ve = new Element("ITEM_Object"); + index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index; + ve.setAttr("class", x.getCpString(index)); break; + default: + ve = new Element("Unknown"); } + Element kindE = new Element(kind); + kindE.setAttr("tag", "" + v.tag); + container.add(kindE); + kindE.add(ve); } - cpool = new Element("ConstantPool", cpName.length); - for (int i = 0; i < cpName.length; i++) { - if (cpName[i] == null) { - continue; - } - cpool.add(new Element(cpTagName(cpTag[i]), - new String[]{"id", "" + i}, - cpName[i])); - } - if (keepCP) { - cfile.add(cpool); - } + container.trimToSize(); + return container; } +} - private String cpRef() throws IOException { - int ref = u2(); - if (resolveRefs) { - return cpName(ref); - } else { - return REF_PREFIX + ref; +class InstructionVisitor implements Instruction.KindVisitor { + + final ClassReader x; + final ClassFile cf; + + public InstructionVisitor(ClassReader x, ClassFile cf) { + this.x = x; + this.cf = cf; + } + + public Element visit(Instruction i) { + Element ie = i.accept(this, null); + ie.trimToSize(); + return ie; + } + + @Override + public Element visitNoOperands(Instruction i, Void p) { + Opcode o = i.getOpcode(); + Element e = new Element(i.getMnemonic()); + if (o.opcode > 0xab && o.opcode <= 0xb1) { + e.setAttr("pc", "" + i.getPC()); } + return e; + } + + @Override + public Element visitArrayType(Instruction i, TypeKind tk, Void p) { + Element ie = new Element(i.getMnemonic()); + ie.setAttr("num", "" + tk.value); + ie.setAttr("val", tk.name); + return ie; + } + + @Override + public Element visitBranch(Instruction i, int i1, Void p) { + Element ie = new Element(i.getMnemonic()); + ie.setAttr("lab", "" + (i.getPC() + i1)); + return ie; + } + + @Override + public Element visitConstantPoolRef(Instruction i, int i1, Void p) { + Element ie = new Element(i.getMnemonic()); + ie.setAttr("ref", x.getCpString(i1)); + return ie; } - private String cpName(int id) { - if (id >= 0 && id < cpName.length) { - return cpName[id]; + @Override + public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) { + // workaround for a potential bug in classfile + Element ie = new Element(i.getMnemonic()); + if (i.getOpcode().equals(Opcode.IINC_W)) { + ie.setAttr("loc", "" + i1); + ie.setAttr("num", "" + i2); } else { - return "[CP#" + Integer.toHexString(id) + "]"; + ie.setAttr("ref", x.getCpString(i1)); + ie.setAttr("val", "" + i2); } + return ie; } - private long u8() throws IOException { - return ((long) u4() << 32) + (((long) u4() << 32) >>> 32); + @Override + public Element visitLocal(Instruction i, int i1, Void p) { + Element ie = new Element(i.getMnemonic()); + ie.setAttr("loc", "" + i1); + return ie; } - private int u4() throws IOException { - return (u2() << 16) + u2(); + @Override + public Element visitLocalAndValue(Instruction i, int i1, int i2, Void p) { + Element ie = new Element(i.getMnemonic()); + ie.setAttr("loc", "" + i1); + ie.setAttr("num", "" + i2); + return ie; } - private int u2() throws IOException { - return (u1() << 8) + u1(); + @Override + public Element visitLookupSwitch(Instruction i, int i1, int i2, int[] ints, + int[] ints1, Void p) { + Element ie = new Element(i.getMnemonic()); + int pc = i.getPC(); + ie.setAttr("lab", "" + (pc + i1)); + for (int k = 0 ; k < i2 ; k++) { + Element c = new Element("Case"); + c.setAttr("num", "" + (ints[k])); + c.setAttr("lab", "" + (pc + ints1[k])); + c.trimToSize(); + ie.add(c); + } + return ie; } - private int u1() throws IOException { - int x = in.read(); - if (x < 0) { - paddingSize++; - return 0; // error recovery - } - fileSize++; - assert (x == (x & 0xFF)); - return x; + @Override + public Element visitTableSwitch(Instruction i, int i1, int i2, int i3, + int[] ints, Void p) { + Element ie = new Element(i.getMnemonic()); + int pc = i.getPC(); + ie.setAttr("lab", "" + (pc + i1)); + for (int k : ints) { + Element c = new Element("Case"); + c.setAttr("num", "" + (k + i2)); + c.setAttr("lab", "" + (pc + k)); + c.trimToSize(); + ie.add(c); + } + return ie; + } + + @Override + public Element visitValue(Instruction i, int i1, Void p) { + Element ie = new Element(i.getMnemonic()); + ie.setAttr("num", "" + i1); + return ie; + } + + @Override + public Element visitUnknown(Instruction i, Void p) { + Element e = new Element(i.getMnemonic()); + e.setAttr("pc", "" + i.getPC()); + e.setAttr("opcode", "" + i.getOpcode().opcode); + return e; } } +class AnnotationsElementVisitor implements Annotation.element_value.Visitor { + final ClassReader x; + final ClassFile cf; + + public AnnotationsElementVisitor(ClassReader x, ClassFile cf) { + this.x = x; + this.cf = cf; + } + + public Element visit(Annotation.element_value v, Element p) { + return v.accept(this, p); + } + + @Override + public Element visitPrimitive(Primitive_element_value e, Element p) { + Element el = new Element("String"); + el.setAttr("val", x.getCpString(e.const_value_index)); + el.trimToSize(); + return el; + } + + @Override + public Element visitEnum(Enum_element_value e, Element p) { + Element el = new Element("Enum"); + el.setAttr("name", x.getCpString(e.const_name_index)); + el.setAttr("type", x.getCpString(e.type_name_index)); + el.trimToSize(); + return el; + } + + @Override + public Element visitClass(Class_element_value c, Element p) { + Element el = new Element("Class"); + el.setAttr("name", x.getCpString(c.class_info_index)); + el.trimToSize(); + return el; + } + + @Override + public Element visitAnnotation(Annotation_element_value a, Element p) { + Element el = new Element("Annotation"); + Annotation anno = a.annotation_value; + for (Annotation.element_value_pair evp : anno.element_value_pairs) { + Element child = visit(evp.value, el); + if (child != null) { + el.add(child); + } + } + el.trimToSize(); + return el; + } + + @Override + public Element visitArray(Array_element_value a, Element p) { + Element el = new Element("Array"); + for (Annotation.element_value v : a.values) { + Element child = visit(v, el); + if (child != null) { + el.add(child); + } + } + el.trimToSize(); + return el; + } +} diff --git a/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java b/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java deleted file mode 100644 index d34ecbad004c7ee6dcf4af1d0b97fce9da58fca1..0000000000000000000000000000000000000000 --- a/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- -import xmlkit.XMLKit.*; - -import java.util.*; -import java.security.MessageDigest; -import java.nio.ByteBuffer; -import xmlkit.XMLKit.Element; -/* - * @author jrose - */ -public abstract class ClassSyntax { - - public interface GetCPIndex { - - int getCPIndex(int tag, String name); // cp finder - } - public static final int CONSTANT_Utf8 = 1, - CONSTANT_Integer = 3, - CONSTANT_Float = 4, - CONSTANT_Long = 5, - CONSTANT_Double = 6, - CONSTANT_Class = 7, - CONSTANT_String = 8, - CONSTANT_Fieldref = 9, - CONSTANT_Methodref = 10, - CONSTANT_InterfaceMethodref = 11, - CONSTANT_NameAndType = 12; - private static final String[] cpTagName = { - /* 0: */null, - /* 1: */ "Utf8", - /* 2: */ null, - /* 3: */ "Integer", - /* 4: */ "Float", - /* 5: */ "Long", - /* 6: */ "Double", - /* 7: */ "Class", - /* 8: */ "String", - /* 9: */ "Fieldref", - /* 10: */ "Methodref", - /* 11: */ "InterfaceMethodref", - /* 12: */ "NameAndType", - null - }; - private static final Set cpTagNames; - - static { - Set set = new HashSet(Arrays.asList(cpTagName)); - set.remove(null); - cpTagNames = Collections.unmodifiableSet(set); - } - public static final int ITEM_Top = 0, // replicates by [1..4,1..4] - ITEM_Integer = 1, // (ditto) - ITEM_Float = 2, - ITEM_Double = 3, - ITEM_Long = 4, - ITEM_Null = 5, - ITEM_UninitializedThis = 6, - ITEM_Object = 7, - ITEM_Uninitialized = 8, - ITEM_ReturnAddress = 9, - ITEM_LIMIT = 10; - private static final String[] itemTagName = { - "Top", - "Integer", - "Float", - "Double", - "Long", - "Null", - "UninitializedThis", - "Object", - "Uninitialized", - "ReturnAddress",}; - private static final Set itemTagNames; - - static { - Set set = new HashSet(Arrays.asList(itemTagName)); - set.remove(null); - itemTagNames = Collections.unmodifiableSet(set); - } - protected static final HashMap attrTypesBacking; - protected static final Map attrTypesInit; - - static { - HashMap at = new HashMap(); - - //at.put("*.Deprecated", ""); - //at.put("*.Synthetic", ""); - ////at.put("Field.ConstantValue", "KQH"); - //at.put("Class.SourceFile", "RUH"); - at.put("Method.Bridge", ""); - at.put("Method.Varargs", ""); - at.put("Class.Enum", ""); - at.put("*.Signature", "RSH"); - //at.put("*.Deprecated", ""); - //at.put("*.Synthetic", ""); - at.put("Field.ConstantValue", "KQH"); - at.put("Class.SourceFile", "RUH"); - at.put("Class.InnerClasses", "NH[RCHRCHRUHFH]"); - at.put("Code.LineNumberTable", "NH[PHH]"); - at.put("Code.LocalVariableTable", "NH[PHHRUHRSHH]"); - at.put("Code.LocalVariableTypeTable", "NH[PHHRUHRSHH]"); - at.put("Method.Exceptions", "NH[RCH]"); - at.put("Method.Code", "..."); - at.put("Code.StackMapTable", "..."); - //at.put("Code.StkMapX", "..."); - if (true) { - at.put("Code.StackMapTable", - "[NH[(1)]]" - + "[TB" - + "(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79" - + ",80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95" - + ",96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111" - + ",112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127" - + ")[(4)]" - + "(247)[H(4)]" - + "(248)[H]" - + "(249)[H]" - + "(250)[H]" - + "(251)[H]" - + "(252)[H(4)]" - + "(253)[H(4)(4)]" - + "(254)[H(4)(4)(4)]" - + "(255)[H(2)(3)]" - + "()[]]" - + "[NH[(4)]]" - + "[NH[(4)]]" - + "[TB" - + ("(0)[]" - + "(1)[](2)[](3)[](4)[]" - + "(5)[](6)[]" - + "(7)[RCH]" - + "(8)[PH]" - + "()[]]")); - } - - at.put("Class.EnclosingMethod", "RCHRDH");//RDNH - - // Layouts of metadata attrs: - String vpf = "["; - String ipf = "["; - String apf = "["; - String mdanno2 = "" - + "RSHNH[RUH(3)]]" - + ("[TB" - + "(\\B,\\C,\\I,\\S,\\Z)[KIH]" - + "(\\D)[KDH]" - + "(\\F)[KFH]" - + "(\\J)[KJH]" - + "(\\c)[RSH]" - + "(\\e)[RSHRUH]" - + "(\\s)[RUH]" - + "(\\@)[(2)]" - + "(\\[)[NH[(3)]]" - + "()[]" - + "]"); - String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2; - String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2; - String vparamanno = "" - + "[NB[(1)]][NH[(2)]]" - + apf + mdanno2; - String iparamanno = "" - + "[NB[(1)]][NH[(2)]]" - + apf + mdanno2; - String mdannodef = "[(3)][(1)]" + apf + mdanno2; - String[] mdplaces = {"Class", "Field", "Method"}; - for (String place : mdplaces) { - at.put(place + ".RuntimeVisibleAnnotations", visanno); - at.put(place + ".RuntimeInvisibleAnnotations", invanno); - } - at.put("Method.RuntimeVisibleParameterAnnotations", vparamanno); - at.put("Method.RuntimeInvisibleParameterAnnotations", iparamanno); - at.put("Method.AnnotationDefault", mdannodef); - - attrTypesBacking = at; - attrTypesInit = Collections.unmodifiableMap(at); - } - - ; - private static final String[] jcovAttrTypes = { - "Code.CoverageTable=NH[PHHII]", - "Code.CharacterRangeTable=NH[PHPOHIIH]", - "Class.SourceID=RUH", - "Class.CompilationID=RUH" - }; - protected static final String[][] modifierNames = { - {"public"}, - {"private"}, - {"protected"}, - {"static"}, - {"final"}, - {"synchronized"}, - {null, "volatile", "bridge"}, - {null, "transient", "varargs"}, - {null, null, "native"}, - {"interface"}, - {"abstract"}, - {"strictfp"}, - {"synthetic"}, - {"annotation"}, - {"enum"},}; - protected static final String EIGHT_BIT_CHAR_ENCODING = "ISO8859_1"; - protected static final String UTF8_ENCODING = "UTF8"; - // What XML tags are used by this syntax, apart from attributes? - protected static final Set nonAttrTags; - - static { - HashSet tagSet = new HashSet(); - Collections.addAll(tagSet, new String[]{ - "ConstantPool",// the CP - "Class", // the class - "Interface", // implemented interfaces - "Method", // methods - "Field", // fields - "Handler", // exception handler pseudo-attribute - "Attribute", // unparsed attribute - "Bytes", // bytecodes - "Instructions" // bytecodes, parsed - }); - nonAttrTags = Collections.unmodifiableSet(tagSet); - } - - // Accessors. - public static Set nonAttrTags() { - return nonAttrTags; - } - - public static String cpTagName(int t) { - t &= 0xFF; - String ts = null; - if (t < cpTagName.length) { - ts = cpTagName[t]; - } - if (ts != null) { - return ts; - } - return ("UnknownTag" + (int) t).intern(); - } - - public static int cpTagValue(String name) { - for (int t = 0; t < cpTagName.length; t++) { - if (name.equals(cpTagName[t])) { - return t; - } - } - return 0; - } - - public static String itemTagName(int t) { - t &= 0xFF; - String ts = null; - if (t < itemTagName.length) { - ts = itemTagName[t]; - } - if (ts != null) { - return ts; - } - return ("UnknownItem" + (int) t).intern(); - } - - public static int itemTagValue(String name) { - for (int t = 0; t < itemTagName.length; t++) { - if (name.equals(itemTagName[t])) { - return t; - } - } - return -1; - } - - public void addJcovAttrTypes() { - addAttrTypes(jcovAttrTypes); - } - // Public methods for declaring attribute types. - protected Map attrTypes = attrTypesInit; - - public void addAttrType(String opt) { - int eqpos = opt.indexOf('='); - addAttrType(opt.substring(0, eqpos), opt.substring(eqpos + 1)); - } - - public void addAttrTypes(String[] opts) { - for (String opt : opts) { - addAttrType(opt); - } - } - - private void checkAttr(String attr) { - if (!attr.startsWith("Class.") - && !attr.startsWith("Field.") - && !attr.startsWith("Method.") - && !attr.startsWith("Code.") - && !attr.startsWith("*.")) { - throw new IllegalArgumentException("attr name must start with 'Class.', etc."); - } - String uattr = attr.substring(attr.indexOf('.') + 1); - if (nonAttrTags.contains(uattr)) { - throw new IllegalArgumentException("attr name must not be one of " + nonAttrTags); - } - } - - private void checkAttrs(Map at) { - for (String attr : at.keySet()) { - checkAttr(attr); - } - } - - private void modAttrs() { - if (attrTypes == attrTypesInit) { - // Make modifiable. - attrTypes = new HashMap(attrTypesBacking); - } - } - - public void addAttrType(String attr, String fmt) { - checkAttr(attr); - modAttrs(); - attrTypes.put(attr, fmt); - } - - public void addAttrTypes(Map at) { - checkAttrs(at); - modAttrs(); - attrTypes.putAll(at); - } - - public Map getAttrTypes() { - if (attrTypes == attrTypesInit) { - return attrTypes; - } - return Collections.unmodifiableMap(attrTypes); - } - - public void setAttrTypes(Map at) { - checkAttrs(at); - modAttrs(); - attrTypes.keySet().retainAll(at.keySet()); - attrTypes.putAll(at); - } - - // attr format helpers - protected static boolean matchTag(int tagValue, String caseStr) { - //System.out.println("matchTag "+tagValue+" in "+caseStr); - for (int pos = 0, max = caseStr.length(), comma; - pos < max; - pos = comma + 1) { - int caseValue; - if (caseStr.charAt(pos) == '\\') { - caseValue = caseStr.charAt(pos + 1); - comma = pos + 2; - assert (comma == max || caseStr.charAt(comma) == ','); - } else { - comma = caseStr.indexOf(',', pos); - if (comma < 0) { - comma = max; - } - caseValue = Integer.parseInt(caseStr.substring(pos, comma)); - } - if (tagValue == caseValue) { - return true; - } - } - return false; - } - - protected static String[] getBodies(String type) { - ArrayList bodies = new ArrayList(); - for (int i = 0; i < type.length();) { - String body = getBody(type, i); - bodies.add(body); - i += body.length() + 2; // skip body and brackets - } - return bodies.toArray(new String[bodies.size()]); - } - - protected static String getBody(String type, int i) { - assert (type.charAt(i) == '['); - int next = ++i; // skip bracket - for (int depth = 1; depth > 0; next++) { - switch (type.charAt(next)) { - case '[': - depth++; - break; - case ']': - depth--; - break; - case '(': - next = type.indexOf(')', next); - break; - case '<': - next = type.indexOf('>', next); - break; - } - assert (next > 0); - } - --next; // get before bracket - assert (type.charAt(next) == ']'); - return type.substring(i, next); - } - - public Element makeCPDigest(int length) { - MessageDigest md; - try { - md = MessageDigest.getInstance("MD5"); - } catch (java.security.NoSuchAlgorithmException ee) { - throw new Error(ee); - } - int items = 0; - for (Element e : cpool.elements()) { - if (items == length) { - break; - } - if (cpTagNames.contains(e.getName())) { - items += 1; - md.update((byte) cpTagValue(e.getName())); - try { - md.update(e.getText().toString().getBytes(UTF8_ENCODING)); - } catch (java.io.UnsupportedEncodingException ee) { - throw new Error(ee); - } - } - } - ByteBuffer bb = ByteBuffer.wrap(md.digest()); - String l0 = Long.toHexString(bb.getLong(0)); - String l1 = Long.toHexString(bb.getLong(8)); - while (l0.length() < 16) { - l0 = "0" + l0; - } - while (l1.length() < 16) { - l1 = "0" + l1; - } - return new Element("Digest", - "length", "" + items, - "bytes", l0 + l1); - } - - public Element getCPDigest(int length) { - if (length == -1) { - length = cpool.countAll(XMLKit.elementFilter(cpTagNames)); - } - for (Element md : cpool.findAllElements("Digest").elements()) { - if (md.getAttrLong("length") == length) { - return md; - } - } - Element md = makeCPDigest(length); - cpool.add(md); - return md; - } - - public Element getCPDigest() { - return getCPDigest(-1); - } - - public boolean checkCPDigest(Element md) { - return md.equals(getCPDigest((int) md.getAttrLong("length"))); - } - - public static int computeInterfaceNum(String intMethRef) { - intMethRef = intMethRef.substring(1 + intMethRef.lastIndexOf(' ')); - if (!intMethRef.startsWith("(")) { - return -1; - } - int signum = 1; // start with one for "this" - scanSig: - for (int i = 1; i < intMethRef.length(); i++) { - char ch = intMethRef.charAt(i); - signum++; - switch (ch) { - case ')': - --signum; - break scanSig; - case 'L': - i = intMethRef.indexOf(';', i); - break; - case '[': - while (ch == '[') { - ch = intMethRef.charAt(++i); - } - if (ch == 'L') { - i = intMethRef.indexOf(';', i); - } - break; - } - } - int num = (signum << 8) | 0; - //System.out.println("computeInterfaceNum "+intMethRef+" => "+num); - return num; - } - // Protected state for representing the class file. - protected Element cfile; // - protected Element cpool; // - protected Element klass; // - protected Element currentMember; // varies during scans - protected Element currentCode; // varies during scans -} diff --git a/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java b/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java deleted file mode 100644 index 037de37e540aac65ed9265b5630f6cfc40fd63f1..0000000000000000000000000000000000000000 --- a/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- - -import java.util.*; -import java.lang.reflect.*; -import java.io.*; -import xmlkit.XMLKit.Element; -/* - * @author jrose - */ -public class ClassWriter extends ClassSyntax implements ClassSyntax.GetCPIndex { - - private static final CommandLineParser CLP = new CommandLineParser("" - + "-source: +> = \n" - + "-dest: +> = \n" - + "-encoding: +> = \n" - + "-parseBytes $ \n" - + "- *? \n" - + "\n"); - - public static void main(String[] ava) throws IOException { - ArrayList av = new ArrayList(Arrays.asList(ava)); - HashMap props = new HashMap(); - props.put("-encoding:", "UTF8"); // default - CLP.parse(av, props); - File source = asFile(props.get("-source:")); - File dest = asFile(props.get("-dest:")); - String encoding = props.get("-encoding:"); - boolean parseBytes = props.containsKey("-parseBytes"); - boolean destMade = false; - - for (String a : av) { - File f; - File inf = new File(source, a); - System.out.println("Reading " + inf); - Element e; - if (inf.getName().endsWith(".class")) { - ClassReader cr = new ClassReader(); - cr.parseBytes = parseBytes; - e = cr.readFrom(inf); - f = new File(a); - } else if (inf.getName().endsWith(".xml")) { - InputStream in = new FileInputStream(inf); - Reader inw = ClassReader.makeReader(in, encoding); - e = XMLKit.readFrom(inw); - e.findAllInTree(XMLKit.and(XMLKit.elementFilter(nonAttrTags()), - XMLKit.methodFilter(Element.method("trimText")))); - //System.out.println(e); - inw.close(); - f = new File(a.substring(0, a.length() - ".xml".length()) + ".class"); - } else { - System.out.println("Warning: unknown input " + a); - continue; - } - // Now write it: - if (!destMade) { - destMade = true; - if (dest == null) { - dest = File.createTempFile("TestOut", ".dir", new File(".")); - dest.delete(); - System.out.println("Writing results to " + dest); - } - if (!(dest.isDirectory() || dest.mkdir())) { - throw new RuntimeException("Cannot create " + dest); - } - } - File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); - outf.getParentFile().mkdirs(); - new ClassWriter(e).writeTo(outf); - } - } - - private static File asFile(String str) { - return (str == null) ? null : new File(str); - } - - public void writeTo(File file) throws IOException { - OutputStream out = null; - try { - out = new BufferedOutputStream(new FileOutputStream(file)); - writeTo(out); - } finally { - if (out != null) { - out.close(); - } - } - } - protected String[] callables; // varies - protected int cpoolSize = 0; - protected HashMap attrTypesByTag; - protected OutputStream out; - protected HashMap cpMap = new HashMap(); - protected ArrayList attrBufs = new ArrayList(); - - private void setupAttrTypes() { - attrTypesByTag = new HashMap(); - for (String key : attrTypes.keySet()) { - String pfx = key.substring(0, key.indexOf('.') + 1); - String val = attrTypes.get(key); - int pos = val.indexOf('<'); - if (pos >= 0) { - String tag = val.substring(pos + 1, val.indexOf('>', pos)); - attrTypesByTag.put(pfx + tag, key); - } - } - //System.out.println("attrTypesByTag: "+attrTypesByTag); - } - - protected ByteArrayOutputStream getAttrBuf() { - int nab = attrBufs.size(); - if (nab == 0) { - return new ByteArrayOutputStream(1024); - } - ByteArrayOutputStream ab = attrBufs.get(nab - 1); - attrBufs.remove(nab - 1); - return ab; - } - - protected void putAttrBuf(ByteArrayOutputStream ab) { - ab.reset(); - attrBufs.add(ab); - } - - public ClassWriter(Element root) { - this(root, null); - } - - public ClassWriter(Element root, ClassSyntax cr) { - if (cr != null) { - attrTypes = cr.attrTypes; - } - setupAttrTypes(); - if (root.getName() == "ClassFile") { - cfile = root; - cpool = root.findElement("ConstantPool"); - klass = root.findElement("Class"); - } else if (root.getName() == "Class") { - cfile = new Element("ClassFile", - new String[]{ - "magic", String.valueOf(0xCAFEBABE), - "minver", "0", "majver", "46",}); - cpool = new Element("ConstantPool"); - klass = root; - } else { - throw new IllegalArgumentException("bad element type " + root.getName()); - } - if (cpool == null) { - cpool = new Element("ConstantPool"); - } - - int cpLen = 1 + cpool.size(); - for (Element c : cpool.elements()) { - int id = (int) c.getAttrLong("id"); - int tag = cpTagValue(c.getName()); - setCPIndex(tag, c.getText().toString(), id); - switch (tag) { - case CONSTANT_Long: - case CONSTANT_Double: - cpLen += 1; - } - } - cpoolSize = cpLen; - } - - public int findCPIndex(int tag, String name) { - if (name == null) { - return 0; - } - int[] ids = cpMap.get(name.toString()); - return (ids == null) ? 0 : ids[tag]; - } - - public int getCPIndex(int tag, String name) { - //System.out.println("getCPIndex "+cpTagName(tag)+" "+name); - if (name == null) { - return 0; - } - int id = findCPIndex(tag, name); - if (id == 0) { - id = cpoolSize; - cpoolSize += 1; - setCPIndex(tag, name, id); - cpool.add(new Element(cpTagName(tag), - new String[]{"id", "" + id}, - new Object[]{name})); - int pos; - switch (tag) { - case CONSTANT_Long: - case CONSTANT_Double: - cpoolSize += 1; - break; - case CONSTANT_Class: - case CONSTANT_String: - getCPIndex(CONSTANT_Utf8, name); - break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - pos = name.indexOf(' '); - getCPIndex(CONSTANT_Class, name.substring(0, pos)); - getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1)); - break; - case CONSTANT_NameAndType: - pos = name.indexOf(' '); - getCPIndex(CONSTANT_Utf8, name.substring(0, pos)); - getCPIndex(CONSTANT_Utf8, name.substring(pos + 1)); - break; - } - } - return id; - } - - public void setCPIndex(int tag, String name, int id) { - //System.out.println("setCPIndex id="+id+" tag="+tag+" name="+name); - int[] ids = cpMap.get(name); - if (ids == null) { - cpMap.put(name, ids = new int[13]); - } - if (ids[tag] != 0 && ids[tag] != id) { - System.out.println("Warning: Duplicate CP entries for " + ids[tag] + " and " + id); - } - //assert(ids[tag] == 0 || ids[tag] == id); - ids[tag] = id; - } - - public int parseFlags(String flagString) { - int flags = 0; - int i = -1; - for (String[] names : modifierNames) { - ++i; - for (String name : names) { - if (name == null) { - continue; - } - int pos = flagString.indexOf(name); - if (pos >= 0) { - flags |= (1 << i); - } - } - } - return flags; - } - - public void writeTo(OutputStream realOut) throws IOException { - OutputStream headOut = realOut; - ByteArrayOutputStream tailOut = new ByteArrayOutputStream(); - - // write the body of the class file first - this.out = tailOut; - writeClass(); - - // write the file header last - this.out = headOut; - u4((int) cfile.getAttrLong("magic")); - u2((int) cfile.getAttrLong("minver")); - u2((int) cfile.getAttrLong("majver")); - writeCP(); - - // recopy the file tail - this.out = null; - tailOut.writeTo(realOut); - } - - void writeClass() throws IOException { - int flags = parseFlags(klass.getAttr("flags")); - flags ^= Modifier.SYNCHRONIZED; - u2(flags); - cpRef(CONSTANT_Class, klass.getAttr("name")); - cpRef(CONSTANT_Class, klass.getAttr("super")); - Element interfaces = klass.findAllElements("Interface"); - u2(interfaces.size()); - for (Element e : interfaces.elements()) { - cpRef(CONSTANT_Class, e.getAttr("name")); - } - for (int isMethod = 0; isMethod <= 1; isMethod++) { - Element members = klass.findAllElements(isMethod != 0 ? "Method" : "Field"); - u2(members.size()); - for (Element m : members.elements()) { - writeMember(m, isMethod != 0); - } - } - writeAttributesFor(klass); - } - - private void writeMember(Element member, boolean isMethod) throws IOException { - //System.out.println("writeMember "+member); - u2(parseFlags(member.getAttr("flags"))); - cpRef(CONSTANT_Utf8, member.getAttr("name")); - cpRef(CONSTANT_Utf8, member.getAttr("type")); - writeAttributesFor(member); - } - - protected void writeAttributesFor(Element x) throws IOException { - LinkedHashSet attrNames = new LinkedHashSet(); - for (Element e : x.elements()) { - attrNames.add(e.getName()); // uniquifying - } - attrNames.removeAll(nonAttrTags()); - u2(attrNames.size()); - if (attrNames.isEmpty()) { - return; - } - Element prevCurrent; - if (x.getName() == "Code") { - prevCurrent = currentCode; - currentCode = x; - } else { - prevCurrent = currentMember; - currentMember = x; - } - OutputStream realOut = this.out; - for (String utag : attrNames) { - String qtag = x.getName() + "." + utag; - String wtag = "*." + utag; - String key = attrTypesByTag.get(qtag); - if (key == null) { - key = attrTypesByTag.get(wtag); - } - String type = attrTypes.get(key); - //System.out.println("tag "+qtag+" => key "+key+"; type "+type); - Element attrs = x.findAllElements(utag); - ByteArrayOutputStream attrBuf = getAttrBuf(); - if (type == null) { - if (attrs.size() != 1 || !attrs.get(0).equals(new Element(utag))) { - System.out.println("Warning: No attribute type description: " + qtag); - } - key = wtag; - } else { - try { - this.out = attrBuf; - // unparse according to type desc. - if (type.equals("...")) { - writeCode((Element) attrs.get(0)); // assume only 1 - } else if (type.equals("...")) { - writeStackMap(attrs, false); - } else if (type.equals("...")) { - writeStackMap(attrs, true); - } else if (type.startsWith("[")) { - writeAttributeRecursive(attrs, type); - } else { - writeAttribute(attrs, type); - } - } finally { - //System.out.println("Attr Bytes = \""+attrBuf.toString(EIGHT_BIT_CHAR_ENCODING).replace('"', (char)('"'|0x80))+"\""); - this.out = realOut; - } - } - cpRef(CONSTANT_Utf8, key.substring(key.indexOf('.') + 1)); - u4(attrBuf.size()); - attrBuf.writeTo(out); - putAttrBuf(attrBuf); - } - if (x.getName() == "Code") { - currentCode = prevCurrent; - } else { - currentMember = prevCurrent; - } - } - - private void writeAttributeRecursive(Element aval, String type) throws IOException { - assert (callables == null); - callables = getBodies(type); - writeAttribute(aval, callables[0]); - callables = null; - } - - private void writeAttribute(Element aval, String type) throws IOException { - //System.out.println("writeAttribute "+aval+" using "+type); - String nextAttrName = null; - boolean afterElemHead = false; - for (int len = type.length(), next, i = 0; i < len; i = next) { - int value; - char intKind; - int tag; - int sigChar; - String attrValue; - switch (type.charAt(i)) { - case '<': - assert (nextAttrName == null); - next = type.indexOf('>', i); - String form = type.substring(i + 1, next++); - if (form.indexOf('=') < 0) { - // elem_placement = '<' elemname '>' - if (aval.isAnonymous()) { - assert (aval.size() == 1); - aval = (Element) aval.get(0); - } - assert (aval.getName().equals(form)) : aval + " // " + form; - afterElemHead = true; - } else { - // attr_placement = '(' attrname '=' (value)? ')' - int eqPos = form.indexOf('='); - assert (eqPos >= 0); - nextAttrName = form.substring(0, eqPos).intern(); - if (eqPos != form.length() - 1) { - // value is implicit, not placed in file - nextAttrName = null; - } - afterElemHead = false; - } - continue; - case '(': - next = type.indexOf(')', ++i); - int callee = Integer.parseInt(type.substring(i, next++)); - writeAttribute(aval, callables[callee]); - continue; - case 'N': // replication = 'N' int '[' type ... ']' - { - assert (nextAttrName == null); - afterElemHead = false; - char countType = type.charAt(i + 1); - next = i + 2; - String type1 = getBody(type, next); - Element elems = aval; - if (type1.startsWith("<")) { - // Select only matching members of aval. - String elemName = type1.substring(1, type1.indexOf('>')); - elems = aval.findAllElements(elemName); - } - putInt(elems.size(), countType); - next += type1.length() + 2; // skip body and brackets - for (Element elem : elems.elements()) { - writeAttribute(elem, type1); - } - } - continue; - case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']' - // write the value - value = (int) aval.getAttrLong("tag"); - assert (aval.getAttr("tag") != null) : aval; - intKind = type.charAt(++i); - if (intKind == 'S') { - intKind = type.charAt(++i); - } - putInt(value, intKind); - nextAttrName = null; - afterElemHead = false; - ++i; // skip the int type char - // union_case = '(' ('-')? digit+ ')' '[' body ']' - for (boolean foundCase = false;;) { - assert (type.charAt(i) == '('); - next = type.indexOf(')', ++i); - assert (next >= i); - String caseStr = type.substring(i, next++); - String type1 = getBody(type, next); - next += type1.length() + 2; // skip body and brackets - boolean lastCase = (caseStr.length() == 0); - if (!foundCase - && (lastCase || matchTag(value, caseStr))) { - foundCase = true; - // Execute this body. - writeAttribute(aval, type1); - } - if (lastCase) { - break; - } - } - continue; - case 'B': - case 'H': - case 'I': // int = oneof "BHI" - value = (int) aval.getAttrLong(nextAttrName); - intKind = type.charAt(i); - next = i + 1; - break; - case 'K': - sigChar = type.charAt(i + 1); - if (sigChar == 'Q') { - assert (currentMember.getName() == "Field"); - assert (aval.getName() == "ConstantValue"); - String sig = currentMember.getAttr("type"); - sigChar = sig.charAt(0); - switch (sigChar) { - case 'Z': - case 'B': - case 'C': - case 'S': - sigChar = 'I'; - break; - } - } - switch (sigChar) { - case 'I': - tag = CONSTANT_Integer; - break; - case 'J': - tag = CONSTANT_Long; - break; - case 'F': - tag = CONSTANT_Float; - break; - case 'D': - tag = CONSTANT_Double; - break; - case 'L': - tag = CONSTANT_String; - break; - default: - assert (false); - tag = 0; - } - assert (type.charAt(i + 2) == 'H'); // only H works for now - next = i + 3; - assert (afterElemHead || nextAttrName != null); - //System.out.println("get attr "+nextAttrName+" in "+aval); - if (nextAttrName != null) { - attrValue = aval.getAttr(nextAttrName); - assert (attrValue != null); - } else { - assert (aval.isText()) : aval; - attrValue = aval.getText().toString(); - } - value = getCPIndex(tag, attrValue); - intKind = 'H'; //type.charAt(i+2); - break; - case 'R': - sigChar = type.charAt(i + 1); - switch (sigChar) { - case 'C': - tag = CONSTANT_Class; - break; - case 'S': - tag = CONSTANT_Utf8; - break; - case 'D': - tag = CONSTANT_Class; - break; - case 'F': - tag = CONSTANT_Fieldref; - break; - case 'M': - tag = CONSTANT_Methodref; - break; - case 'I': - tag = CONSTANT_InterfaceMethodref; - break; - case 'U': - tag = CONSTANT_Utf8; - break; - //case 'Q': tag = CONSTANT_Class; break; - default: - assert (false); - tag = 0; - } - assert (type.charAt(i + 2) == 'H'); // only H works for now - next = i + 3; - assert (afterElemHead || nextAttrName != null); - //System.out.println("get attr "+nextAttrName+" in "+aval); - if (nextAttrName != null) { - attrValue = aval.getAttr(nextAttrName); - } else if (aval.hasText()) { - attrValue = aval.getText().toString(); - } else { - attrValue = null; - } - value = getCPIndex(tag, attrValue); - intKind = 'H'; //type.charAt(i+2); - break; - case 'P': // bci = 'P' int - case 'S': // signed_int = 'S' int - next = i + 2; - value = (int) aval.getAttrLong(nextAttrName); - intKind = type.charAt(i + 1); - break; - case 'F': - next = i + 2; - value = parseFlags(aval.getAttr(nextAttrName)); - intKind = type.charAt(i + 1); - break; - default: - throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type); - } - // write the value - putInt(value, intKind); - nextAttrName = null; - afterElemHead = false; - } - assert (nextAttrName == null); - } - - private void putInt(int x, char ch) throws IOException { - switch (ch) { - case 'B': - u1(x); - break; - case 'H': - u2(x); - break; - case 'I': - u4(x); - break; - } - assert ("BHI".indexOf(ch) >= 0); - } - - private void writeCode(Element code) throws IOException { - //System.out.println("writeCode "+code); - //Element m = new Element(currentMember); m.remove(code); - //System.out.println(" in "+m); - int stack = (int) code.getAttrLong("stack"); - int local = (int) code.getAttrLong("local"); - Element bytes = code.findElement("Bytes"); - Element insns = code.findElement("Instructions"); - String bytecodes; - if (insns == null) { - bytecodes = bytes.getText().toString(); - } else { - bytecodes = InstructionSyntax.assemble(insns, this); - // Cache the assembled bytecodes: - bytes = new Element("Bytes", (String[]) null, bytecodes); - code.add(0, bytes); - } - u2(stack); - u2(local); - int length = bytecodes.length(); - u4(length); - for (int i = 0; i < length; i++) { - u1((byte) bytecodes.charAt(i)); - } - Element handlers = code.findAllElements("Handler"); - u2(handlers.size()); - for (Element handler : handlers.elements()) { - int start = (int) handler.getAttrLong("start"); - int end = (int) handler.getAttrLong("end"); - int catsh = (int) handler.getAttrLong("catch"); - u2(start); - u2(end); - u2(catsh); - cpRef(CONSTANT_Class, handler.getAttr("class")); - } - writeAttributesFor(code); - } - - protected void writeStackMap(Element attrs, boolean hasXOption) throws IOException { - Element bytes = currentCode.findElement("Bytes"); - assert (bytes != null && bytes.size() == 1); - int byteLength = ((String) bytes.get(0)).length(); - boolean uoffsetIsU4 = (byteLength >= (1 << 16)); - boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); - boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); - if (uoffsetIsU4) { - u4(attrs.size()); - } else { - u2(attrs.size()); - } - for (Element frame : attrs.elements()) { - int bci = (int) frame.getAttrLong("bci"); - if (uoffsetIsU4) { - u4(bci); - } else { - u2(bci); - } - if (hasXOption) { - u1((int) frame.getAttrLong("flags")); - } - // Scan local and stack types in this frame: - final int LOCALS = 0, STACK = 1; - for (int j = LOCALS; j <= STACK; j++) { - Element types = frame.findElement(j == LOCALS ? "Local" : "Stack"); - int typeSize = (types == null) ? 0 : types.size(); - if (j == LOCALS) { - if (ulocalvarIsU4) { - u4(typeSize); - } else { - u2(typeSize); - } - } else { // STACK - if (ustackIsU4) { - u4(typeSize); - } else { - u2(typeSize); - } - } - if (types == null) { - continue; - } - for (Element type : types.elements()) { - int tag = itemTagValue(type.getName()); - u1(tag); - switch (tag) { - case ITEM_Object: - cpRef(CONSTANT_Class, type.getAttr("class")); - break; - case ITEM_Uninitialized: - case ITEM_ReturnAddress: { - int offset = (int) type.getAttrLong("bci"); - if (uoffsetIsU4) { - u4(offset); - } else { - u2(offset); - } - } - break; - } - } - } - } - } - - public void writeCP() throws IOException { - int cpLen = cpoolSize; - u2(cpLen); - ByteArrayOutputStream buf = getAttrBuf(); - for (Element c : cpool.elements()) { - if (!c.isText()) { - System.out.println("## !isText " + c); - } - int id = (int) c.getAttrLong("id"); - int tag = cpTagValue(c.getName()); - String name = c.getText().toString(); - int pos; - u1(tag); - switch (tag) { - case CONSTANT_Utf8: { - int done = 0; - buf.reset(); - int nameLen = name.length(); - while (done < nameLen) { - int next = name.indexOf((char) 0, done); - if (next < 0) { - next = nameLen; - } - if (done < next) { - buf.write(name.substring(done, next).getBytes(UTF8_ENCODING)); - } - if (next < nameLen) { - buf.write(0300); - buf.write(0200); - next++; - } - done = next; - } - u2(buf.size()); - buf.writeTo(out); - } - break; - case CONSTANT_Integer: - u4(Integer.parseInt(name)); - break; - case CONSTANT_Float: - u4(Float.floatToIntBits(Float.parseFloat(name))); - break; - case CONSTANT_Long: - u8(Long.parseLong(name)); - //i += 1; // no need: extra cp slot is implicit - break; - case CONSTANT_Double: - u8(Double.doubleToLongBits(Double.parseDouble(name))); - //i += 1; // no need: extra cp slot is implicit - break; - case CONSTANT_Class: - case CONSTANT_String: - u2(getCPIndex(CONSTANT_Utf8, name)); - break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - pos = name.indexOf(' '); - u2(getCPIndex(CONSTANT_Class, name.substring(0, pos))); - u2(getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1))); - break; - case CONSTANT_NameAndType: - pos = name.indexOf(' '); - u2(getCPIndex(CONSTANT_Utf8, name.substring(0, pos))); - u2(getCPIndex(CONSTANT_Utf8, name.substring(pos + 1))); - break; - } - } - putAttrBuf(buf); - } - - public void cpRef(int tag, String name) throws IOException { - u2(getCPIndex(tag, name)); - } - - public void u8(long x) throws IOException { - u4((int) (x >>> 32)); - u4((int) (x >>> 0)); - } - - public void u4(int x) throws IOException { - u2(x >>> 16); - u2(x >>> 0); - } - - public void u2(int x) throws IOException { - u1(x >>> 8); - u1(x >>> 0); - } - - public void u1(int x) throws IOException { - out.write(x & 0xFF); - } -} - diff --git a/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java b/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java deleted file mode 100644 index cbe34e6b96020c29a6ade25f731e4390eb52dbbb..0000000000000000000000000000000000000000 --- a/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- - -import xmlkit.XMLKit.Element; -import java.util.HashMap; -/* - * @author jrose - */ -abstract class InstructionAssembler extends InstructionSyntax { - - InstructionAssembler() { - } - - public static String assemble(Element instructions, String pcAttrName, - ClassSyntax.GetCPIndex getCPI) { - int insCount = instructions.size(); - Element[] insElems = new Element[insCount]; - int[] elemToIndexMap; - int[] insLocs; - byte[] ops = new byte[insCount]; - int[] operands = new int[insCount]; - boolean[] isWide = new boolean[insCount]; - int[] branches; - int[] branchInsLocs; - HashMap labels = new HashMap(); - - final int WIDE = 0xc4; - final int GOTO = 0xa7; - final int GOTO_W = 0xc8; - final int GOTO_LEN = 3; - final int GOTO_W_LEN = 5; - assert ("wide".equals(bcNames[WIDE])); - assert ("goto".equals(bcNames[GOTO])); - assert ("goto_w".equals(bcNames[GOTO_W])); - assert (bcFormats[GOTO].length() == GOTO_LEN); - assert (bcFormats[GOTO_W].length() == GOTO_W_LEN); - - // Unpack instructions into temp. arrays, and find branches and labels. - { - elemToIndexMap = (pcAttrName != null) ? new int[insCount] : null; - int[] buffer = operands; - int id = 0; - int branchCount = 0; - for (int i = 0; i < insCount; i++) { - Element ins = (Element) instructions.get(i); - if (elemToIndexMap != null) { - elemToIndexMap[i] = (ins.getAttr(pcAttrName) != null ? id : -1); - } - String lab = ins.getAttr("pc"); - if (lab != null) { - labels.put(lab, String.valueOf(id)); - } - int op = opCode(ins.getName()); - if (op < 0) { - assert (ins.getAttr(pcAttrName) != null - || ins.getName().equals("label")); - continue; // delete PC holder element - } - if (op == WIDE) { //0xc4 - isWide[id] = true; // force wide format - continue; - } - if (bcFormats[op].indexOf('o') >= 0) { - buffer[branchCount++] = id; - } - if (bcFormats[op] == bcWideFormats[op]) { - isWide[id] = false; - } - insElems[id] = ins; - ops[id] = (byte) op; - id++; - } - insCount = id; // maybe we deleted some wide prefixes, etc. - branches = new int[branchCount + 1]; - System.arraycopy(buffer, 0, branches, 0, branchCount); - branches[branchCount] = -1; // sentinel - } - - // Compute instruction sizes. These sizes are final, - // except for branch instructions, which may need lengthening. - // Some instructions (ldc, bipush, iload, iinc) are automagically widened. - insLocs = new int[insCount + 1]; - int loc = 0; - for (int bn = 0, id = 0; id < insCount; id++) { - insLocs[id] = loc; - Element ins = insElems[id]; - int op = ops[id] & 0xFF; - String format = opFormat(op, isWide[id]); - // Make sure operands fit within the given format. - for (int j = 1, jlimit = format.length(); j < jlimit; j++) { - char fc = format.charAt(j); - int x = 0; - switch (fc) { - case 'l': - x = (int) ins.getAttrLong("loc"); - assert (x >= 0); - if (x > 0xFF && !isWide[id]) { - isWide[id] = true; - format = opFormat(op, isWide[id]); - } - assert (x <= 0xFFFF); - break; - case 'k': - char fc2 = format.charAt(Math.min(j + 1, format.length() - 1)); - x = getCPIndex(ins, fc2, getCPI); - if (x > 0xFF && j == jlimit - 1) { - assert (op == 0x12); //ldc - ops[id] = (byte) (op = 0x13); //ldc_w - format = opFormat(op); - } - assert (x <= 0xFFFF); - j++; // skip type-of-constant marker - break; - case 'x': - x = (int) ins.getAttrLong("num"); - assert (x >= 0 && x <= ((j == jlimit - 1) ? 0xFF : 0xFFFF)); - break; - case 's': - x = (int) ins.getAttrLong("num"); - if (x != (byte) x && j == jlimit - 1) { - switch (op) { - case 0x10: //bipush - ops[id] = (byte) (op = 0x11); //sipush - break; - case 0x84: //iinc - isWide[id] = true; - format = opFormat(op, isWide[id]); - break; - default: - assert (false); // cannot lengthen - } - } - // unsign the value now, to make later steps clearer - if (j == jlimit - 1) { - assert (x == (byte) x); - x = x & 0xFF; - } else { - assert (x == (short) x); - x = x & 0xFFFF; - } - break; - case 'o': - assert (branches[bn] == id); - bn++; - // make local copies of the branches, and fix up labels - insElems[id] = ins = new Element(ins); - String newLab = labels.get(ins.getAttr("lab")); - assert (newLab != null); - ins.setAttr("lab", newLab); - int prevCas = 0; - int k = 0; - for (Element cas : ins.elements()) { - assert (cas.getName().equals("Case")); - ins.set(k++, cas = new Element(cas)); - newLab = labels.get(cas.getAttr("lab")); - assert (newLab != null); - cas.setAttr("lab", newLab); - int thisCas = (int) cas.getAttrLong("num"); - assert (op == 0xab - || op == 0xaa && (k == 0 || thisCas == prevCas + 1)); - prevCas = thisCas; - } - break; - case 't': - // switch table is represented as Switch.Case sub-elements - break; - default: - assert (false); - } - operands[id] = x; // record operand (last if there are 2) - // skip redundant chars - while (j + 1 < jlimit && format.charAt(j + 1) == fc) { - ++j; - } - } - - switch (op) { - case 0xaa: //tableswitch - loc = switchBase(loc); - loc += 4 * (3 + ins.size()); - break; - case 0xab: //lookupswitch - loc = switchBase(loc); - loc += 4 * (2 + 2 * ins.size()); - break; - default: - if (isWide[id]) { - loc++; // 'wide' opcode prefix - } - loc += format.length(); - break; - } - } - insLocs[insCount] = loc; - - // compute branch offsets, and see if any branches need expansion - for (int maxTries = 9, tries = 0;; ++tries) { - boolean overflowing = false; - boolean[] branchExpansions = null; - for (int bn = 0; bn < branches.length - 1; bn++) { - int id = branches[bn]; - Element ins = insElems[id]; - int insSize = insLocs[id + 1] - insLocs[id]; - int origin = insLocs[id]; - int target = insLocs[(int) ins.getAttrLong("lab")]; - int offset = target - origin; - operands[id] = offset; - //System.out.println("branch id="+id+" len="+insSize+" to="+target+" offset="+offset); - assert (insSize == GOTO_LEN || insSize == GOTO_W_LEN || ins.getName().indexOf("switch") > 0); - boolean thisOverflow = (insSize == GOTO_LEN && (offset != (short) offset)); - if (thisOverflow && !overflowing) { - overflowing = true; - branchExpansions = new boolean[branches.length]; - } - if (thisOverflow || tries == maxTries - 1) { - // lengthen the branch - assert (!(thisOverflow && isWide[id])); - isWide[id] = true; - branchExpansions[bn] = true; - } - } - if (!overflowing) { - break; // done, usually on first try - } - assert (tries <= maxTries); - - // Walk over all instructions, expanding branches and updating locations. - int fixup = 0; - for (int bn = 0, id = 0; id < insCount; id++) { - insLocs[id] += fixup; - if (branches[bn] == id) { - int op = ops[id] & 0xFF; - int wop; - boolean invert; - if (branchExpansions[bn]) { - switch (op) { - case GOTO: //0xa7 - wop = GOTO_W; //0xc8 - invert = false; - break; - case 0xa8: //jsr - wop = 0xc9; //jsr_w - invert = false; - break; - default: - wop = invertBranchOp(op); - invert = true; - break; - } - assert (op != wop); - ops[id] = (byte) wop; - isWide[id] = invert; - if (invert) { - fixup += GOTO_W_LEN; //branch around a wide goto - } else { - fixup += (GOTO_W_LEN - GOTO_LEN); - } - // done expanding: ops and isWide reflect the decision - } - bn++; - } - } - insLocs[insCount] += fixup; - } - // we know the layout now - - // notify the caller of offsets, if requested - if (elemToIndexMap != null) { - for (int i = 0; i < elemToIndexMap.length; i++) { - int id = elemToIndexMap[i]; - if (id >= 0) { - Element ins = (Element) instructions.get(i); - ins.setAttr(pcAttrName, "" + insLocs[id]); - } - } - elemToIndexMap = null; // release the pointer - } - - // output the bytes - StringBuffer sbuf = new StringBuffer(insLocs[insCount]); - for (int bn = 0, id = 0; id < insCount; id++) { - //System.out.println("output id="+id+" loc="+insLocs[id]+" len="+(insLocs[id+1]-insLocs[id])+" #sbuf="+sbuf.length()); - assert (sbuf.length() == insLocs[id]); - Element ins; - int pc = insLocs[id]; - int nextpc = insLocs[id + 1]; - int op = ops[id] & 0xFF; - int opnd = operands[id]; - String format; - if (branches[bn] == id) { - bn++; - sbuf.append((char) op); - if (isWide[id]) { - // emit