提交 d183c7bc 编写于 作者: K ksrini

6981776: Pack200 must support -target 7 bytecodes

Summary: pack200 implementation of JSR-200 updated for JSR-292 changes
Reviewed-by: jrose, ksrini
Contributed-by: john.r.rose@oracle.com, kumar.x.srinivasan@oracle.com
上级 4ef15b70
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -166,6 +166,7 @@ class Attribute implements Comparable<Attribute> { ...@@ -166,6 +166,7 @@ class Attribute implements Comparable<Attribute> {
define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH"); define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH");
define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH"); define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH");
define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]"); 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, "Signature", "RSH");
define(sd, ATTR_CONTEXT_FIELD, "Synthetic", ""); define(sd, ATTR_CONTEXT_FIELD, "Synthetic", "");
...@@ -203,6 +204,8 @@ class Attribute implements Comparable<Attribute> { ...@@ -203,6 +204,8 @@ class Attribute implements Comparable<Attribute> {
// Their layout specs. are given here for completeness. // Their layout specs. are given here for completeness.
// The Code spec is incomplete, in that it does not distinguish // The Code spec is incomplete, in that it does not distinguish
// bytecode bytes or locate CP references. // bytecode bytes or locate CP references.
// The BootstrapMethods attribute is also special-cased
// elsewhere as an appendix to the local constant pool.
} }
// Metadata. // Metadata.
...@@ -822,9 +825,9 @@ class Attribute implements Comparable<Attribute> { ...@@ -822,9 +825,9 @@ class Attribute implements Comparable<Attribute> {
reference_type: reference_type:
( constant_ref | schema_ref | utf8_ref | untyped_ref ) ( constant_ref | schema_ref | utf8_ref | untyped_ref )
constant_ref: constant_ref:
( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' ) ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' )
schema_ref: schema_ref:
( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' ) ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' )
utf8_ref: utf8_ref:
'RU' 'RU'
untyped_ref: untyped_ref:
...@@ -1012,7 +1015,12 @@ class Attribute implements Comparable<Attribute> { ...@@ -1012,7 +1015,12 @@ class Attribute implements Comparable<Attribute> {
case 'F': e.refKind = CONSTANT_Float; break; case 'F': e.refKind = CONSTANT_Float; break;
case 'D': e.refKind = CONSTANT_Double; break; case 'D': e.refKind = CONSTANT_Double; break;
case 'S': e.refKind = CONSTANT_String; 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 default: { i = -i; continue; } // fail
} }
break; break;
...@@ -1029,6 +1037,11 @@ class Attribute implements Comparable<Attribute> { ...@@ -1029,6 +1037,11 @@ class Attribute implements Comparable<Attribute> {
case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref
case 'Q': e.refKind = CONSTANT_All; break; //untyped_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 default: { i = -i; continue; } // fail
} }
break; break;
...@@ -1279,10 +1292,12 @@ class Attribute implements Comparable<Attribute> { ...@@ -1279,10 +1292,12 @@ class Attribute implements Comparable<Attribute> {
// Cf. ClassReader.readSignatureRef. // Cf. ClassReader.readSignatureRef.
String typeName = globalRef.stringValue(); String typeName = globalRef.stringValue();
globalRef = ConstantPool.getSignatureEntry(typeName); 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_Integer);
assert(globalRef.getTag() <= CONSTANT_String); assert(globalRef.getTag() <= CONSTANT_String ||
} else if (e.refKind != CONSTANT_All) { globalRef.getTag() >= CONSTANT_MethodHandle);
assert(globalRef.getTag() <= CONSTANT_MethodType);
} else if (e.refKind < CONSTANT_GroupFirst) {
assert(e.refKind == globalRef.getTag()); assert(e.refKind == globalRef.getTag());
} }
} }
...@@ -1462,27 +1477,29 @@ class Attribute implements Comparable<Attribute> { ...@@ -1462,27 +1477,29 @@ class Attribute implements Comparable<Attribute> {
"NH[PHPOHIIH]", // CharacterRangeTable "NH[PHPOHIIH]", // CharacterRangeTable
"NH[PHHII]", // CoverageTable "NH[PHHII]", // CoverageTable
"NH[RCHRCNHRUNHFH]", // InnerClasses "NH[RCHRCNHRUNHFH]", // InnerClasses
"NH[RMHNH[KLH]]", // BootstrapMethods
"HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code
"=AnnotationDefault", "=AnnotationDefault",
// Like metadata, but with a compact tag set: // Like metadata, but with a compact tag set:
"[NH[(1)]]" "[NH[(1)]]"
+"[NH[(2)]]" +"[NH[(1)]]"
+"[RSHNH[RUH(3)]]" +"[RSHNH[RUH(1)]]"
+"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(2)](6)[NH[(3)]]()[]]", +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]",
"" ""
}; };
ap = 0; ap = 0;
} }
Utils.currentInstance.set(new PackerImpl());
final int[][] counts = new int[2][3]; // int bci ref final int[][] counts = new int[2][3]; // int bci ref
final Entry[] cpMap = new Entry[maxVal+1]; final Entry[] cpMap = new Entry[maxVal+1];
for (int i = 0; i < cpMap.length; i++) { for (int i = 0; i < cpMap.length; i++) {
if (i == 0) continue; // 0 => null if (i == 0) continue; // 0 => null
cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i)); cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i));
} }
Class cls = new Package().new Class(""); Package.Class cls = new Package().new Class("");
cls.cpMap = cpMap; cls.cpMap = cpMap;
class TestValueStream extends ValueStream { class TestValueStream extends ValueStream {
Random rand = new Random(0); java.util.Random rand = new java.util.Random(0);
ArrayList history = new ArrayList(); ArrayList history = new ArrayList();
int ckidx = 0; int ckidx = 0;
int maxVal; int maxVal;
...@@ -1570,8 +1587,7 @@ class Attribute implements Comparable<Attribute> { ...@@ -1570,8 +1587,7 @@ class Attribute implements Comparable<Attribute> {
String layout = av[i]; String layout = av[i];
if (layout.startsWith("=")) { if (layout.startsWith("=")) {
String name = layout.substring(1); String name = layout.substring(1);
for (Iterator j = standardDefs.values().iterator(); j.hasNext(); ) { for (Attribute a : standardDefs.values()) {
Attribute a = (Attribute) j.next();
if (a.name().equals(name)) { if (a.name().equals(name)) {
layout = a.layout().layout(); layout = a.layout().layout();
break; break;
...@@ -1604,7 +1620,7 @@ class Attribute implements Comparable<Attribute> { ...@@ -1604,7 +1620,7 @@ class Attribute implements Comparable<Attribute> {
if (verbose) { if (verbose) {
System.out.print(" parse: {"); System.out.print(" parse: {");
} }
self.parse(0, cls, bytes, 0, bytes.length, tts); self.parse(cls, bytes, 0, bytes.length, tts);
if (verbose) { if (verbose) {
System.out.println("}"); System.out.println("}");
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -1372,17 +1372,17 @@ class BandStructure { ...@@ -1372,17 +1372,17 @@ class BandStructure {
protected long archiveSize1; // size reported in archive_header protected long archiveSize1; // size reported in archive_header
protected int archiveNextCount; // 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_LENGTH_0 = 3; // archive_header_0 = {minver, majver, options}
static final int AH_ARCHIVE_SIZE_HI = 0; static final int AH_LENGTH_MIN = 15; // observed in spec {header_0[3], cp_counts[8], class_counts[4]}
static final int AH_ARCHIVE_SIZE_LO = 1; // Length contributions from optional archive size fields:
static final int AH_LENGTH_S = 2; //optional size hi/lo static final int AH_LENGTH_S = 2; // archive_header_S = optional {size_hi, size_lo}
static final int AH_LENGTH = 26; // mentioned in spec 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: // Length contributions from optional header fields:
static final int AH_FILE_HEADER_LEN = 5; // sizehi/lo/next/modtime/files 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; // layouts/band-headers static final int AH_SPECIAL_FORMAT_LEN = 2; // special_counts = {layouts, band_headers}
static final int AH_CP_NUMBER_LEN = 4; // int/float/long/double static final int AH_CP_NUMBER_LEN = 4; // cp_number_counts = {int, float, long, double}
static final int AH_LENGTH_MIN = AH_LENGTH static final int AH_CP_EXTRA_LEN = 4; // cp_attr_counts = {MH, MT, InDy, BSM}
-(AH_SPECIAL_FORMAT_LEN+AH_FILE_HEADER_LEN+AH_CP_NUMBER_LEN);
// Common structure of attribute band groups: // Common structure of attribute band groups:
static final int AB_FLAGS_HI = 0; static final int AB_FLAGS_HI = 0;
...@@ -1446,6 +1446,14 @@ class BandStructure { ...@@ -1446,6 +1446,14 @@ class BandStructure {
CPRefBand cp_Method_desc = cp_bands.newCPRefBand("cp_Method_desc", UDELTA5, CONSTANT_NameandType); 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_class = cp_bands.newCPRefBand("cp_Imethod_class", CONSTANT_Class);
CPRefBand cp_Imethod_desc = cp_bands.newCPRefBand("cp_Imethod_desc", UDELTA5, CONSTANT_NameandType); 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: // bands for carrying attribute definitions:
MultiBand attr_definition_bands = all_bands.newMultiBand("(attr_definition_bands)", UNSIGNED5); MultiBand attr_definition_bands = all_bands.newMultiBand("(attr_definition_bands)", UNSIGNED5);
...@@ -1481,7 +1489,7 @@ class BandStructure { ...@@ -1481,7 +1489,7 @@ class BandStructure {
IntBand field_attr_calls = field_attr_bands.newIntBand("field_attr_calls"); IntBand field_attr_calls = field_attr_bands.newIntBand("field_attr_calls");
// bands for predefined field attributes // 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); 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); MultiBand field_metadata_bands = field_attr_bands.newMultiBand("(field_metadata_bands)", UNSIGNED5);
...@@ -1585,12 +1593,14 @@ class BandStructure { ...@@ -1585,12 +1593,14 @@ class BandStructure {
CPRefBand bc_longref = bc_bands.newCPRefBand("bc_longref", DELTA5, CONSTANT_Long); CPRefBand bc_longref = bc_bands.newCPRefBand("bc_longref", DELTA5, CONSTANT_Long);
CPRefBand bc_doubleref = bc_bands.newCPRefBand("bc_doubleref", DELTA5, CONSTANT_Double); CPRefBand bc_doubleref = bc_bands.newCPRefBand("bc_doubleref", DELTA5, CONSTANT_Double);
CPRefBand bc_stringref = bc_bands.newCPRefBand("bc_stringref", DELTA5, CONSTANT_String); 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 // 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_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_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_methodref = bc_bands.newCPRefBand("bc_methodref", CONSTANT_Methodref); // invoke[vs]*
CPRefBand bc_imethodref = bc_bands.newCPRefBand("bc_imethodref", DELTA5, CONSTANT_InterfaceMethodref); // invokeinterface 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 // _self_linker_op family
CPRefBand bc_thisfield = bc_bands.newCPRefBand("bc_thisfield", CONSTANT_None); // any field within cur. class CPRefBand bc_thisfield = bc_bands.newCPRefBand("bc_thisfield", CONSTANT_None); // any field within cur. class
...@@ -1633,7 +1643,7 @@ class BandStructure { ...@@ -1633,7 +1643,7 @@ class BandStructure {
protected void setBandIndex(CPRefBand b, byte which) { protected void setBandIndex(CPRefBand b, byte which) {
Object[] need = { b, Byte.valueOf(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). // I.e., attribute layouts KQ (no null) or KQN (null ok).
allKQBands.add(b); allKQBands.add(b);
} else if (needPredefIndex != null) { } else if (needPredefIndex != null) {
...@@ -1856,12 +1866,20 @@ class BandStructure { ...@@ -1856,12 +1866,20 @@ class BandStructure {
attrClassFileVersionMask = (1<<CLASS_ATTR_ClassFile_version); attrClassFileVersionMask = (1<<CLASS_ATTR_ClassFile_version);
} }
private void adjustToMajver() { private void adjustToMajver() throws IOException {
if (getPackageMajver() < JAVA6_PACKAGE_MAJOR_VERSION) { if (getPackageMajver() < JAVA6_PACKAGE_MAJOR_VERSION) {
if (verbose > 0) Utils.log.fine("Legacy package version"); if (verbose > 0) Utils.log.fine("Legacy package version");
// Revoke definition of pre-1.6 attribute type. // Revoke definition of pre-1.6 attribute type.
undefineAttribute(CODE_ATTR_StackMapTable, ATTR_CONTEXT_CODE); 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() { protected void initAttrIndexLimit() {
...@@ -2323,7 +2341,9 @@ class BandStructure { ...@@ -2323,7 +2341,9 @@ class BandStructure {
return bc_methodref; return bc_methodref;
case CONSTANT_InterfaceMethodref: case CONSTANT_InterfaceMethodref:
return bc_imethodref; return bc_imethodref;
case CONSTANT_Literal: case CONSTANT_InvokeDynamic:
return bc_indyref;
case CONSTANT_LoadableValue:
switch (bc) { switch (bc) {
case _ildc: case _ildc_w: case _ildc: case _ildc_w:
return bc_intref; return bc_intref;
...@@ -2333,10 +2353,12 @@ class BandStructure { ...@@ -2333,10 +2353,12 @@ class BandStructure {
return bc_longref; return bc_longref;
case _dldc2_w: case _dldc2_w:
return bc_doubleref; return bc_doubleref;
case _aldc: case _aldc_w: case _sldc: case _sldc_w:
return bc_stringref; return bc_stringref;
case _cldc: case _cldc_w: case _cldc: case _cldc_w:
return bc_classref; return bc_classref;
case _qldc: case _qldc_w:
return bc_loadablevalueref;
} }
break; break;
} }
...@@ -2623,15 +2645,23 @@ class BandStructure { ...@@ -2623,15 +2645,23 @@ class BandStructure {
} }
static void printArrayTo(PrintStream ps, Entry[] cpMap, int start, int end) { 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(); StringBuffer buf = new StringBuffer();
int len = end-start; int len = end-start;
for (int i = 0; i < len; i++) { 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); buf.setLength(0);
for (int j = 0; j < s.length(); j++) { for (int j = 0; j < s.length(); j++) {
char ch = s.charAt(j); char ch = s.charAt(j);
if (!(ch < ' ' || ch > '~' || ch == '\\')) { if (!(ch < ' ' || ch > '~' || ch == '\\')) {
buf.append(ch); buf.append(ch);
} else if (ch == '\\') {
buf.append("\\\\");
} else if (ch == '\n') { } else if (ch == '\n') {
buf.append("\\n"); buf.append("\\n");
} else if (ch == '\t') { } else if (ch == '\t') {
...@@ -2639,7 +2669,8 @@ class BandStructure { ...@@ -2639,7 +2669,8 @@ class BandStructure {
} else if (ch == '\r') { } else if (ch == '\r') {
buf.append("\\r"); buf.append("\\r");
} else { } else {
buf.append("\\x"+Integer.toHexString(ch)); String str = "000"+Integer.toHexString(ch);
buf.append("\\u"+str.substring(str.length()-4));
} }
} }
ps.println(buf); ps.println(buf);
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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; ...@@ -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.DescriptorEntry;
import com.sun.java.util.jar.pack.ConstantPool.Entry; 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.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.ConstantPool.Utf8Entry;
import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass; import com.sun.java.util.jar.pack.Package.InnerClass;
...@@ -37,6 +40,7 @@ import java.io.FilterInputStream; ...@@ -37,6 +40,7 @@ import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import static com.sun.java.util.jar.pack.Constants.*; import static com.sun.java.util.jar.pack.Constants.*;
...@@ -114,6 +118,7 @@ class ClassReader { ...@@ -114,6 +118,7 @@ class ClassReader {
private Entry readRef(byte tag) throws IOException { private Entry readRef(byte tag) throws IOException {
Entry e = readRef(); Entry e = readRef();
assert(e != null); assert(e != null);
assert(!(e instanceof UnresolvedEntry));
assert(e.tagMatches(tag)); assert(e.tagMatches(tag));
return e; return e;
} }
...@@ -151,6 +156,7 @@ class ClassReader { ...@@ -151,6 +156,7 @@ class ClassReader {
readMembers(false); // fields readMembers(false); // fields
readMembers(true); // methods readMembers(true); // methods
readAttributes(ATTR_CONTEXT_CLASS, cls); readAttributes(ATTR_CONTEXT_CLASS, cls);
fixUnresolvedEntries();
cls.finishReading(); cls.finishReading();
assert(0 >= in.read(new byte[1])); assert(0 >= in.read(new byte[1]));
ok = true; ok = true;
...@@ -236,6 +242,7 @@ class ClassReader { ...@@ -236,6 +242,7 @@ class ClassReader {
// just read the refs; do not attempt to resolve while reading // just read the refs; do not attempt to resolve while reading
case CONSTANT_Class: case CONSTANT_Class:
case CONSTANT_String: case CONSTANT_String:
case CONSTANT_MethodType:
fixups[fptr++] = i; fixups[fptr++] = i;
fixups[fptr++] = tag; fixups[fptr++] = tag;
fixups[fptr++] = in.readUnsignedShort(); fixups[fptr++] = in.readUnsignedShort();
...@@ -250,6 +257,18 @@ class ClassReader { ...@@ -250,6 +257,18 @@ class ClassReader {
fixups[fptr++] = in.readUnsignedShort(); fixups[fptr++] = in.readUnsignedShort();
fixups[fptr++] = in.readUnsignedShort(); fixups[fptr++] = in.readUnsignedShort();
break; 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: default:
throw new ClassFormatException("Bad constant pool tag " + throw new ClassFormatException("Bad constant pool tag " +
tag + " in File: " + cls.file.nameString + tag + " in File: " + cls.file.nameString +
...@@ -270,7 +289,7 @@ class ClassReader { ...@@ -270,7 +289,7 @@ class ClassReader {
int ref2 = fixups[fi++]; int ref2 = fixups[fi++];
if (verbose > 3) if (verbose > 3)
Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}"); 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. // Defer.
fixups[fptr++] = cpi; fixups[fptr++] = cpi;
fixups[fptr++] = tag; fixups[fptr++] = tag;
...@@ -297,6 +316,19 @@ class ClassReader { ...@@ -297,6 +316,19 @@ class ClassReader {
Utf8Entry mtype = (Utf8Entry) cpMap[ref2]; Utf8Entry mtype = (Utf8Entry) cpMap[ref2];
cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype); cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype);
break; 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: default:
assert(false); assert(false);
} }
...@@ -307,6 +339,50 @@ class ClassReader { ...@@ -307,6 +339,50 @@ class ClassReader {
cls.cpMap = cpMap; 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 { void readHeader() throws IOException {
cls.flags = readUnsignedShort(); cls.flags = readUnsignedShort();
cls.thisClass = readClassRef(); cls.thisClass = readClassRef();
...@@ -416,25 +492,31 @@ class ClassReader { ...@@ -416,25 +492,31 @@ class ClassReader {
unknownAttrCommand); unknownAttrCommand);
} }
} }
if (a.layout() == Package.attrCodeEmpty || long pos0 = inPos; // in case we want to check it
a.layout() == Package.attrInnerClassesEmpty) { if (a.layout() == Package.attrCodeEmpty) {
// These are hardwired. // These are hardwired.
long pos0 = inPos; Class.Method m = (Class.Method) h;
if ("Code".equals(a.name())) { m.code = new Code(m);
Class.Method m = (Class.Method) h; try {
m.code = new Code(m); readCode(m.code);
try { } catch (Instruction.FormatException iie) {
readCode(m.code); String message = iie.getMessage() + " in " + h;
} catch (Instruction.FormatException iie) { throw new ClassReader.ClassFormatException(message, iie);
String message = iie.getMessage() + " in " + h;
throw new ClassReader.ClassFormatException(message, iie);
}
} else {
assert(h == cls);
readInnerClasses(cls);
} }
assert(length == inPos - pos0); assert(length == inPos - pos0);
// Keep empty attribute a... // 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) { } else if (length > 0) {
byte[] bytes = new byte[length]; byte[] bytes = new byte[length];
in.readFully(bytes); in.readFully(bytes);
...@@ -467,6 +549,19 @@ class ClassReader { ...@@ -467,6 +549,19 @@ class ClassReader {
readAttributes(ATTR_CONTEXT_CODE, code); 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 { void readInnerClasses(Class cls) throws IOException {
int nc = readUnsignedShort(); int nc = readUnsignedShort();
ArrayList<InnerClass> ics = new ArrayList<>(nc); ArrayList<InnerClass> ics = new ArrayList<>(nc);
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,8 @@ package com.sun.java.util.jar.pack; ...@@ -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.Entry;
import com.sun.java.util.jar.pack.ConstantPool.Index; 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.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.Class;
import com.sun.java.util.jar.pack.Package.InnerClass; import com.sun.java.util.jar.pack.Package.InnerClass;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
...@@ -49,6 +51,7 @@ class ClassWriter { ...@@ -49,6 +51,7 @@ class ClassWriter {
Class cls; Class cls;
DataOutputStream out; DataOutputStream out;
Index cpIndex; Index cpIndex;
Index bsmIndex;
ClassWriter(Class cls, OutputStream out) throws IOException { ClassWriter(Class cls, OutputStream out) throws IOException {
this.pkg = cls.getPackage(); this.pkg = cls.getPackage();
...@@ -57,6 +60,10 @@ class ClassWriter { ...@@ -57,6 +60,10 @@ class ClassWriter {
this.out = new DataOutputStream(new BufferedOutputStream(out)); this.out = new DataOutputStream(new BufferedOutputStream(out));
this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap()); this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap());
this.cpIndex.flattenSigs = true; this.cpIndex.flattenSigs = true;
if (cls.hasBootstrapMethods()) {
this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods",
cls.getBootstrapMethodMap());
}
if (verbose > 1) if (verbose > 1)
Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString())); Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString()));
} }
...@@ -71,6 +78,11 @@ class ClassWriter { ...@@ -71,6 +78,11 @@ class ClassWriter {
/** Write a 2-byte int representing a CP entry, using the local cpIndex. */ /** Write a 2-byte int representing a CP entry, using the local cpIndex. */
private void writeRef(Entry e) throws IOException { 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); int i = (e == null) ? 0 : cpIndex.indexOf(e);
writeShort(i); writeShort(i);
} }
...@@ -117,8 +129,7 @@ class ClassWriter { ...@@ -117,8 +129,7 @@ class ClassWriter {
out.write(tag); out.write(tag);
switch (tag) { switch (tag) {
case CONSTANT_Signature: case CONSTANT_Signature:
assert(false); // should not reach here throw new AssertionError("CP should have Signatures remapped to Utf8");
break;
case CONSTANT_Utf8: case CONSTANT_Utf8:
out.writeUTF(e.stringValue()); out.writeUTF(e.stringValue());
break; break;
...@@ -138,8 +149,14 @@ class ClassWriter { ...@@ -138,8 +149,14 @@ class ClassWriter {
break; break;
case CONSTANT_Class: case CONSTANT_Class:
case CONSTANT_String: case CONSTANT_String:
case CONSTANT_MethodType:
writeRef(e.getRef(0)); writeRef(e.getRef(0));
break; break;
case CONSTANT_MethodHandle:
MethodHandleEntry mhe = (MethodHandleEntry) e;
out.writeByte(mhe.refKind);
writeRef(mhe.getRef(0));
break;
case CONSTANT_Fieldref: case CONSTANT_Fieldref:
case CONSTANT_Methodref: case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref: case CONSTANT_InterfaceMethodref:
...@@ -147,6 +164,12 @@ class ClassWriter { ...@@ -147,6 +164,12 @@ class ClassWriter {
writeRef(e.getRef(0)); writeRef(e.getRef(0));
writeRef(e.getRef(1)); writeRef(e.getRef(1));
break; 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: default:
throw new IOException("Bad constant pool tag "+tag); throw new IOException("Bad constant pool tag "+tag);
} }
...@@ -198,6 +221,7 @@ class ClassWriter { ...@@ -198,6 +221,7 @@ class ClassWriter {
a.finishRefs(cpIndex); a.finishRefs(cpIndex);
writeRef(a.getNameRef()); writeRef(a.getNameRef());
if (a.layout() == Package.attrCodeEmpty || if (a.layout() == Package.attrCodeEmpty ||
a.layout() == Package.attrBootstrapMethodsEmpty ||
a.layout() == Package.attrInnerClassesEmpty) { a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired. // These are hardwired.
DataOutputStream savedOut = out; DataOutputStream savedOut = out;
...@@ -207,9 +231,14 @@ class ClassWriter { ...@@ -207,9 +231,14 @@ class ClassWriter {
if ("Code".equals(a.name())) { if ("Code".equals(a.name())) {
Class.Method m = (Class.Method) h; Class.Method m = (Class.Method) h;
writeCode(m.code); writeCode(m.code);
} else { } else if ("BootstrapMethods".equals(a.name())) {
assert(h == cls);
writeBootstrapMethods(cls);
} else if ("InnerClasses".equals(a.name())) {
assert(h == cls); assert(h == cls);
writeInnerClasses(cls); writeInnerClasses(cls);
} else {
throw new AssertionError();
} }
out = savedOut; out = savedOut;
if (verbose > 2) if (verbose > 2)
...@@ -242,6 +271,18 @@ class ClassWriter { ...@@ -242,6 +271,18 @@ class ClassWriter {
writeAttributes(ATTR_CONTEXT_CODE, code); writeAttributes(ATTR_CONTEXT_CODE, code);
} }
void writeBootstrapMethods(Class cls) throws IOException {
List<BootstrapMethodEntry> 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 { void writeInnerClasses(Class cls) throws IOException {
List<InnerClass> ics = cls.getInnerClasses(); List<InnerClass> ics = cls.getInnerClasses();
writeShort(ics.size()); writeShort(ics.size());
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -52,7 +52,7 @@ class ConstantPool { ...@@ -52,7 +52,7 @@ class ConstantPool {
* Also used to back up more complex constant pool entries, like Class. * Also used to back up more complex constant pool entries, like Class.
*/ */
public static synchronized Utf8Entry getUtf8Entry(String value) { public static synchronized Utf8Entry getUtf8Entry(String value) {
Map<String, Utf8Entry> utf8Entries = Utils.getUtf8Entries(); Map<String, Utf8Entry> utf8Entries = Utils.getTLGlobals().getUtf8Entries();
Utf8Entry e = utf8Entries.get(value); Utf8Entry e = utf8Entries.get(value);
if (e == null) { if (e == null) {
e = new Utf8Entry(value); e = new Utf8Entry(value);
...@@ -61,8 +61,8 @@ class ConstantPool { ...@@ -61,8 +61,8 @@ class ConstantPool {
return e; return e;
} }
/** Factory for Class constants. */ /** Factory for Class constants. */
public static synchronized ClassEntry getClassEntry(String name) { public static ClassEntry getClassEntry(String name) {
Map<String, ClassEntry> classEntries = Utils.getClassEntries(); Map<String, ClassEntry> classEntries = Utils.getTLGlobals().getClassEntries();
ClassEntry e = classEntries.get(name); ClassEntry e = classEntries.get(name);
if (e == null) { if (e == null) {
e = new ClassEntry(getUtf8Entry(name)); e = new ClassEntry(getUtf8Entry(name));
...@@ -72,8 +72,8 @@ class ConstantPool { ...@@ -72,8 +72,8 @@ class ConstantPool {
return e; return e;
} }
/** Factory for literal constants (String, Integer, etc.). */ /** Factory for literal constants (String, Integer, etc.). */
public static synchronized LiteralEntry getLiteralEntry(Comparable<?> value) { public static LiteralEntry getLiteralEntry(Comparable<?> value) {
Map<Object, LiteralEntry> literalEntries = Utils.getLiteralEntries(); Map<Object, LiteralEntry> literalEntries = Utils.getTLGlobals().getLiteralEntries();
LiteralEntry e = literalEntries.get(value); LiteralEntry e = literalEntries.get(value);
if (e == null) { if (e == null) {
if (value instanceof String) if (value instanceof String)
...@@ -85,13 +85,13 @@ class ConstantPool { ...@@ -85,13 +85,13 @@ class ConstantPool {
return e; return e;
} }
/** Factory for literal constants (String, Integer, etc.). */ /** Factory for literal constants (String, Integer, etc.). */
public static synchronized StringEntry getStringEntry(String value) { public static StringEntry getStringEntry(String value) {
return (StringEntry) getLiteralEntry(value); return (StringEntry) getLiteralEntry(value);
} }
/** Factory for signature (type) constants. */ /** Factory for signature (type) constants. */
public static synchronized SignatureEntry getSignatureEntry(String type) { public static SignatureEntry getSignatureEntry(String type) {
Map<String, SignatureEntry> signatureEntries = Utils.getSignatureEntries(); Map<String, SignatureEntry> signatureEntries = Utils.getTLGlobals().getSignatureEntries();
SignatureEntry e = signatureEntries.get(type); SignatureEntry e = signatureEntries.get(type);
if (e == null) { if (e == null) {
e = new SignatureEntry(type); e = new SignatureEntry(type);
...@@ -106,8 +106,8 @@ class ConstantPool { ...@@ -106,8 +106,8 @@ class ConstantPool {
} }
/** Factory for descriptor (name-and-type) constants. */ /** Factory for descriptor (name-and-type) constants. */
public static synchronized DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) { public static DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) {
Map<String, DescriptorEntry> descriptorEntries = Utils.getDescriptorEntries(); Map<String, DescriptorEntry> descriptorEntries = Utils.getTLGlobals().getDescriptorEntries();
String key = DescriptorEntry.stringValueOf(nameRef, typeRef); String key = DescriptorEntry.stringValueOf(nameRef, typeRef);
DescriptorEntry e = descriptorEntries.get(key); DescriptorEntry e = descriptorEntries.get(key);
if (e == null) { if (e == null) {
...@@ -124,8 +124,8 @@ class ConstantPool { ...@@ -124,8 +124,8 @@ class ConstantPool {
} }
/** Factory for member reference constants. */ /** Factory for member reference constants. */
public static synchronized MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) { public static MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) {
Map<String, MemberEntry> memberEntries = Utils.getMemberEntries(); Map<String, MemberEntry> memberEntries = Utils.getTLGlobals().getMemberEntries();
String key = MemberEntry.stringValueOf(tag, classRef, descRef); String key = MemberEntry.stringValueOf(tag, classRef, descRef);
MemberEntry e = memberEntries.get(key); MemberEntry e = memberEntries.get(key);
if (e == null) { if (e == null) {
...@@ -137,6 +137,61 @@ class ConstantPool { ...@@ -137,6 +137,61 @@ class ConstantPool {
return e; return e;
} }
/** Factory for MethodHandle constants. */
public static MethodHandleEntry getMethodHandleEntry(byte refKind, MemberEntry memRef) {
Map<String, MethodHandleEntry> 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<String, MethodTypeEntry> 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<String, InvokeDynamicEntry> 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<String, BootstrapMethodEntry> 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. */ /** Entries in the constant pool. */
public static abstract public static abstract
...@@ -251,6 +306,10 @@ class ConstantPool { ...@@ -251,6 +306,10 @@ class ConstantPool {
throw new RuntimeException("bad literal value "+value); throw new RuntimeException("bad literal value "+value);
} }
static boolean isRefKind(byte refKind) {
return (REF_getField <= refKind && refKind <= REF_invokeInterface);
}
public static abstract public static abstract
class LiteralEntry extends Entry { class LiteralEntry extends Entry {
protected LiteralEntry(byte tag) { protected LiteralEntry(byte tag) {
...@@ -404,7 +463,7 @@ class ConstantPool { ...@@ -404,7 +463,7 @@ class ConstantPool {
} }
static static
String stringValueOf(Entry nameRef, Entry typeRef) { String stringValueOf(Entry nameRef, Entry typeRef) {
return typeRef.stringValue()+","+nameRef.stringValue(); return qualifiedStringValue(typeRef, nameRef);
} }
public String prettyString() { public String prettyString() {
...@@ -420,6 +479,15 @@ class ConstantPool { ...@@ -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 public static
class MemberEntry extends Entry { class MemberEntry extends Entry {
final ClassEntry classRef; final ClassEntry classRef;
...@@ -453,8 +521,12 @@ class ConstantPool { ...@@ -453,8 +521,12 @@ class ConstantPool {
int x = superCompareTo(o); int x = superCompareTo(o);
if (x == 0) { if (x == 0) {
MemberEntry that = (MemberEntry)o; 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. // Primary key is classRef.
x = this.classRef.compareTo(that.classRef); if (x == 0)
x = this.classRef.compareTo(that.classRef);
if (x == 0) if (x == 0)
x = this.descRef.compareTo(that.descRef); x = this.descRef.compareTo(that.descRef);
} }
...@@ -473,7 +545,7 @@ class ConstantPool { ...@@ -473,7 +545,7 @@ class ConstantPool {
case CONSTANT_InterfaceMethodref: pfx = "IMethod:"; break; case CONSTANT_InterfaceMethodref: pfx = "IMethod:"; break;
default: pfx = tag+"???"; break; default: pfx = tag+"???"; break;
} }
return pfx+classRef.stringValue()+","+descRef.stringValue(); return pfx+qualifiedStringValue(classRef, descRef);
} }
public boolean isMethod() { public boolean isMethod() {
...@@ -581,13 +653,26 @@ class ConstantPool { ...@@ -581,13 +653,26 @@ class ConstantPool {
} }
public byte getLiteralTag() { public byte getLiteralTag() {
switch (formRef.stringValue().charAt(0)) { switch (formRef.stringValue().charAt(0)) {
case 'L': return CONSTANT_String;
case 'I': return CONSTANT_Integer; case 'I': return CONSTANT_Integer;
case 'J': return CONSTANT_Long; case 'J': return CONSTANT_Long;
case 'F': return CONSTANT_Float; case 'F': return CONSTANT_Float;
case 'D': return CONSTANT_Double; case 'D': return CONSTANT_Double;
case 'B': case 'S': case 'C': case 'Z': case 'B': case 'S': case 'C': case 'Z':
return CONSTANT_Integer; 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); assert(false);
return CONSTANT_None; return CONSTANT_None;
...@@ -724,6 +809,218 @@ class ConstantPool { ...@@ -724,6 +809,218 @@ class ConstantPool {
return parts; 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 "<foo;bar;baz>" 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: // Handy constants:
protected static final Entry[] noRefs = {}; protected static final Entry[] noRefs = {};
protected static final ClassEntry[] noClassRefs = {}; protected static final ClassEntry[] noClassRefs = {};
...@@ -964,35 +1261,51 @@ class ConstantPool { ...@@ -964,35 +1261,51 @@ class ConstantPool {
/** Coherent group of constant pool indexes. */ /** Coherent group of constant pool indexes. */
public static public static
class IndexGroup { class IndexGroup {
private Index indexUntyped;
private Index[] indexByTag = new Index[CONSTANT_Limit]; private Index[] indexByTag = new Index[CONSTANT_Limit];
private Index[] indexByTagGroup;
private int[] untypedFirstIndexByTag; private int[] untypedFirstIndexByTag;
private int totalSize; private int totalSizeQQ;
private Index[][] indexByTagAndClass; private Index[][] indexByTagAndClass;
/** Index of all CP entries of all types, in definition order. */ /** Index of all CP entries of all types, in definition order. */
public Index getUntypedIndex() { private Index makeTagGroupIndex(byte tagGroupTag, byte[] tagsInGroup) {
if (indexUntyped == null) { 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 untypedIndexOf(null); // warm up untypedFirstIndexByTag
Entry[] cpMap = new Entry[totalSize]; for (byte tag : tagsInGroup) {
for (int tag = 0; tag < indexByTag.length; tag++) {
Index ix = indexByTag[tag]; Index ix = indexByTag[tag];
if (ix == null) continue; if (ix == null) continue;
int ixLen = ix.cpMap.length; int ixLen = ix.cpMap.length;
if (ixLen == 0) continue; if (ixLen == 0) continue;
int fillp = untypedFirstIndexByTag[tag]; assert(tagGroupTag == CONSTANT_All
assert(cpMap[fillp] == null); ? fillp == untypedFirstIndexByTag[tag]
assert(cpMap[fillp+ixLen-1] == null); : fillp < untypedFirstIndexByTag[tag]);
System.arraycopy(ix.cpMap, 0, cpMap, fillp, ixLen); 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) { public int untypedIndexOf(Entry e) {
if (untypedFirstIndexByTag == null) { if (untypedFirstIndexByTag == null) {
untypedFirstIndexByTag = new int[CONSTANT_Limit]; untypedFirstIndexByTag = new int[CONSTANT_Limit+1];
int fillp = 0; int fillp = 0;
for (int i = 0; i < TAGS_IN_ORDER.length; i++) { for (int i = 0; i < TAGS_IN_ORDER.length; i++) {
byte tag = TAGS_IN_ORDER[i]; byte tag = TAGS_IN_ORDER[i];
...@@ -1002,7 +1315,7 @@ class ConstantPool { ...@@ -1002,7 +1315,7 @@ class ConstantPool {
untypedFirstIndexByTag[tag] = fillp; untypedFirstIndexByTag[tag] = fillp;
fillp += ixLen; fillp += ixLen;
} }
totalSize = fillp; untypedFirstIndexByTag[CONSTANT_Limit] = fillp;
} }
if (e == null) return -1; if (e == null) return -1;
int tag = e.tag; int tag = e.tag;
...@@ -1028,16 +1341,15 @@ class ConstantPool { ...@@ -1028,16 +1341,15 @@ class ConstantPool {
indexByTag[tag] = ix; indexByTag[tag] = ix;
// decache indexes derived from this one: // decache indexes derived from this one:
untypedFirstIndexByTag = null; untypedFirstIndexByTag = null;
indexUntyped = null; indexByTagGroup = null;
if (indexByTagAndClass != null) if (indexByTagAndClass != null)
indexByTagAndClass[tag] = null; indexByTagAndClass[tag] = null;
} }
/** Index of all CP entries of a given tag. */ /** Index of all CP entries of a given tag. */
public Index getIndexByTag(byte tag) { public Index getIndexByTag(byte tag) {
if (tag == CONSTANT_All) { if (tag >= CONSTANT_GroupFirst)
return getUntypedIndex(); return getIndexByTagGroup(tag);
}
Index ix = indexByTag[tag]; Index ix = indexByTag[tag];
if (ix == null) { if (ix == null) {
// Make an empty one by default. // Make an empty one by default.
...@@ -1047,6 +1359,26 @@ class ConstantPool { ...@@ -1047,6 +1359,26 @@ class ConstantPool {
return ix; 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. */ /** Index of all CP entries of a given tag and class. */
public Index getMemberIndex(byte tag, ClassEntry classRef) { public Index getMemberIndex(byte tag, ClassEntry classRef) {
if (indexByTagAndClass == null) if (indexByTagAndClass == null)
...@@ -1107,16 +1439,14 @@ class ConstantPool { ...@@ -1107,16 +1439,14 @@ class ConstantPool {
} }
public boolean haveNumbers() { public boolean haveNumbers() {
for (byte tag = CONSTANT_Integer; tag <= CONSTANT_Double; tag++) { for (byte tag : NUMBER_TAGS) {
switch (tag) { if (getIndexByTag(tag).size() > 0) return true;
case CONSTANT_Integer: }
case CONSTANT_Float: return false;
case CONSTANT_Long: }
case CONSTANT_Double:
break; public boolean haveExtraTags() {
default: for (byte tag : EXTRA_TAGS) {
assert(false);
}
if (getIndexByTag(tag).size() > 0) return true; if (getIndexByTag(tag).size() > 0) return true;
} }
return false; return false;
...@@ -1129,8 +1459,13 @@ class ConstantPool { ...@@ -1129,8 +1459,13 @@ class ConstantPool {
* by their equivalent Utf8s. * by their equivalent Utf8s.
* Also, discard null from cpRefs. * Also, discard null from cpRefs.
*/ */
public static void completeReferencesIn(Set<Entry> cpRefs, boolean flattenSigs) {
completeReferencesIn(cpRefs, flattenSigs, null);
}
public static public static
void completeReferencesIn(Set<Entry> cpRefs, boolean flattenSigs) { void completeReferencesIn(Set<Entry> cpRefs, boolean flattenSigs,
List<BootstrapMethodEntry>bsms) {
cpRefs.remove(null); cpRefs.remove(null);
for (ListIterator<Entry> work = for (ListIterator<Entry> work =
new ArrayList<>(cpRefs).listIterator(cpRefs.size()); new ArrayList<>(cpRefs).listIterator(cpRefs.size());
...@@ -1146,6 +1481,14 @@ class ConstantPool { ...@@ -1146,6 +1481,14 @@ class ConstantPool {
cpRefs.add(ue); cpRefs.add(ue);
e = ue; // do not descend into the sig 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: // Recursively add the refs of e to cpRefs:
for (int i = 0; ; i++) { for (int i = 0; ; i++) {
Entry re = e.getRef(i); Entry re = e.getRef(i);
...@@ -1174,15 +1517,37 @@ class ConstantPool { ...@@ -1174,15 +1517,37 @@ class ConstantPool {
case CONSTANT_Methodref: return "Methodref"; case CONSTANT_Methodref: return "Methodref";
case CONSTANT_InterfaceMethodref: return "InterfaceMethodref"; case CONSTANT_InterfaceMethodref: return "InterfaceMethodref";
case CONSTANT_NameandType: return "NameandType"; case CONSTANT_NameandType: return "NameandType";
case CONSTANT_MethodHandle: return "MethodHandle";
case CONSTANT_MethodType: return "MethodType";
case CONSTANT_InvokeDynamic: return "InvokeDynamic";
// pseudo-tags: // pseudo-tags:
case CONSTANT_All: return "*All"; case CONSTANT_All: return "**All";
case CONSTANT_None: return "*None"; 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_Signature: return "*Signature";
case CONSTANT_BootstrapMethod: return "*BootstrapMethod";
} }
return "tag#"+tag; 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 // archive constant pool definition order
static final byte TAGS_IN_ORDER[] = { static final byte TAGS_IN_ORDER[] = {
CONSTANT_Utf8, CONSTANT_Utf8,
...@@ -1190,13 +1555,19 @@ class ConstantPool { ...@@ -1190,13 +1555,19 @@ class ConstantPool {
CONSTANT_Float, CONSTANT_Float,
CONSTANT_Long, CONSTANT_Long,
CONSTANT_Double, CONSTANT_Double,
CONSTANT_String, CONSTANT_String, // note that String=8 precedes Class=7
CONSTANT_Class, CONSTANT_Class,
CONSTANT_Signature, CONSTANT_Signature,
CONSTANT_NameandType, // cp_Descr CONSTANT_NameandType, // cp_Descr
CONSTANT_Fieldref, // cp_Field CONSTANT_Fieldref, // cp_Field
CONSTANT_Methodref, // cp_Method 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 final byte TAG_ORDER[];
static { static {
...@@ -1211,4 +1582,45 @@ class ConstantPool { ...@@ -1211,4 +1582,45 @@ class ConstantPool {
System.out.println("};"); 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;
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -65,6 +65,9 @@ class Constants { ...@@ -65,6 +65,9 @@ class Constants {
public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160; public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160;
public final static int JAVA6_PACKAGE_MINOR_VERSION = 1; 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_INDEX_LIMIT = 0x10000;
public final static int CONSTANT_POOL_NARROW_LIMIT = 0x00100; public final static int CONSTANT_POOL_NARROW_LIMIT = 0x00100;
...@@ -82,14 +85,36 @@ class Constants { ...@@ -82,14 +85,36 @@ class Constants {
public final static byte CONSTANT_Methodref = 10; public final static byte CONSTANT_Methodref = 10;
public final static byte CONSTANT_InterfaceMethodref = 11; public final static byte CONSTANT_InterfaceMethodref = 11;
public final static byte CONSTANT_NameandType = 12; 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: // pseudo-constants:
public final static byte CONSTANT_None = 0; public final static byte CONSTANT_None = 0;
public final static byte CONSTANT_Signature = 13; public final static byte CONSTANT_Signature = CONSTANT_unused13;
public final static byte CONSTANT_Limit = 14; 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 = 19; // combined global map
public final static byte CONSTANT_Literal = 20; // used only for ldc fields 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 // pseudo-access bits
public final static int ACC_IC_LONG_FORM = (1<<16); //for ic_flags public final static int ACC_IC_LONG_FORM = (1<<16); //for ic_flags
...@@ -133,7 +158,7 @@ class Constants { ...@@ -133,7 +158,7 @@ class Constants {
public static final int AO_HAVE_SPECIAL_FORMATS = 1<<0; 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_CP_NUMBERS = 1<<1;
public static final int AO_HAVE_ALL_CODE_FLAGS = 1<<2; 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_HAVE_FILE_HEADERS = 1<<4;
public static final int AO_DEFLATE_HINT = 1<<5; public static final int AO_DEFLATE_HINT = 1<<5;
public static final int AO_HAVE_FILE_MODTIME = 1<<6; public static final int AO_HAVE_FILE_MODTIME = 1<<6;
...@@ -143,6 +168,7 @@ class Constants { ...@@ -143,6 +168,7 @@ class Constants {
public static final int AO_HAVE_FIELD_FLAGS_HI = 1<<10; 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_METHOD_FLAGS_HI = 1<<11;
public static final int AO_HAVE_CODE_FLAGS_HI = 1<<12; 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; public static final int LG_AO_HAVE_XXX_FLAGS_HI = 9;
...@@ -357,7 +383,7 @@ class Constants { ...@@ -357,7 +383,7 @@ class Constants {
_invokespecial = 183, // 0xb7 _invokespecial = 183, // 0xb7
_invokestatic = 184, // 0xb8 _invokestatic = 184, // 0xb8
_invokeinterface = 185, // 0xb9 _invokeinterface = 185, // 0xb9
_xxxunusedxxx = 186, // 0xba _invokedynamic = 186, // 0xba
_new = 187, // 0xbb _new = 187, // 0xbb
_newarray = 188, // 0xbc _newarray = 188, // 0xbc
_anewarray = 189, // 0xbd _anewarray = 189, // 0xbd
...@@ -422,15 +448,18 @@ class Constants { ...@@ -422,15 +448,18 @@ class Constants {
// Ldc variants gain us only 0.007% improvement in compression ratio, // Ldc variants gain us only 0.007% improvement in compression ratio,
// but they simplify the file format greatly. // but they simplify the file format greatly.
public final static int _xldc_op = _invokeinit_limit; 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 _cldc = _xldc_op+0;
public final static int _ildc = _xldc_op+1; public final static int _ildc = _xldc_op+1;
public final static int _fldc = _xldc_op+2; 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 _cldc_w = _xldc_op+3;
public final static int _ildc_w = _xldc_op+4; public final static int _ildc_w = _xldc_op+4;
public final static int _fldc_w = _xldc_op+5; public final static int _fldc_w = _xldc_op+5;
public final static int _lldc2_w = _ldc2_w; public final static int _lldc2_w = _ldc2_w;
public final static int _dldc2_w = _xldc_op+6; 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;
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -451,7 +451,7 @@ class Instruction { ...@@ -451,7 +451,7 @@ class Instruction {
public static byte getCPRefOpTag(int bc) { public static byte getCPRefOpTag(int bc) {
if (bc < BC_INDEX[0].length && BC_INDEX[0][bc] > 0) return BC_TAG[0][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; return CONSTANT_None;
} }
...@@ -500,7 +500,7 @@ class Instruction { ...@@ -500,7 +500,7 @@ class Instruction {
def("bkf", _getstatic, _putfield); // pack kf (base=Field) def("bkf", _getstatic, _putfield); // pack kf (base=Field)
def("bkm", _invokevirtual, _invokestatic); // pack kn (base=Method) def("bkm", _invokevirtual, _invokestatic); // pack kn (base=Method)
def("bkixx", _invokeinterface); // pack ki (base=IMethod), omit xx 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("bkc", _new); // pack kc
def("bx", _newarray); def("bx", _newarray);
def("bkc", _anewarray); // pack kc def("bkc", _anewarray); // pack kc
...@@ -515,7 +515,6 @@ class Instruction { ...@@ -515,7 +515,6 @@ class Instruction {
//System.out.println(i+": l="+BC_LENGTH[0][i]+" i="+BC_INDEX[0][i]); //System.out.println(i+": l="+BC_LENGTH[0][i]+" i="+BC_INDEX[0][i]);
//assert(BC_LENGTH[0][i] != -1); //assert(BC_LENGTH[0][i] != -1);
if (BC_LENGTH[0][i] == -1) { if (BC_LENGTH[0][i] == -1) {
assert(i == _xxxunusedxxx);
continue; // unknown opcode continue; // unknown opcode
} }
...@@ -543,7 +542,7 @@ class Instruction { ...@@ -543,7 +542,7 @@ class Instruction {
"if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple if_acmpeq if_acmpne "+ "if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple if_acmpeq if_acmpne "+
"goto jsr ret tableswitch lookupswitch ireturn lreturn freturn dreturn "+ "goto jsr ret tableswitch lookupswitch ireturn lreturn freturn dreturn "+
"areturn return getstatic putstatic getfield putfield invokevirtual "+ "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 "+ "anewarray arraylength athrow checkcast instanceof monitorenter "+
"monitorexit wide multianewarray ifnull ifnonnull goto_w jsr_w "; "monitorexit wide multianewarray ifnull ifnonnull goto_w jsr_w ";
for (int bc = 0; names.length() > 0; bc++) { for (int bc = 0; names.length() > 0; bc++) {
...@@ -588,6 +587,8 @@ class Instruction { ...@@ -588,6 +587,8 @@ class Instruction {
case _dldc2_w: iname = "*dldc2_w"; break; case _dldc2_w: iname = "*dldc2_w"; break;
case _cldc: iname = "*cldc"; break; case _cldc: iname = "*cldc"; break;
case _cldc_w: iname = "*cldc_w"; 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 _byte_escape: iname = "*byte_escape"; break;
case _ref_escape: iname = "*ref_escape"; break; case _ref_escape: iname = "*ref_escape"; break;
case _end_marker: iname = "*end"; break; case _end_marker: iname = "*end"; break;
...@@ -618,15 +619,16 @@ class Instruction { ...@@ -618,15 +619,16 @@ class Instruction {
if (index > 0 && index+1 < length) { if (index > 0 && index+1 < length) {
switch (fmt.charAt(index+1)) { switch (fmt.charAt(index+1)) {
case 'c': tag = CONSTANT_Class; break; 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 'f': tag = CONSTANT_Fieldref; break;
case 'm': tag = CONSTANT_Methodref; break; case 'm': tag = CONSTANT_Methodref; break;
case 'i': tag = CONSTANT_InterfaceMethodref; break; case 'i': tag = CONSTANT_InterfaceMethodref; break;
case 'y': tag = CONSTANT_InvokeDynamic; break;
} }
assert(tag != CONSTANT_None); assert(tag != CONSTANT_None);
} else if (index > 0 && length == 2) { } else if (index > 0 && length == 2) {
assert(from_bc == _ldc); 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++) { for (int bc = from_bc; bc <= to_bc; bc++) {
BC_FORMAT[w][bc] = fmt; BC_FORMAT[w][bc] = fmt;
...@@ -649,7 +651,7 @@ class Instruction { ...@@ -649,7 +651,7 @@ class Instruction {
Instruction i = at(code, 0); Instruction i = at(code, 0);
while (i != null) { while (i != null) {
int opcode = i.getBC(); int opcode = i.getBC();
if (opcode == _xxxunusedxxx || opcode < _nop || opcode > _jsr_w) { if (opcode < _nop || opcode > _jsr_w) {
String message = "illegal opcode: " + opcode + " " + i; String message = "illegal opcode: " + opcode + " " + i;
throw new FormatException(message); throw new FormatException(message);
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -28,6 +28,7 @@ package com.sun.java.util.jar.pack; ...@@ -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.Attribute.Layout;
import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; 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.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.Index;
import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry; import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry;
import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
...@@ -100,6 +101,8 @@ class Package { ...@@ -100,6 +101,8 @@ class Package {
classes.clear(); classes.clear();
files.clear(); files.clear();
BandStructure.nextSeqForDebug = 0; BandStructure.nextSeqForDebug = 0;
package_minver = -1; // fill in later
package_majver = 0; // fill in later
} }
int getPackageVersion() { int getPackageVersion() {
...@@ -108,6 +111,7 @@ class Package { ...@@ -108,6 +111,7 @@ class Package {
// Special empty versions of Code and InnerClasses, used for markers. // Special empty versions of Code and InnerClasses, used for markers.
public static final Attribute.Layout attrCodeEmpty; public static final Attribute.Layout attrCodeEmpty;
public static final Attribute.Layout attrBootstrapMethodsEmpty;
public static final Attribute.Layout attrInnerClassesEmpty; public static final Attribute.Layout attrInnerClassesEmpty;
public static final Attribute.Layout attrSourceFileSpecial; public static final Attribute.Layout attrSourceFileSpecial;
public static final Map<Attribute.Layout, Attribute> attrDefs; public static final Map<Attribute.Layout, Attribute> attrDefs;
...@@ -115,6 +119,8 @@ class Package { ...@@ -115,6 +119,8 @@ class Package {
Map<Layout, Attribute> ad = new HashMap<>(3); Map<Layout, Attribute> ad = new HashMap<>(3);
attrCodeEmpty = Attribute.define(ad, ATTR_CONTEXT_METHOD, attrCodeEmpty = Attribute.define(ad, ATTR_CONTEXT_METHOD,
"Code", "").layout(); "Code", "").layout();
attrBootstrapMethodsEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS,
"BootstrapMethods", "").layout();
attrInnerClassesEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS, attrInnerClassesEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS,
"InnerClasses", "").layout(); "InnerClasses", "").layout();
attrSourceFileSpecial = Attribute.define(ad, ATTR_CONTEXT_CLASS, attrSourceFileSpecial = Attribute.define(ad, ATTR_CONTEXT_CLASS,
...@@ -153,9 +159,8 @@ class Package { ...@@ -153,9 +159,8 @@ class Package {
package_minver = JAVA6_PACKAGE_MINOR_VERSION; package_minver = JAVA6_PACKAGE_MINOR_VERSION;
} else { } else {
// Normal case. Use the newest archive format, when available // Normal case. Use the newest archive format, when available
// TODO: replace the following with JAVA7* when the need arises package_majver = JAVA7_PACKAGE_MAJOR_VERSION;
package_majver = JAVA6_PACKAGE_MAJOR_VERSION; package_minver = JAVA7_PACKAGE_MINOR_VERSION;
package_minver = JAVA6_PACKAGE_MINOR_VERSION;
} }
} }
...@@ -168,13 +173,22 @@ class Package { ...@@ -168,13 +173,22 @@ class Package {
String expMag = Integer.toHexString(JAVA_PACKAGE_MAGIC); String expMag = Integer.toHexString(JAVA_PACKAGE_MAGIC);
throw new IOException("Unexpected package magic number: got "+gotMag+"; expected "+expMag); throw new IOException("Unexpected package magic number: got "+gotMag+"; expected "+expMag);
} }
if ((package_majver != JAVA6_PACKAGE_MAJOR_VERSION && int[] majminFound = null;
package_majver != JAVA5_PACKAGE_MAJOR_VERSION) || for (int[] majmin : new int[][]{
(package_minver != JAVA6_PACKAGE_MINOR_VERSION && { JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION },
package_minver != JAVA5_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 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 "+ " OR "+
JAVA5_PACKAGE_MAJOR_VERSION+"."+JAVA5_PACKAGE_MINOR_VERSION; JAVA5_PACKAGE_MAJOR_VERSION+"."+JAVA5_PACKAGE_MINOR_VERSION;
throw new IOException("Unexpected package minor version: got "+gotVer+"; expected "+expVer); throw new IOException("Unexpected package minor version: got "+gotVer+"; expected "+expVer);
...@@ -213,6 +227,7 @@ class Package { ...@@ -213,6 +227,7 @@ class Package {
//ArrayList attributes; // in Attribute.Holder.this.attributes //ArrayList attributes; // in Attribute.Holder.this.attributes
// Note that InnerClasses may be collected at the package level. // Note that InnerClasses may be collected at the package level.
ArrayList<InnerClass> innerClasses; ArrayList<InnerClass> innerClasses;
ArrayList<BootstrapMethodEntry> bootstrapMethods;
Class(int flags, ClassEntry thisClass, ClassEntry superClass, ClassEntry[] interfaces) { Class(int flags, ClassEntry thisClass, ClassEntry superClass, ClassEntry[] interfaces) {
this.magic = JAVA_MAGIC; this.magic = JAVA_MAGIC;
...@@ -313,6 +328,25 @@ class Package { ...@@ -313,6 +328,25 @@ class Package {
this.cpMap = cpMap; this.cpMap = cpMap;
} }
boolean hasBootstrapMethods() {
return bootstrapMethods != null && !bootstrapMethods.isEmpty();
}
List<BootstrapMethodEntry> getBootstrapMethods() {
return bootstrapMethods;
}
BootstrapMethodEntry[] getBootstrapMethodMap() {
return (hasBootstrapMethods())
? bootstrapMethods.toArray(new BootstrapMethodEntry[bootstrapMethods.size()])
: null;
}
void setBootstrapMethods(Collection<BootstrapMethodEntry> bsms) {
assert(bootstrapMethods == null); // do not do this twice
bootstrapMethods = new ArrayList<>(bsms);
}
boolean hasInnerClasses() { boolean hasInnerClasses() {
return innerClasses != null; return innerClasses != null;
} }
...@@ -1283,7 +1317,8 @@ class Package { ...@@ -1283,7 +1317,8 @@ class Package {
byTagU[tag] = null; // done with it byTagU[tag] = null; // done with it
} }
for (int i = 0; i < byTagU.length; i++) { 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++) { for (int i = 0; i < ConstantPool.TAGS_IN_ORDER.length; i++) {
byte tag = ConstantPool.TAGS_IN_ORDER[i]; byte tag = ConstantPool.TAGS_IN_ORDER[i];
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,13 +25,7 @@ ...@@ -25,13 +25,7 @@
package com.sun.java.util.jar.pack; package com.sun.java.util.jar.pack;
import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; import com.sun.java.util.jar.pack.ConstantPool.*;
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.Package.Class; 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.File;
import com.sun.java.util.jar.pack.Package.InnerClass; import com.sun.java.util.jar.pack.Package.InnerClass;
...@@ -46,6 +40,7 @@ import java.util.ArrayList; ...@@ -46,6 +40,7 @@ import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.HashMap; import java.util.HashMap;
...@@ -266,7 +261,6 @@ class PackageReader extends BandStructure { ...@@ -266,7 +261,6 @@ class PackageReader extends BandStructure {
// #band_headers_size :UNSIGNED5[1] // #band_headers_size :UNSIGNED5[1]
// #attr_definition_count :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.expectLength(AH_LENGTH_0);
archive_header_0.readFrom(in); archive_header_0.readFrom(in);
...@@ -282,6 +276,7 @@ class PackageReader extends BandStructure { ...@@ -282,6 +276,7 @@ class PackageReader extends BandStructure {
boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS); boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS);
boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS); boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS);
boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS); boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS);
boolean haveCPExtra = testBit(archiveOptions, AO_HAVE_CP_EXTRAS);
initAttrIndexLimit(); initAttrIndexLimit();
// now we are ready to use the data: // now we are ready to use the data:
...@@ -300,11 +295,11 @@ class PackageReader extends BandStructure { ...@@ -300,11 +295,11 @@ class PackageReader extends BandStructure {
archive_header_S.doneDisbursing(); archive_header_S.doneDisbursing();
archiveSize0 = in.getBytesServed(); archiveSize0 = in.getBytesServed();
int remainingHeaders = AH_LENGTH - AH_LENGTH_0 - AH_LENGTH_S; int remainingHeaders = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S;
if (!haveFiles) remainingHeaders -= AH_FILE_HEADER_LEN-AH_LENGTH_S; if (haveFiles) remainingHeaders += AH_FILE_HEADER_LEN;
if (!haveSpecial) remainingHeaders -= AH_SPECIAL_FORMAT_LEN; if (haveSpecial) remainingHeaders += AH_SPECIAL_FORMAT_LEN;
if (!haveNumbers) remainingHeaders -= AH_CP_NUMBER_LEN; if (haveNumbers) remainingHeaders += AH_CP_NUMBER_LEN;
assert(remainingHeaders >= AH_LENGTH_MIN - AH_LENGTH_0); if (haveCPExtra) remainingHeaders += AH_CP_EXTRA_LEN;
archive_header_1.expectLength(remainingHeaders); archive_header_1.expectLength(remainingHeaders);
archive_header_1.readFrom(in); archive_header_1.readFrom(in);
...@@ -325,7 +320,7 @@ class PackageReader extends BandStructure { ...@@ -325,7 +320,7 @@ class PackageReader extends BandStructure {
numAttrDefs = 0; numAttrDefs = 0;
} }
readConstantPoolCounts(haveNumbers); readConstantPoolCounts(haveNumbers, haveCPExtra);
numInnerClasses = archive_header_1.getInt(); numInnerClasses = archive_header_1.getInt();
...@@ -351,7 +346,7 @@ class PackageReader extends BandStructure { ...@@ -351,7 +346,7 @@ class PackageReader extends BandStructure {
band_headers.doneDisbursing(); band_headers.doneDisbursing();
} }
void readConstantPoolCounts(boolean haveNumbers) throws IOException { void readConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException {
// size the constant pools: // size the constant pools:
for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) {
// cp_counts: // cp_counts:
...@@ -364,6 +359,7 @@ class PackageReader extends BandStructure { ...@@ -364,6 +359,7 @@ class PackageReader extends BandStructure {
// #cp_Field_count :UNSIGNED5[1] // #cp_Field_count :UNSIGNED5[1]
// #cp_Method_count :UNSIGNED5[1] // #cp_Method_count :UNSIGNED5[1]
// #cp_Imethod_count :UNSIGNED5[1] // #cp_Imethod_count :UNSIGNED5[1]
// (cp_attr_counts) ** (#have_cp_attr_counts)
// //
// cp_number_counts: // cp_number_counts:
// #cp_Int_count :UNSIGNED5[1] // #cp_Int_count :UNSIGNED5[1]
...@@ -371,6 +367,12 @@ class PackageReader extends BandStructure { ...@@ -371,6 +367,12 @@ class PackageReader extends BandStructure {
// #cp_Long_count :UNSIGNED5[1] // #cp_Long_count :UNSIGNED5[1]
// #cp_Double_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]; byte tag = ConstantPool.TAGS_IN_ORDER[k];
if (!haveNumbers) { if (!haveNumbers) {
// These four counts are optional. // These four counts are optional.
...@@ -382,6 +384,16 @@ class PackageReader extends BandStructure { ...@@ -382,6 +384,16 @@ class PackageReader extends BandStructure {
continue; 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(); tagCount[tag] = archive_header_1.getInt();
} }
} }
...@@ -401,6 +413,11 @@ class PackageReader extends BandStructure { ...@@ -401,6 +413,11 @@ class PackageReader extends BandStructure {
return index; 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 { void readConstantPool() throws IOException {
// cp_bands: // cp_bands:
// cp_Utf8 // cp_Utf8
...@@ -533,8 +550,82 @@ class PackageReader extends BandStructure { ...@@ -533,8 +550,82 @@ class PackageReader extends BandStructure {
case CONSTANT_InterfaceMethodref: case CONSTANT_InterfaceMethodref:
readMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); readMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc);
break; 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: default:
assert(false); throw new AssertionError("unexpected CP tag in package");
} }
Index index = initCPIndex(tag, cpMap); Index index = initCPIndex(tag, cpMap);
...@@ -548,6 +639,21 @@ class PackageReader extends BandStructure { ...@@ -548,6 +639,21 @@ class PackageReader extends BandStructure {
cp_bands.doneDisbursing(); 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(); setBandIndexes();
} }
...@@ -1056,8 +1162,16 @@ class PackageReader extends BandStructure { ...@@ -1056,8 +1162,16 @@ class PackageReader extends BandStructure {
// look for constant pool entries: // look for constant pool entries:
cls.visitRefs(VRM_CLASSIC, cpRefs); cls.visitRefs(VRM_CLASSIC, cpRefs);
ArrayList<BootstrapMethodEntry> 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 // flesh out the local constant pool
ConstantPool.completeReferencesIn(cpRefs, true); ConstantPool.completeReferencesIn(cpRefs, true, bsms);
// Now that we know all our local class references, // Now that we know all our local class references,
// compute the InnerClasses attribute. // compute the InnerClasses attribute.
...@@ -1074,14 +1188,23 @@ class PackageReader extends BandStructure { ...@@ -1074,14 +1188,23 @@ class PackageReader extends BandStructure {
} }
// flesh out the local constant pool, again // 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 // construct a local constant pool
int numDoubles = 0; int numDoubles = 0;
for (Entry e : cpRefs) { for (Entry e : cpRefs) {
if (e.isDoubleWord()) numDoubles++; if (e.isDoubleWord()) numDoubles++;
assert(e.tag != CONSTANT_Signature) : (e);
} }
Entry[] cpMap = new Entry[1+numDoubles+cpRefs.size()]; Entry[] cpMap = new Entry[1+numDoubles+cpRefs.size()];
int fillp = 1; int fillp = 1;
...@@ -1154,7 +1277,8 @@ class PackageReader extends BandStructure { ...@@ -1154,7 +1277,8 @@ class PackageReader extends BandStructure {
int totalNM = class_method_count.getIntTotal(); int totalNM = class_method_count.getIntTotal();
field_descr.expectLength(totalNF); field_descr.expectLength(totalNF);
method_descr.expectLength(totalNM); 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<Class.Field> fields = new ArrayList<>(totalNF); List<Class.Field> fields = new ArrayList<>(totalNF);
field_descr.readFrom(in); field_descr.readFrom(in);
...@@ -1393,7 +1517,8 @@ class PackageReader extends BandStructure { ...@@ -1393,7 +1517,8 @@ class PackageReader extends BandStructure {
MultiBand xxx_attr_bands = attrBands[ctype]; MultiBand xxx_attr_bands = attrBands[ctype];
long flagMask = attrFlagMask[ctype]; long flagMask = attrFlagMask[ctype];
if (verbose > 1) { 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 // Fetch the attribute layout definitions which govern the bands
...@@ -1751,8 +1876,10 @@ class PackageReader extends BandStructure { ...@@ -1751,8 +1876,10 @@ class PackageReader extends BandStructure {
bc_local, bc_label, bc_local, bc_label,
bc_intref, bc_floatref, bc_intref, bc_floatref,
bc_longref, bc_doubleref, bc_stringref, bc_longref, bc_doubleref, bc_stringref,
bc_loadablevalueref,
bc_classref, bc_fieldref, bc_classref, bc_fieldref,
bc_methodref, bc_imethodref, bc_methodref, bc_imethodref,
bc_indyref,
bc_thisfield, bc_superfield, bc_thisfield, bc_superfield,
bc_thismethod, bc_supermethod, bc_thismethod, bc_supermethod,
bc_initref, bc_initref,
...@@ -2099,7 +2226,8 @@ class PackageReader extends BandStructure { ...@@ -2099,7 +2226,8 @@ class PackageReader extends BandStructure {
case _ildc: case _ildc:
case _cldc: case _cldc:
case _fldc: case _fldc:
case _aldc: case _sldc:
case _qldc:
origBC = _ldc; origBC = _ldc;
size = 1; size = 1;
ldcRefSet.add(ref); ldcRefSet.add(ref);
...@@ -2107,7 +2235,8 @@ class PackageReader extends BandStructure { ...@@ -2107,7 +2235,8 @@ class PackageReader extends BandStructure {
case _ildc_w: case _ildc_w:
case _cldc_w: case _cldc_w:
case _fldc_w: case _fldc_w:
case _aldc_w: case _sldc_w:
case _qldc_w:
origBC = _ldc_w; origBC = _ldc_w;
break; break;
case _lldc2_w: case _lldc2_w:
...@@ -2136,6 +2265,9 @@ class PackageReader extends BandStructure { ...@@ -2136,6 +2265,9 @@ class PackageReader extends BandStructure {
int argSize = ((MemberEntry)ref).descRef.typeRef.computeSize(true); int argSize = ((MemberEntry)ref).descRef.typeRef.computeSize(true);
buf[pc++] = (byte)( 1 + argSize ); buf[pc++] = (byte)( 1 + argSize );
buf[pc++] = 0; buf[pc++] = 0;
} else if (origBC == _invokedynamic) {
buf[pc++] = 0;
buf[pc++] = 0;
} }
assert(Instruction.opLength(origBC) == (pc - curPC)); assert(Instruction.opLength(origBC) == (pc - curPC));
continue; continue;
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,15 +25,7 @@ ...@@ -25,15 +25,7 @@
package com.sun.java.util.jar.pack; package com.sun.java.util.jar.pack;
import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; import com.sun.java.util.jar.pack.ConstantPool.*;
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.Package.Class; 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.File;
import com.sun.java.util.jar.pack.Package.InnerClass; import com.sun.java.util.jar.pack.Package.InnerClass;
...@@ -281,7 +273,7 @@ class PackageWriter extends BandStructure { ...@@ -281,7 +273,7 @@ class PackageWriter extends BandStructure {
void writeArchiveHeader() throws IOException { void writeArchiveHeader() throws IOException {
// for debug only: number of words optimized away // 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 // AO_HAVE_SPECIAL_FORMATS is set if non-default
// coding techniques are used, or if there are // coding techniques are used, or if there are
...@@ -293,8 +285,8 @@ class PackageWriter extends BandStructure { ...@@ -293,8 +285,8 @@ class PackageWriter extends BandStructure {
if (haveSpecial) if (haveSpecial)
archiveOptions |= AO_HAVE_SPECIAL_FORMATS; archiveOptions |= AO_HAVE_SPECIAL_FORMATS;
} }
if (!haveSpecial) if (haveSpecial)
headerDiscountForDebug += AH_SPECIAL_FORMAT_LEN; headerSizeForDebug += AH_SPECIAL_FORMAT_LEN;
// AO_HAVE_FILE_HEADERS is set if there is any // AO_HAVE_FILE_HEADERS is set if there is any
// file or segment envelope information present. // file or segment envelope information present.
...@@ -305,8 +297,8 @@ class PackageWriter extends BandStructure { ...@@ -305,8 +297,8 @@ class PackageWriter extends BandStructure {
if (haveFiles) if (haveFiles)
archiveOptions |= AO_HAVE_FILE_HEADERS; archiveOptions |= AO_HAVE_FILE_HEADERS;
} }
if (!haveFiles) if (haveFiles)
headerDiscountForDebug += AH_FILE_HEADER_LEN; headerSizeForDebug += AH_FILE_HEADER_LEN;
// AO_HAVE_CP_NUMBERS is set if there are any numbers // AO_HAVE_CP_NUMBERS is set if there are any numbers
// in the global constant pool. (Numbers are in 15% of classes.) // in the global constant pool. (Numbers are in 15% of classes.)
...@@ -316,8 +308,19 @@ class PackageWriter extends BandStructure { ...@@ -316,8 +308,19 @@ class PackageWriter extends BandStructure {
if (haveNumbers) if (haveNumbers)
archiveOptions |= AO_HAVE_CP_NUMBERS; archiveOptions |= AO_HAVE_CP_NUMBERS;
} }
if (!haveNumbers) if (haveNumbers)
headerDiscountForDebug += AH_CP_NUMBER_LEN; 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! assert(pkg.package_majver > 0); // caller must specify!
archive_header_0.putInt(pkg.package_minver); archive_header_0.putInt(pkg.package_minver);
...@@ -355,18 +358,18 @@ class PackageWriter extends BandStructure { ...@@ -355,18 +358,18 @@ class PackageWriter extends BandStructure {
assert(attrDefsWritten.length == 0); assert(attrDefsWritten.length == 0);
} }
writeConstantPoolCounts(haveNumbers); writeConstantPoolCounts(haveNumbers, haveCPExtra);
archive_header_1.putInt(pkg.getAllInnerClasses().size()); archive_header_1.putInt(pkg.getAllInnerClasses().size());
archive_header_1.putInt(pkg.default_class_minver); archive_header_1.putInt(pkg.default_class_minver);
archive_header_1.putInt(pkg.default_class_majver); archive_header_1.putInt(pkg.default_class_majver);
archive_header_1.putInt(pkg.classes.size()); 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() + assert(archive_header_0.length() +
archive_header_S.length() + archive_header_S.length() +
archive_header_1.length() archive_header_1.length()
== AH_LENGTH - headerDiscountForDebug); == headerSizeForDebug);
// Figure out all the sizes now, first cut: // Figure out all the sizes now, first cut:
archiveSize0 = 0; archiveSize0 = 0;
...@@ -394,9 +397,8 @@ class PackageWriter extends BandStructure { ...@@ -394,9 +397,8 @@ class PackageWriter extends BandStructure {
assert(all_bands.outputSize() == archiveSize0+archiveSize1); assert(all_bands.outputSize() == archiveSize0+archiveSize1);
} }
void writeConstantPoolCounts(boolean haveNumbers) throws IOException { void writeConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException {
for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { for (byte tag : ConstantPool.TAGS_IN_ORDER) {
byte tag = ConstantPool.TAGS_IN_ORDER[k];
int count = pkg.cp.getIndexByTag(tag).size(); int count = pkg.cp.getIndexByTag(tag).size();
switch (tag) { switch (tag) {
case CONSTANT_Utf8: case CONSTANT_Utf8:
...@@ -416,6 +418,17 @@ class PackageWriter extends BandStructure { ...@@ -416,6 +418,17 @@ class PackageWriter extends BandStructure {
continue; continue;
} }
break; 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); archive_header_1.putInt(count);
} }
...@@ -449,8 +462,7 @@ class PackageWriter extends BandStructure { ...@@ -449,8 +462,7 @@ class PackageWriter extends BandStructure {
if (verbose > 0) Utils.log.info("Writing CP"); if (verbose > 0) Utils.log.info("Writing CP");
for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { for (byte tag : ConstantPool.TAGS_IN_ORDER) {
byte tag = ConstantPool.TAGS_IN_ORDER[k];
Index index = cp.getIndexByTag(tag); Index index = cp.getIndexByTag(tag);
Entry[] cpMap = index.cpMap; Entry[] cpMap = index.cpMap;
...@@ -530,8 +542,52 @@ class PackageWriter extends BandStructure { ...@@ -530,8 +542,52 @@ class PackageWriter extends BandStructure {
case CONSTANT_InterfaceMethodref: case CONSTANT_InterfaceMethodref:
writeMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); writeMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc);
break; 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: 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 { ...@@ -988,6 +1044,8 @@ class PackageWriter extends BandStructure {
for (Class cls : pkg.classes) { for (Class cls : pkg.classes) {
// Replace "obvious" SourceFile attrs by null. // Replace "obvious" SourceFile attrs by null.
cls.minimizeSourceFile(); cls.minimizeSourceFile();
// BootstrapMethods should never have been inserted.
assert(cls.getAttribute(Package.attrBootstrapMethodsEmpty) == null);
} }
} }
...@@ -1325,9 +1383,7 @@ class PackageWriter extends BandStructure { ...@@ -1325,9 +1383,7 @@ class PackageWriter extends BandStructure {
// %%% Add a stress mode which issues _ref/_byte_escape. // %%% Add a stress mode which issues _ref/_byte_escape.
if (verbose > 3) Utils.log.fine(i.toString()); if (verbose > 3) Utils.log.fine(i.toString());
if (i.isNonstandard() if (i.isNonstandard()) {
&& (!p200.getBoolean(Utils.COM_PREFIX+"invokedynamic")
|| i.getBC() != _xxxunusedxxx)) {
// Crash and burn with a complaint if there are funny // Crash and burn with a complaint if there are funny
// bytecodes in this class file. // bytecodes in this class file.
String complaint = code.getMethod() String complaint = code.getMethod()
...@@ -1427,24 +1483,6 @@ class PackageWriter extends BandStructure { ...@@ -1427,24 +1483,6 @@ class PackageWriter extends BandStructure {
continue; 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(); int branch = i.getBranchLabel();
if (branch >= 0) { if (branch >= 0) {
bc_codes.putByte(bc); bc_codes.putByte(bc);
...@@ -1458,7 +1496,7 @@ class PackageWriter extends BandStructure { ...@@ -1458,7 +1496,7 @@ class PackageWriter extends BandStructure {
CPRefBand bc_which; CPRefBand bc_which;
int vbc = bc; int vbc = bc;
switch (i.getCPTag()) { switch (i.getCPTag()) {
case CONSTANT_Literal: case CONSTANT_LoadableValue:
switch (ref.tag) { switch (ref.tag) {
case CONSTANT_Integer: case CONSTANT_Integer:
bc_which = bc_intref; bc_which = bc_intref;
...@@ -1489,8 +1527,8 @@ class PackageWriter extends BandStructure { ...@@ -1489,8 +1527,8 @@ class PackageWriter extends BandStructure {
case CONSTANT_String: case CONSTANT_String:
bc_which = bc_stringref; bc_which = bc_stringref;
switch (bc) { switch (bc) {
case _ldc: vbc = _aldc; break; case _ldc: vbc = _sldc; break;
case _ldc_w: vbc = _aldc_w; break; case _ldc_w: vbc = _sldc_w; break;
default: assert(false); default: assert(false);
} }
break; break;
...@@ -1503,8 +1541,16 @@ class PackageWriter extends BandStructure { ...@@ -1503,8 +1541,16 @@ class PackageWriter extends BandStructure {
} }
break; break;
default: default:
bc_which = null; // CONSTANT_MethodHandle, etc.
assert(false); 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; break;
case CONSTANT_Class: case CONSTANT_Class:
...@@ -1517,6 +1563,8 @@ class PackageWriter extends BandStructure { ...@@ -1517,6 +1563,8 @@ class PackageWriter extends BandStructure {
bc_which = bc_methodref; break; bc_which = bc_methodref; break;
case CONSTANT_InterfaceMethodref: case CONSTANT_InterfaceMethodref:
bc_which = bc_imethodref; break; bc_which = bc_imethodref; break;
case CONSTANT_InvokeDynamic:
bc_which = bc_indyref; break;
default: default:
bc_which = null; bc_which = null;
assert(false); assert(false);
...@@ -1532,6 +1580,12 @@ class PackageWriter extends BandStructure { ...@@ -1532,6 +1580,12 @@ class PackageWriter extends BandStructure {
assert(i.getLength() == 5); assert(i.getLength() == 5);
// Make sure the discarded bytes are sane: // Make sure the discarded bytes are sane:
assert(i.getConstant() == (1+((MemberEntry)ref).descRef.typeRef.computeSize(true)) << 8); 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 { } else {
// Make sure there is nothing else to write. // Make sure there is nothing else to write.
assert(i.getLength() == ((bc == _ldc)?2:3)); assert(i.getLength() == ((bc == _ldc)?2:3));
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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; ...@@ -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.DescriptorEntry;
import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry; 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.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.SignatureEntry;
import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
import java.util.HashMap; import java.util.HashMap;
...@@ -56,6 +60,10 @@ class TLGlobals { ...@@ -56,6 +60,10 @@ class TLGlobals {
private final Map<String, SignatureEntry> signatureEntries; private final Map<String, SignatureEntry> signatureEntries;
private final Map<String, DescriptorEntry> descriptorEntries; private final Map<String, DescriptorEntry> descriptorEntries;
private final Map<String, MemberEntry> memberEntries; private final Map<String, MemberEntry> memberEntries;
private final Map<String, MethodHandleEntry> methodHandleEntries;
private final Map<String, MethodTypeEntry> methodTypeEntries;
private final Map<String, InvokeDynamicEntry> invokeDynamicEntries;
private final Map<String, BootstrapMethodEntry> bootstrapMethodEntries;
TLGlobals() { TLGlobals() {
utf8Entries = new HashMap<>(); utf8Entries = new HashMap<>();
...@@ -64,6 +72,10 @@ class TLGlobals { ...@@ -64,6 +72,10 @@ class TLGlobals {
signatureEntries = new HashMap<>(); signatureEntries = new HashMap<>();
descriptorEntries = new HashMap<>(); descriptorEntries = new HashMap<>();
memberEntries = new HashMap<>(); memberEntries = new HashMap<>();
methodHandleEntries = new HashMap<>();
methodTypeEntries = new HashMap<>();
invokeDynamicEntries = new HashMap<>();
bootstrapMethodEntries = new HashMap<>();
props = new PropMap(); props = new PropMap();
} }
...@@ -94,4 +106,20 @@ class TLGlobals { ...@@ -94,4 +106,20 @@ class TLGlobals {
Map<String, MemberEntry> getMemberEntries() { Map<String, MemberEntry> getMemberEntries() {
return memberEntries; return memberEntries;
} }
Map<String, MethodHandleEntry> getMethodHandleEntries() {
return methodHandleEntries;
}
Map<String, MethodTypeEntry> getMethodTypeEntries() {
return methodTypeEntries;
}
Map<String, InvokeDynamicEntry> getInvokeDynamicEntries() {
return invokeDynamicEntries;
}
Map<String, BootstrapMethodEntry> getBootstrapMethodEntries() {
return bootstrapMethodEntries;
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -60,7 +60,7 @@ class Utils { ...@@ -60,7 +60,7 @@ class Utils {
* If >3, print tons of comments (e.g., processing of references). * If >3, print tons of comments (e.g., processing of references).
* (installer only) * (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. * Disables use of native code, prefers the Java-coded implementation.
...@@ -134,35 +134,11 @@ class Utils { ...@@ -134,35 +134,11 @@ class Utils {
// to the engine code, especially the native code. // to the engine code, especially the native code.
static final ThreadLocal<TLGlobals> currentInstance = new ThreadLocal<>(); static final ThreadLocal<TLGlobals> currentInstance = new ThreadLocal<>();
// convenience methods to access the TL globals // convenience method to access the TL globals
static TLGlobals getTLGlobals() { static TLGlobals getTLGlobals() {
return currentInstance.get(); return currentInstance.get();
} }
static Map<String, Utf8Entry> getUtf8Entries() {
return getTLGlobals().getUtf8Entries();
}
static Map<String, ClassEntry> getClassEntries() {
return getTLGlobals().getClassEntries();
}
static Map<Object, LiteralEntry> getLiteralEntries() {
return getTLGlobals().getLiteralEntries();
}
static Map<String, DescriptorEntry> getDescriptorEntries() {
return getTLGlobals().getDescriptorEntries();
}
static Map<String, SignatureEntry> getSignatureEntries() {
return getTLGlobals().getSignatureEntries();
}
static Map<String, MemberEntry> getMemberEntries() {
return getTLGlobals().getMemberEntries();
}
static PropMap currentPropMap() { static PropMap currentPropMap() {
Object obj = currentInstance.get(); Object obj = currentInstance.get();
if (obj instanceof PackerImpl) if (obj instanceof PackerImpl)
...@@ -173,8 +149,19 @@ class Utils { ...@@ -173,8 +149,19 @@ class Utils {
} }
static final boolean nolog 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 { static class Pack200Logger {
private final String name; private final String name;
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -188,9 +188,13 @@ void band::setIndexByTag(byte tag) { ...@@ -188,9 +188,13 @@ void band::setIndexByTag(byte tag) {
entry* band::getRefCommon(cpindex* ix_, bool nullOKwithCaller) { entry* band::getRefCommon(cpindex* ix_, bool nullOKwithCaller) {
CHECK_0; CHECK_0;
assert(ix_->ixTag == ixTag assert(ix_->ixTag == ixTag
|| (ixTag == CONSTANT_Literal || ((ixTag == CONSTANT_All ||
&& ix_->ixTag >= CONSTANT_Integer ixTag == CONSTANT_LoadableValue ||
&& ix_->ixTag <= CONSTANT_String)); ixTag == CONSTANT_AnyMember)
|| (ixTag == CONSTANT_FieldSpecific &&
ix_->ixTag >= CONSTANT_Integer &&
ix_->ixTag <= CONSTANT_String))
);
int n = vs[0].getInt() - nullOK; int n = vs[0].getInt() - nullOK;
// Note: band-local nullOK means null encodes as 0. // Note: band-local nullOK means null encodes as 0.
// But nullOKwithCaller means caller is willing to tolerate a null. // But nullOKwithCaller means caller is willing to tolerate a null.
...@@ -270,22 +274,15 @@ int band::getIntCount(int tag) { ...@@ -270,22 +274,15 @@ int band::getIntCount(int tag) {
#define NO_INDEX 0 #define NO_INDEX 0
struct band_init { struct band_init {
#ifndef PRODUCT
int bn; int bn;
const char* name; const char* name;
#endif
int defc; int defc;
int index; int index;
}; };
#ifdef PRODUCT
#define BAND_INIT(name, cspec, ix) \
{ cspec, ix }
#else
#define BAND_INIT(name, cspec, ix) \ #define BAND_INIT(name, cspec, ix) \
{ e_##name, #name, /*debug only*/ \ { e_##name, #name, /*debug only*/ \
cspec, ix } cspec, ix }
#endif
const band_init all_band_inits[] = { const band_init all_band_inits[] = {
//BAND_INIT(archive_magic, BYTE1_spec, 0), //BAND_INIT(archive_magic, BYTE1_spec, 0),
...@@ -314,6 +311,14 @@ const band_init all_band_inits[] = { ...@@ -314,6 +311,14 @@ const band_init all_band_inits[] = {
BAND_INIT(cp_Method_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), BAND_INIT(cp_Method_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)),
BAND_INIT(cp_Imethod_class, DELTA5_spec, INDEX(CONSTANT_Class)), BAND_INIT(cp_Imethod_class, DELTA5_spec, INDEX(CONSTANT_Class)),
BAND_INIT(cp_Imethod_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), 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_headers, BYTE1_spec, 0),
BAND_INIT(attr_definition_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), BAND_INIT(attr_definition_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)),
BAND_INIT(attr_definition_layout, 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[] = { ...@@ -333,7 +338,7 @@ const band_init all_band_inits[] = {
BAND_INIT(field_attr_count, UNSIGNED5_spec, 0), BAND_INIT(field_attr_count, UNSIGNED5_spec, 0),
BAND_INIT(field_attr_indexes, UNSIGNED5_spec, 0), BAND_INIT(field_attr_indexes, UNSIGNED5_spec, 0),
BAND_INIT(field_attr_calls, 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_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)),
BAND_INIT(field_metadata_bands, -1, -1), BAND_INIT(field_metadata_bands, -1, -1),
BAND_INIT(field_attr_bands, -1, -1), BAND_INIT(field_attr_bands, -1, -1),
...@@ -415,10 +420,12 @@ const band_init all_band_inits[] = { ...@@ -415,10 +420,12 @@ const band_init all_band_inits[] = {
BAND_INIT(bc_longref, DELTA5_spec, INDEX(CONSTANT_Long)), BAND_INIT(bc_longref, DELTA5_spec, INDEX(CONSTANT_Long)),
BAND_INIT(bc_doubleref, DELTA5_spec, INDEX(CONSTANT_Double)), BAND_INIT(bc_doubleref, DELTA5_spec, INDEX(CONSTANT_Double)),
BAND_INIT(bc_stringref, DELTA5_spec, INDEX(CONSTANT_String)), 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_classref, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)),
BAND_INIT(bc_fieldref, DELTA5_spec, INDEX(CONSTANT_Fieldref)), BAND_INIT(bc_fieldref, DELTA5_spec, INDEX(CONSTANT_Fieldref)),
BAND_INIT(bc_methodref, UNSIGNED5_spec, INDEX(CONSTANT_Methodref)), BAND_INIT(bc_methodref, UNSIGNED5_spec, INDEX(CONSTANT_Methodref)),
BAND_INIT(bc_imethodref, DELTA5_spec, INDEX(CONSTANT_InterfaceMethodref)), 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_thisfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)),
BAND_INIT(bc_superfield, 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)), BAND_INIT(bc_thismethod, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)),
...@@ -471,7 +478,7 @@ void band::initIndexes(unpacker* u) { ...@@ -471,7 +478,7 @@ void band::initIndexes(unpacker* u) {
for (int i = 0; i < BAND_LIMIT; i++) { for (int i = 0; i < BAND_LIMIT; i++) {
band* scan = &tmp_all_bands[i]; band* scan = &tmp_all_bands[i];
uint tag = scan->ixTag; // Cf. #define INDEX(tag) above 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)); scan->setIndex(u->cp.getIndex(tag));
} }
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,9 +29,7 @@ struct cpindex; ...@@ -29,9 +29,7 @@ struct cpindex;
struct unpacker; struct unpacker;
struct band { struct band {
#ifndef PRODUCT
const char* name; const char* name;
#endif
int bn; // band_number of this band int bn; // band_number of this band
coding* defc; // default coding method coding* defc; // default coding method
cpindex* ix; // CP entry mapping, if CPRefBand cpindex* ix; // CP entry mapping, if CPRefBand
...@@ -162,6 +160,14 @@ enum band_number { ...@@ -162,6 +160,14 @@ enum band_number {
e_cp_Method_desc, e_cp_Method_desc,
e_cp_Imethod_class, e_cp_Imethod_class,
e_cp_Imethod_desc, 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 // bands which define transmission of attributes
e_attr_definition_headers, e_attr_definition_headers,
...@@ -284,11 +290,13 @@ enum band_number { ...@@ -284,11 +290,13 @@ enum band_number {
e_bc_longref, e_bc_longref,
e_bc_doubleref, e_bc_doubleref,
e_bc_stringref, e_bc_stringref,
e_bc_loadablevalueref,
e_bc_classref, e_bc_classref,
e_bc_fieldref, e_bc_fieldref,
e_bc_methodref, e_bc_methodref,
e_bc_imethodref, e_bc_imethodref,
e_bc_indyref,
// _self_linker_op family // _self_linker_op family
e_bc_thisfield, e_bc_thisfield,
...@@ -343,6 +351,14 @@ enum band_number { ...@@ -343,6 +351,14 @@ enum band_number {
#define cp_Method_desc all_bands[e_cp_Method_desc] #define cp_Method_desc all_bands[e_cp_Method_desc]
#define cp_Imethod_class all_bands[e_cp_Imethod_class] #define cp_Imethod_class all_bands[e_cp_Imethod_class]
#define cp_Imethod_desc all_bands[e_cp_Imethod_desc] #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_headers all_bands[e_attr_definition_headers]
#define attr_definition_name all_bands[e_attr_definition_name] #define attr_definition_name all_bands[e_attr_definition_name]
#define attr_definition_layout all_bands[e_attr_definition_layout] #define attr_definition_layout all_bands[e_attr_definition_layout]
...@@ -437,10 +453,12 @@ enum band_number { ...@@ -437,10 +453,12 @@ enum band_number {
#define bc_longref all_bands[e_bc_longref] #define bc_longref all_bands[e_bc_longref]
#define bc_doubleref all_bands[e_bc_doubleref] #define bc_doubleref all_bands[e_bc_doubleref]
#define bc_stringref all_bands[e_bc_stringref] #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_classref all_bands[e_bc_classref]
#define bc_fieldref all_bands[e_bc_fieldref] #define bc_fieldref all_bands[e_bc_fieldref]
#define bc_methodref all_bands[e_bc_methodref] #define bc_methodref all_bands[e_bc_methodref]
#define bc_imethodref all_bands[e_bc_imethodref] #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_thisfield all_bands[e_bc_thisfield]
#define bc_superfield all_bands[e_bc_superfield] #define bc_superfield all_bands[e_bc_superfield]
#define bc_thismethod all_bands[e_bc_thismethod] #define bc_thismethod all_bands[e_bc_thismethod]
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -49,61 +49,82 @@ ...@@ -49,61 +49,82 @@
#define JAVA6_PACKAGE_MAJOR_VERSION 160 #define JAVA6_PACKAGE_MAJOR_VERSION 160
#define JAVA6_PACKAGE_MINOR_VERSION 1 #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) // magic number for gzip streams (for processing pack200-gzip data)
#define GZIP_MAGIC 0x1F8B0800 #define GZIP_MAGIC 0x1F8B0800
#define GZIP_MAGIC_MASK 0xFFFFFF00 // last byte is variable "flg" field #define GZIP_MAGIC_MASK 0xFFFFFF00 // last byte is variable "flg" field
enum { enum {
CONSTANT_None, CONSTANT_None = 0,
CONSTANT_Utf8, CONSTANT_Utf8 = 1,
CONSTANT_unused2, /* unused, was Unicode */ CONSTANT_unused = 2, /* unused, was Unicode */
CONSTANT_Integer, CONSTANT_Integer = 3,
CONSTANT_Float, CONSTANT_Float = 4,
CONSTANT_Long, CONSTANT_Long = 5,
CONSTANT_Double, CONSTANT_Double = 6,
CONSTANT_Class, CONSTANT_Class = 7,
CONSTANT_String, CONSTANT_String = 8,
CONSTANT_Fieldref, CONSTANT_Fieldref = 9,
CONSTANT_Methodref, CONSTANT_Methodref = 10,
CONSTANT_InterfaceMethodref, CONSTANT_InterfaceMethodref = 11,
CONSTANT_NameandType, 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_MethodHandle reference kinds
CONSTANT_All = 14, REF_getField = 1,
CONSTANT_Limit = 15, REF_getStatic = 2,
CONSTANT_NONE = 0, REF_putField = 3,
REF_putStatic = 4,
CONSTANT_Literal = 20, //pseudo-tag for debugging REF_invokeVirtual = 5,
CONSTANT_Member = 21, //pseudo-tag for debugging REF_invokeStatic = 6,
REF_invokeSpecial = 7,
REF_newInvokeSpecial = 8,
REF_invokeInterface = 9,
SUBINDEX_BIT = 64, // combined with CONSTANT_xxx for ixTag SUBINDEX_BIT = 64, // combined with CONSTANT_xxx for ixTag
ACC_STATIC = 0x0008, ACC_STATIC = 0x0008,
ACC_IC_LONG_FORM = (1<<16), //for ic_flags ACC_IC_LONG_FORM = (1<<16), //for ic_flags
CLASS_ATTR_SourceFile = 17, CLASS_ATTR_SourceFile = 17,
CLASS_ATTR_EnclosingMethod = 18, CLASS_ATTR_EnclosingMethod = 18,
CLASS_ATTR_InnerClasses = 23, CLASS_ATTR_InnerClasses = 23,
CLASS_ATTR_ClassFile_version = 24, CLASS_ATTR_ClassFile_version = 24,
FIELD_ATTR_ConstantValue = 17, CLASS_ATTR_BootstrapMethods = 25,
METHOD_ATTR_Code = 17, FIELD_ATTR_ConstantValue = 17,
METHOD_ATTR_Exceptions = 18, METHOD_ATTR_Code = 17,
METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23, METHOD_ATTR_Exceptions = 18,
METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23,
METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24, METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24,
METHOD_ATTR_AnnotationDefault = 25, METHOD_ATTR_AnnotationDefault = 25,
CODE_ATTR_StackMapTable = 0, CODE_ATTR_StackMapTable = 0,
CODE_ATTR_LineNumberTable = 1, CODE_ATTR_LineNumberTable = 1,
CODE_ATTR_LocalVariableTable = 2, CODE_ATTR_LocalVariableTable = 2,
CODE_ATTR_LocalVariableTypeTable = 3, CODE_ATTR_LocalVariableTypeTable = 3,
//X_ATTR_Synthetic = 12, // ACC_SYNTHETIC; not predefined //X_ATTR_Synthetic = 12, // ACC_SYNTHETIC; not predefined
X_ATTR_Signature = 19, X_ATTR_Signature = 19,
X_ATTR_Deprecated = 20, X_ATTR_Deprecated = 20,
X_ATTR_RuntimeVisibleAnnotations = 21, X_ATTR_RuntimeVisibleAnnotations = 21,
X_ATTR_RuntimeInvisibleAnnotations = 22, X_ATTR_RuntimeInvisibleAnnotations = 22,
X_ATTR_OVERFLOW = 16, X_ATTR_OVERFLOW = 16,
X_ATTR_LIMIT_NO_FLAGS_HI = 32, X_ATTR_LIMIT_NO_FLAGS_HI = 32,
X_ATTR_LIMIT_FLAGS_HI = 63, X_ATTR_LIMIT_FLAGS_HI = 63,
#define O_ATTR_DO(F) \ #define O_ATTR_DO(F) \
F(X_ATTR_OVERFLOW,01) \ F(X_ATTR_OVERFLOW,01) \
...@@ -121,6 +142,7 @@ enum { ...@@ -121,6 +142,7 @@ enum {
F(CLASS_ATTR_InnerClasses,InnerClasses) \ F(CLASS_ATTR_InnerClasses,InnerClasses) \
F(CLASS_ATTR_EnclosingMethod,EnclosingMethod) \ F(CLASS_ATTR_EnclosingMethod,EnclosingMethod) \
F(CLASS_ATTR_ClassFile_version,02) \ F(CLASS_ATTR_ClassFile_version,02) \
F(CLASS_ATTR_BootstrapMethods,BootstrapMethods) \
/*(end)*/ /*(end)*/
#define FIELD_ATTR_DO(F) \ #define FIELD_ATTR_DO(F) \
F(FIELD_ATTR_ConstantValue,ConstantValue) \ F(FIELD_ATTR_ConstantValue,ConstantValue) \
...@@ -175,7 +197,7 @@ enum { ...@@ -175,7 +197,7 @@ enum {
AO_HAVE_SPECIAL_FORMATS = 1<<0, AO_HAVE_SPECIAL_FORMATS = 1<<0,
AO_HAVE_CP_NUMBERS = 1<<1, AO_HAVE_CP_NUMBERS = 1<<1,
AO_HAVE_ALL_CODE_FLAGS = 1<<2, 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_HAVE_FILE_HEADERS = 1<<4,
AO_DEFLATE_HINT = 1<<5, AO_DEFLATE_HINT = 1<<5,
AO_HAVE_FILE_MODTIME = 1<<6, AO_HAVE_FILE_MODTIME = 1<<6,
...@@ -185,11 +207,13 @@ enum { ...@@ -185,11 +207,13 @@ enum {
AO_HAVE_FIELD_FLAGS_HI = 1<<10, AO_HAVE_FIELD_FLAGS_HI = 1<<10,
AO_HAVE_METHOD_FLAGS_HI = 1<<11, AO_HAVE_METHOD_FLAGS_HI = 1<<11,
AO_HAVE_CODE_FLAGS_HI = 1<<12, AO_HAVE_CODE_FLAGS_HI = 1<<12,
AO_UNUSED_MBZ = (-1)<<13, // options bits reserved for future use.
#define ARCHIVE_BIT_DO(F) \ #define ARCHIVE_BIT_DO(F) \
F(AO_HAVE_SPECIAL_FORMATS) \ F(AO_HAVE_SPECIAL_FORMATS) \
F(AO_HAVE_CP_NUMBERS) \ F(AO_HAVE_CP_NUMBERS) \
F(AO_HAVE_ALL_CODE_FLAGS) \ F(AO_HAVE_ALL_CODE_FLAGS) \
/*F(AO_3_UNUSED_MBZ)*/ \ F(AO_HAVE_CP_EXTRAS) \
F(AO_HAVE_FILE_HEADERS) \ F(AO_HAVE_FILE_HEADERS) \
F(AO_DEFLATE_HINT) \ F(AO_DEFLATE_HINT) \
F(AO_HAVE_FILE_MODTIME) \ F(AO_HAVE_FILE_MODTIME) \
...@@ -215,14 +239,14 @@ enum { ...@@ -215,14 +239,14 @@ enum {
NO_MODTIME = 0, // null modtime value NO_MODTIME = 0, // null modtime value
// meta-coding // meta-coding
_meta_default = 0, _meta_default = 0,
_meta_canon_min = 1, _meta_canon_min = 1,
_meta_canon_max = 115, _meta_canon_max = 115,
_meta_arb = 116, _meta_arb = 116,
_meta_run = 117, _meta_run = 117,
_meta_pop = 141, _meta_pop = 141,
_meta_limit = 189, _meta_limit = 189,
_meta_error = 255, _meta_error = 255,
_xxx_1_end _xxx_1_end
}; };
...@@ -416,7 +440,7 @@ enum { ...@@ -416,7 +440,7 @@ enum {
bc_invokespecial = 183, // 0xb7 bc_invokespecial = 183, // 0xb7
bc_invokestatic = 184, // 0xb8 bc_invokestatic = 184, // 0xb8
bc_invokeinterface = 185, // 0xb9 bc_invokeinterface = 185, // 0xb9
bc_xxxunusedxxx = 186, // 0xba bc_invokedynamic = 186, // 0xba
bc_new = 187, // 0xbb bc_new = 187, // 0xbb
bc_newarray = 188, // 0xbc bc_newarray = 188, // 0xbc
bc_anewarray = 189, // 0xbd bc_anewarray = 189, // 0xbd
...@@ -455,17 +479,19 @@ enum { ...@@ -455,17 +479,19 @@ enum {
_invokeinit_limit = _invokeinit_op+3, _invokeinit_limit = _invokeinit_op+3,
_xldc_op = _invokeinit_limit, _xldc_op = _invokeinit_limit,
bc_aldc = bc_ldc, bc_sldc = bc_ldc, // previously named bc_aldc
bc_cldc = _xldc_op+0, bc_cldc = _xldc_op+0,
bc_ildc = _xldc_op+1, bc_ildc = _xldc_op+1,
bc_fldc = _xldc_op+2, 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_cldc_w = _xldc_op+3,
bc_ildc_w = _xldc_op+4, bc_ildc_w = _xldc_op+4,
bc_fldc_w = _xldc_op+5, bc_fldc_w = _xldc_op+5,
bc_lldc2_w = bc_ldc2_w, bc_lldc2_w = bc_ldc2_w,
bc_dldc2_w = _xldc_op+6, 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 _xxx_3_end
}; };
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -109,7 +109,8 @@ typedef DWORDLONG julong; ...@@ -109,7 +109,8 @@ typedef DWORDLONG julong;
#define dup2(a,b) _dup2(a,b) #define dup2(a,b) _dup2(a,b)
#define strcasecmp(s1, s2) _stricmp(s1,s2) #define strcasecmp(s1, s2) _stricmp(s1,s2)
#define tempname _tempname #define tempname _tempname
#define sleep Sleep #define sleep Sleep
#define snprintf _snprintf
#else #else
typedef signed char byte; typedef signed char byte;
#ifdef _LP64 #ifdef _LP64
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -81,7 +81,12 @@ static const byte TAGS_IN_ORDER[] = { ...@@ -81,7 +81,12 @@ static const byte TAGS_IN_ORDER[] = {
CONSTANT_NameandType, CONSTANT_NameandType,
CONSTANT_Fieldref, CONSTANT_Fieldref,
CONSTANT_Methodref, 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) #define N_TAGS_IN_ORDER (sizeof TAGS_IN_ORDER)
...@@ -101,6 +106,11 @@ static const char* TAG_NAME[] = { ...@@ -101,6 +106,11 @@ static const char* TAG_NAME[] = {
"InterfaceMethodref", "InterfaceMethodref",
"NameandType", "NameandType",
"*Signature", "*Signature",
"unused14",
"MethodHandle",
"MethodType",
"*BootstrapMethod",
"InvokeDynamic",
0 0
}; };
...@@ -114,9 +124,13 @@ static const char* ATTR_CONTEXT_NAME[] = { // match ATTR_CONTEXT_NAME, etc. ...@@ -114,9 +124,13 @@ static const char* ATTR_CONTEXT_NAME[] = { // match ATTR_CONTEXT_NAME, etc.
#endif #endif
// Note that REQUESTED_LDC comes first, then the normal REQUESTED,
// REQUESTED must be -2 for u2 and REQUESTED_LDC must be -1 for u1 // in the regular constant pool.
enum { NOT_REQUESTED = 0, REQUESTED = -2, REQUESTED_LDC = -1 }; 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) #define NO_INORD ((uint)-1)
...@@ -146,7 +160,7 @@ struct entry { ...@@ -146,7 +160,7 @@ struct entry {
void requestOutputIndex(cpool& cp, int req = REQUESTED); void requestOutputIndex(cpool& cp, int req = REQUESTED);
int getOutputIndex() { int getOutputIndex() {
assert(outputIndex > NOT_REQUESTED); assert(outputIndex > REQUESTED_NONE);
return outputIndex; return outputIndex;
} }
...@@ -167,12 +181,12 @@ struct entry { ...@@ -167,12 +181,12 @@ struct entry {
} }
entry* memberClass() { entry* memberClass() {
assert(tagMatches(CONSTANT_Member)); assert(tagMatches(CONSTANT_AnyMember));
return ref(0); return ref(0);
} }
entry* memberDescr() { entry* memberDescr() {
assert(tagMatches(CONSTANT_Member)); assert(tagMatches(CONSTANT_AnyMember));
return ref(1); return ref(1);
} }
...@@ -199,9 +213,9 @@ struct entry { ...@@ -199,9 +213,9 @@ struct entry {
return (tag2 == tag) return (tag2 == tag)
|| (tag2 == CONSTANT_Utf8 && tag == CONSTANT_Signature) || (tag2 == CONSTANT_Utf8 && tag == CONSTANT_Signature)
#ifndef PRODUCT #ifndef PRODUCT
|| (tag2 == CONSTANT_Literal || (tag2 == CONSTANT_FieldSpecific
&& tag >= CONSTANT_Integer && tag <= CONSTANT_String && tag != CONSTANT_Class) && tag >= CONSTANT_Integer && tag <= CONSTANT_String && tag != CONSTANT_Class)
|| (tag2 == CONSTANT_Member || (tag2 == CONSTANT_AnyMember
&& tag >= CONSTANT_Fieldref && tag <= CONSTANT_InterfaceMethodref) && tag >= CONSTANT_Fieldref && tag <= CONSTANT_InterfaceMethodref)
#endif #endif
; ;
...@@ -309,6 +323,7 @@ void unpacker::free() { ...@@ -309,6 +323,7 @@ void unpacker::free() {
code_fixup_offset.free(); code_fixup_offset.free();
code_fixup_source.free(); code_fixup_source.free();
requested_ics.free(); requested_ics.free();
cp.requested_bsms.free();
cur_classfile_head.free(); cur_classfile_head.free();
cur_classfile_tail.free(); cur_classfile_tail.free();
for (i = 0; i < ATTR_CONTEXT_LIMIT; i++) for (i = 0; i < ATTR_CONTEXT_LIMIT; i++)
...@@ -448,12 +463,12 @@ maybe_inline ...@@ -448,12 +463,12 @@ maybe_inline
int unpacker::putref_index(entry* e, int size) { int unpacker::putref_index(entry* e, int size) {
if (e == null) if (e == null)
return 0; return 0;
else if (e->outputIndex > NOT_REQUESTED) else if (e->outputIndex > REQUESTED_NONE)
return e->outputIndex; return e->outputIndex;
else if (e->tag == CONSTANT_Signature) else if (e->tag == CONSTANT_Signature)
return putref_index(e->ref(0), size); return putref_index(e->ref(0), size);
else { else {
e->requestOutputIndex(cp, -size); e->requestOutputIndex(cp, (size == 1 ? REQUESTED_LDC : REQUESTED));
// Later on we'll fix the bits. // Later on we'll fix the bits.
class_fixup_type.addByte(size); class_fixup_type.addByte(size);
class_fixup_offset.add((int)wpoffset()); class_fixup_offset.add((int)wpoffset());
...@@ -515,28 +530,33 @@ void unpacker::saveTo(bytes& b, byte* ptr, size_t len) { ...@@ -515,28 +530,33 @@ void unpacker::saveTo(bytes& b, byte* ptr, size_t len) {
b.copyFrom(ptr, len); b.copyFrom(ptr, len);
} }
bool testBit(int archive_options, int bitMask) {
return (archive_options & bitMask) != 0;
}
// Read up through band_headers. // Read up through band_headers.
// Do the archive_size dance to set the size of the input mega-buffer. // Do the archive_size dance to set the size of the input mega-buffer.
void unpacker::read_file_header() { void unpacker::read_file_header() {
// Read file header to determine file type and total size. // Read file header to determine file type and total size.
enum { enum {
MAGIC_BYTES = 4, 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_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: // Length contributions from optional header fields:
AH_FILE_HEADER_LEN = 5, // sizehi/lo/next/modtime/files AH_LENGTH_S = 2, // archive_header_S = optional {size_hi, size_lo}
AH_ARCHIVE_SIZE_LEN = 2, // sizehi/lo only; part of AH_FILE_HEADER_LEN AH_ARCHIVE_SIZE_HI = 0, // offset in archive_header_S
AH_CP_NUMBER_LEN = 4, // int/float/long/double AH_ARCHIVE_SIZE_LO = 1, // offset in archive_header_S
AH_SPECIAL_FORMAT_LEN = 2, // layouts/band-headers AH_FILE_HEADER_LEN = 5, // file_counts = {{size_hi, size_lo), next, modtile, files}
AH_LENGTH_MIN = AH_LENGTH AH_SPECIAL_FORMAT_LEN = 2, // special_count = {layouts, band_headers}
-(AH_FILE_HEADER_LEN+AH_SPECIAL_FORMAT_LEN+AH_CP_NUMBER_LEN), AH_CP_NUMBER_LEN = 4, // cp_number_counts = {int, float, long, double}
ARCHIVE_SIZE_MIN = AH_LENGTH_MIN - (AH_LENGTH_0 + AH_ARCHIVE_SIZE_LEN), 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 FIRST_READ = MAGIC_BYTES + AH_LENGTH_MIN
}; };
assert(AH_LENGTH_MIN == 15); // # of UNSIGNED5 fields required after archive_magic 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], // 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. // 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.) // (Note that archive_size is optional; it may be 0..10 bytes in length.)
...@@ -622,23 +642,32 @@ void unpacker::read_file_header() { ...@@ -622,23 +642,32 @@ void unpacker::read_file_header() {
// Read the first 3 values from the header. // Read the first 3 values from the header.
value_stream hdr; value_stream hdr;
int hdrVals = 0; int hdrVals = 0;
int hdrValsSkipped = 0; // debug only int hdrValsSkipped = 0; // for assert
hdr.init(rp, rplimit, UNSIGNED5_spec); hdr.init(rp, rplimit, UNSIGNED5_spec);
minver = hdr.getInt(); minver = hdr.getInt();
majver = hdr.getInt(); majver = hdr.getInt();
hdrVals += 2; hdrVals += 2;
if (magic != (int)JAVA_PACKAGE_MAGIC || int majmin[3][2] = {
(majver != JAVA5_PACKAGE_MAJOR_VERSION && {JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION},
majver != JAVA6_PACKAGE_MAJOR_VERSION) || {JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION},
(minver != JAVA5_PACKAGE_MINOR_VERSION && {JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION}
minver != JAVA6_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]; char message[200];
sprintf(message, "@" ERROR_FORMAT ": magic/ver = " 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, magic, majver, minver,
JAVA_PACKAGE_MAGIC, JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION, 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); abort(message);
} }
CHECK; CHECK;
...@@ -646,18 +675,26 @@ void unpacker::read_file_header() { ...@@ -646,18 +675,26 @@ void unpacker::read_file_header() {
archive_options = hdr.getInt(); archive_options = hdr.getInt();
hdrVals += 1; hdrVals += 1;
assert(hdrVals == AH_LENGTH_0); // first three fields only assert(hdrVals == AH_LENGTH_0); // first three fields only
bool haveSizeHi = testBit(archive_options, AO_HAVE_FILE_SIZE_HI);
#define ORBIT(bit) |(bit) bool haveModTime = testBit(archive_options, AO_HAVE_FILE_MODTIME);
int OPTION_LIMIT = (0 ARCHIVE_BIT_DO(ORBIT)); bool haveFileOpt = testBit(archive_options, AO_HAVE_FILE_OPTIONS);
#undef ORBIT
if ((archive_options & ~OPTION_LIMIT) != 0) { bool haveSpecial = testBit(archive_options, AO_HAVE_SPECIAL_FORMATS);
fprintf(errstrm, "Warning: Illegal archive options 0x%x\n", bool haveFiles = testBit(archive_options, AO_HAVE_FILE_HEADERS);
archive_options); bool haveNumbers = testBit(archive_options, AO_HAVE_CP_NUMBERS);
abort("illegal archive options"); 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; return;
} }
if (haveFiles) {
if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) {
uint hi = hdr.getInt(); uint hi = hdr.getInt();
uint lo = hdr.getInt(); uint lo = hdr.getInt();
julong x = band::makeLong(hi, lo); julong x = band::makeLong(hi, lo);
...@@ -738,13 +775,23 @@ void unpacker::read_file_header() { ...@@ -738,13 +775,23 @@ void unpacker::read_file_header() {
return; return;
} }
// read the rest of the header fields // read the rest of the header fields int assertSkipped = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S;
ensure_input((AH_LENGTH-AH_LENGTH_0) * B_MAX); 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; CHECK;
hdr.rp = rp; hdr.rp = rp;
hdr.rplimit = rplimit; hdr.rplimit = rplimit;
if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) { if (haveFiles) {
archive_next_count = hdr.getInt(); archive_next_count = hdr.getInt();
CHECK_COUNT(archive_next_count); CHECK_COUNT(archive_next_count);
archive_modtime = hdr.getInt(); archive_modtime = hdr.getInt();
...@@ -755,7 +802,7 @@ void unpacker::read_file_header() { ...@@ -755,7 +802,7 @@ void unpacker::read_file_header() {
hdrValsSkipped += 3; hdrValsSkipped += 3;
} }
if ((archive_options & AO_HAVE_SPECIAL_FORMATS) != 0) { if (haveSpecial) {
band_headers_size = hdr.getInt(); band_headers_size = hdr.getInt();
CHECK_COUNT(band_headers_size); CHECK_COUNT(band_headers_size);
attr_definition_count = hdr.getInt(); attr_definition_count = hdr.getInt();
...@@ -767,7 +814,7 @@ void unpacker::read_file_header() { ...@@ -767,7 +814,7 @@ void unpacker::read_file_header() {
int cp_counts[N_TAGS_IN_ORDER]; int cp_counts[N_TAGS_IN_ORDER];
for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) { 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]) { switch (TAGS_IN_ORDER[k]) {
case CONSTANT_Integer: case CONSTANT_Integer:
case CONSTANT_Float: case CONSTANT_Float:
...@@ -778,6 +825,17 @@ void unpacker::read_file_header() { ...@@ -778,6 +825,17 @@ void unpacker::read_file_header() {
continue; 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(); cp_counts[k] = hdr.getInt();
CHECK_COUNT(cp_counts[k]); CHECK_COUNT(cp_counts[k]);
hdrVals += 1; hdrVals += 1;
...@@ -791,36 +849,26 @@ void unpacker::read_file_header() { ...@@ -791,36 +849,26 @@ void unpacker::read_file_header() {
CHECK_COUNT(class_count); CHECK_COUNT(class_count);
hdrVals += 4; hdrVals += 4;
// done with archive_header // done with archive_header, time to reconcile to ensure
// we have read everything correctly
hdrVals += hdrValsSkipped; hdrVals += hdrValsSkipped;
assert(hdrVals == AH_LENGTH); 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; rp = hdr.rp;
if (rp > rplimit) if (rp > rplimit)
abort("EOF reading archive header"); abort("EOF reading archive header");
// Now size the CP. // Now size the CP.
#ifndef PRODUCT #ifndef PRODUCT
bool x = (N_TAGS_IN_ORDER == cpool::NUM_COUNTS); // bool x = (N_TAGS_IN_ORDER == CONSTANT_Limit);
assert(x); // assert(x);
#endif //PRODUCT #endif //PRODUCT
cp.init(this, cp_counts); cp.init(this, cp_counts);
CHECK; CHECK;
default_file_modtime = archive_modtime; 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 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; default_file_options |= FO_DEFLATE_HINT;
// meta-bytes, if any, immediately follow archive header // meta-bytes, if any, immediately follow archive header
...@@ -876,7 +924,7 @@ void unpacker::finish() { ...@@ -876,7 +924,7 @@ void unpacker::finish() {
// Cf. PackageReader.readConstantPoolCounts // Cf. PackageReader.readConstantPoolCounts
void cpool::init(unpacker* u_, int counts[NUM_COUNTS]) { void cpool::init(unpacker* u_, int counts[CONSTANT_Limit]) {
this->u = u_; this->u = u_;
// Fill-pointer for CP. // Fill-pointer for CP.
...@@ -924,13 +972,16 @@ void cpool::init(unpacker* u_, int counts[NUM_COUNTS]) { ...@@ -924,13 +972,16 @@ void cpool::init(unpacker* u_, int counts[NUM_COUNTS]) {
first_extra_entry = &entries[nentries]; first_extra_entry = &entries[nentries];
// Initialize the standard indexes. // Initialize the standard indexes.
tag_count[CONSTANT_All] = nentries;
tag_base[ CONSTANT_All] = 0;
for (int tag = 0; tag < CONSTANT_Limit; tag++) { for (int tag = 0; tag < CONSTANT_Limit; tag++) {
entry* cpMap = &entries[tag_base[tag]]; entry* cpMap = &entries[tag_base[tag]];
tag_index[tag].init(tag_count[tag], cpMap, 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. // Initialize hashTab to a generous power-of-two size.
uint pow2 = 1; uint pow2 = 1;
uint target = maxentries + maxentries/2; // 60% full uint target = maxentries + maxentries/2; // 60% full
...@@ -1281,6 +1332,70 @@ void unpacker::read_signature_values(entry* cpMap, int len) { ...@@ -1281,6 +1332,70 @@ void unpacker::read_signature_values(entry* cpMap, int len) {
//cp_Signature_classes.done(); //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 // Cf. PackageReader.readConstantPool
void unpacker::read_cp() { void unpacker::read_cp() {
byte* rp0 = rp; byte* rp0 = rp;
...@@ -1298,6 +1413,14 @@ void unpacker::read_cp() { ...@@ -1298,6 +1413,14 @@ void unpacker::read_cp() {
cpMap[i].tag = tag; cpMap[i].tag = tag;
cpMap[i].inord = i; 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) { switch (tag) {
case CONSTANT_Utf8: case CONSTANT_Utf8:
...@@ -1344,19 +1467,27 @@ void unpacker::read_cp() { ...@@ -1344,19 +1467,27 @@ void unpacker::read_cp() {
CONSTANT_Class, CONSTANT_NameandType, CONSTANT_Class, CONSTANT_NameandType,
cpMap, len); cpMap, len);
break; 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: default:
assert(false); assert(false);
break; 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; CHECK;
} }
...@@ -1791,7 +1922,12 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res, ...@@ -1791,7 +1922,12 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res,
case 'F': ixTag = CONSTANT_Float; break; case 'F': ixTag = CONSTANT_Float; break;
case 'D': ixTag = CONSTANT_Double; break; case 'D': ixTag = CONSTANT_Double; break;
case 'S': ixTag = CONSTANT_String; 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 { } else {
switch (*lp++) { switch (*lp++) {
...@@ -1803,6 +1939,11 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res, ...@@ -1803,6 +1939,11 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res,
case 'I': ixTag = CONSTANT_InterfaceMethodref; break; case 'I': ixTag = CONSTANT_InterfaceMethodref; break;
case 'U': ixTag = CONSTANT_Utf8; break; //utf8_ref case 'U': ixTag = CONSTANT_Utf8; break; //utf8_ref
case 'Q': ixTag = CONSTANT_All; break; //untyped_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) { if (ixTag == CONSTANT_None) {
...@@ -1873,13 +2014,13 @@ void unpacker::read_attr_defs() { ...@@ -1873,13 +2014,13 @@ void unpacker::read_attr_defs() {
// Decide whether bands for the optional high flag words are present. // Decide whether bands for the optional high flag words are present.
attr_defs[ATTR_CONTEXT_CLASS] 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] 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] 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] 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. // Set up built-in attrs.
// (The simple ones are hard-coded. The metadata layouts are not.) // (The simple ones are hard-coded. The metadata layouts are not.)
...@@ -2579,7 +2720,7 @@ void unpacker::putlayout(band** body) { ...@@ -2579,7 +2720,7 @@ void unpacker::putlayout(band** body) {
// It has data, so unparse an element. // It has data, so unparse an element.
if (b.ixTag != CONSTANT_None) { if (b.ixTag != CONSTANT_None) {
assert(le_kind == EK_REF); assert(le_kind == EK_REF);
if (b.ixTag == CONSTANT_Literal) if (b.ixTag == CONSTANT_FieldSpecific)
e = b.getRefUsing(cp.getKQIndex()); e = b.getRefUsing(cp.getKQIndex());
else else
e = b.getRefN(); e = b.getRefN();
...@@ -2653,13 +2794,13 @@ void unpacker::putlayout(band** body) { ...@@ -2653,13 +2794,13 @@ void unpacker::putlayout(band** body) {
void unpacker::read_files() { void unpacker::read_files() {
file_name.readData(file_count); 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_hi.readData(file_count);
file_size_lo.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); file_modtime.readData(file_count);
int allFiles = file_count + class_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); file_options.readData(file_count);
// FO_IS_CLASS_STUB might be set, causing overlap between classes and files // FO_IS_CLASS_STUB might be set, causing overlap between classes and files
for (int i = 0; i < file_count; i++) { for (int i = 0; i < file_count; i++) {
...@@ -2703,7 +2844,7 @@ void unpacker::get_code_header(int& max_stack, ...@@ -2703,7 +2844,7 @@ void unpacker::get_code_header(int& max_stack,
max_stack = sc % mod; max_stack = sc % mod;
max_na_locals = sc / mod; // caller must add static, siglen max_na_locals = sc / mod; // caller must add static, siglen
handler_count = nh; handler_count = nh;
if ((archive_options & AO_HAVE_ALL_CODE_FLAGS) != 0) if (testBit(archive_options, AO_HAVE_ALL_CODE_FLAGS))
cflags = -1; cflags = -1;
else else
cflags = 0; // this one has no attributes cflags = 0; // this one has no attributes
...@@ -2777,12 +2918,14 @@ band* unpacker::ref_band_for_op(int bc) { ...@@ -2777,12 +2918,14 @@ band* unpacker::ref_band_for_op(int bc) {
return &bc_longref; return &bc_longref;
case bc_dldc2_w: case bc_dldc2_w:
return &bc_doubleref; return &bc_doubleref;
case bc_aldc: case bc_sldc:
case bc_aldc_w: case bc_sldc_w:
return &bc_stringref; return &bc_stringref;
case bc_cldc: case bc_cldc:
case bc_cldc_w: case bc_cldc_w:
return &bc_classref; return &bc_classref;
case bc_qldc: case bc_qldc_w:
return &bc_loadablevalueref;
case bc_getstatic: case bc_getstatic:
case bc_putstatic: case bc_putstatic:
...@@ -2796,6 +2939,8 @@ band* unpacker::ref_band_for_op(int bc) { ...@@ -2796,6 +2939,8 @@ band* unpacker::ref_band_for_op(int bc) {
return &bc_methodref; return &bc_methodref;
case bc_invokeinterface: case bc_invokeinterface:
return &bc_imethodref; return &bc_imethodref;
case bc_invokedynamic:
return &bc_indyref;
case bc_new: case bc_new:
case bc_anewarray: case bc_anewarray:
...@@ -3131,6 +3276,71 @@ void cpool::expandSignatures() { ...@@ -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() { void cpool::initMemberIndexes() {
// This function does NOT refer to any class schema. // This function does NOT refer to any class schema.
// It is totally internal to the cpool. // It is totally internal to the cpool.
...@@ -3238,13 +3448,13 @@ void cpool::initMemberIndexes() { ...@@ -3238,13 +3448,13 @@ void cpool::initMemberIndexes() {
} }
void entry::requestOutputIndex(cpool& cp, int req) { 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) { if (tag == CONSTANT_Signature) {
ref(0)->requestOutputIndex(cp, req); ref(0)->requestOutputIndex(cp, req);
return; return;
} }
assert(req == REQUESTED || req == REQUESTED_LDC); assert(req == REQUESTED || req == REQUESTED_LDC);
if (outputIndex != NOT_REQUESTED) { if (outputIndex != REQUESTED_NONE) {
if (req == REQUESTED_LDC) if (req == REQUESTED_LDC)
outputIndex = req; // this kind has precedence outputIndex = req; // this kind has precedence
return; return;
...@@ -3252,31 +3462,52 @@ void entry::requestOutputIndex(cpool& cp, int req) { ...@@ -3252,31 +3462,52 @@ void entry::requestOutputIndex(cpool& cp, int req) {
outputIndex = req; outputIndex = req;
//assert(!cp.outputEntries.contains(this)); //assert(!cp.outputEntries.contains(this));
assert(tag != CONSTANT_Signature); 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++) { for (int j = 0; j < nrefs; j++) {
ref(j)->requestOutputIndex(cp); ref(j)->requestOutputIndex(cp);
} }
} }
void cpool::resetOutputIndexes() { 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(); entry** oes = (entry**) outputEntries.base();
for (i = 0; i < noes; i++) { for (int i = 0 ; i < noes ; i++) {
entry& e = *oes[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; outputIndexLimit = 0;
outputEntries.empty(); outputEntries.empty();
#ifndef PRODUCT #ifndef PRODUCT
// they must all be clear now // ensure things are cleared out
for (i = 0; i < (int)nentries; i++) for (int i = 0; i < (int)maxentries; i++)
assert(entries[i].outputIndex == NOT_REQUESTED); assert(entries[i].outputIndex == REQUESTED_NONE);
#endif #endif
} }
static const byte TAG_ORDER[CONSTANT_Limit] = { 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" extern "C"
...@@ -3323,10 +3554,18 @@ void cpool::computeOutputIndexes() { ...@@ -3323,10 +3554,18 @@ void cpool::computeOutputIndexes() {
if (nentries > 100) checkStep = nentries / 100; if (nentries > 100) checkStep = nentries / 100;
for (i = (int)(checkStart++ % checkStep); i < (int)nentries; i += checkStep) { for (i = (int)(checkStart++ % checkStep); i < (int)nentries; i += checkStep) {
entry& e = entries[i]; entry& e = entries[i];
if (e.outputIndex != NOT_REQUESTED) { if (e.tag == CONSTANT_BootstrapMethod) {
assert(outputEntries.contains(&e)); if (e.outputIndex != REQUESTED_NONE) {
assert(requested_bsms.contains(&e));
} else {
assert(!requested_bsms.contains(&e));
}
} else { } 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() { ...@@ -3348,7 +3587,7 @@ void cpool::computeOutputIndexes() {
int nextIndex = 1; // always skip index #0 in output cpool int nextIndex = 1; // always skip index #0 in output cpool
for (i = 0; i < noes; i++) { for (i = 0; i < noes; i++) {
entry& e = *oes[i]; entry& e = *oes[i];
assert(e.outputIndex == REQUESTED || e.outputIndex == REQUESTED_LDC); assert(e.outputIndex >= REQUESTED_LDC);
e.outputIndex = nextIndex++; e.outputIndex = nextIndex++;
if (e.isDoubleWord()) nextIndex++; // do not use the next index if (e.isDoubleWord()) nextIndex++; // do not use the next index
} }
...@@ -3396,7 +3635,7 @@ char* entry::string() { ...@@ -3396,7 +3635,7 @@ char* entry::string() {
default: default:
if (nrefs == 0) { if (nrefs == 0) {
buf = getbuf(20); buf = getbuf(20);
sprintf((char*)buf.ptr, "<tag=%d>", tag); sprintf((char*)buf.ptr, TAG_NAME[tag]);
} else if (nrefs == 1) { } else if (nrefs == 1) {
return refs[0]->string(); return refs[0]->string();
} else { } else {
...@@ -3674,6 +3913,7 @@ void unpacker::reset_cur_classfile() { ...@@ -3674,6 +3913,7 @@ void unpacker::reset_cur_classfile() {
class_fixup_offset.empty(); class_fixup_offset.empty();
class_fixup_ref.empty(); class_fixup_ref.empty();
requested_ics.empty(); requested_ics.empty();
cp.requested_bsms.empty();
} }
cpindex* cpool::getKQIndex() { cpindex* cpool::getKQIndex() {
...@@ -3931,13 +4171,15 @@ void unpacker::write_bc_ops() { ...@@ -3931,13 +4171,15 @@ void unpacker::write_bc_ops() {
case bc_ildc: case bc_ildc:
case bc_cldc: case bc_cldc:
case bc_fldc: case bc_fldc:
case bc_aldc: case bc_sldc:
case bc_qldc:
origBC = bc_ldc; origBC = bc_ldc;
break; break;
case bc_ildc_w: case bc_ildc_w:
case bc_cldc_w: case bc_cldc_w:
case bc_fldc_w: case bc_fldc_w:
case bc_aldc_w: case bc_sldc_w:
case bc_qldc_w:
origBC = bc_ldc_w; origBC = bc_ldc_w;
break; break;
case bc_lldc2_w: case bc_lldc2_w:
...@@ -3962,6 +4204,10 @@ void unpacker::write_bc_ops() { ...@@ -3962,6 +4204,10 @@ void unpacker::write_bc_ops() {
int argSize = ref->memberDescr()->descrType()->typeSize(); int argSize = ref->memberDescr()->descrType()->typeSize();
putu1_fast(1 + argSize); putu1_fast(1 + argSize);
putu1_fast(0); putu1_fast(0);
} else if (origBC == bc_invokedynamic) {
// pad the next two byte
putu1_fast(0);
putu1_fast(0);
} }
continue; continue;
} }
...@@ -4353,49 +4599,12 @@ int raw_address_cmp(const void* p1p, const void* p2p) { ...@@ -4353,49 +4599,12 @@ int raw_address_cmp(const void* p1p, const void* p2p) {
return (p1 > p2)? 1: (p1 < p2)? -1: 0; return (p1 > p2)? 1: (p1 < p2)? -1: 0;
} }
void unpacker::write_classfile_tail() { /*
cur_classfile_tail.empty(); * writes the InnerClass attributes and returns the updated attribute
set_output(&cur_classfile_tail); */
int unpacker::write_ics(int naOffset, int na) {
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:
#ifdef ASSERT #ifdef ASSERT
for (i = 0; i < ic_count; i++) { for (int i = 0; i < ic_count; i++) {
assert(!ics[i].requested); assert(!ics[i].requested);
} }
#endif #endif
...@@ -4416,7 +4625,7 @@ void unpacker::write_classfile_tail() { ...@@ -4416,7 +4625,7 @@ void unpacker::write_classfile_tail() {
// include it and all its outers. // include it and all its outers.
int noes = cp.outputEntries.length(); int noes = cp.outputEntries.length();
entry** oes = (entry**) cp.outputEntries.base(); entry** oes = (entry**) cp.outputEntries.base();
for (i = 0; i < noes; i++) { for (int i = 0; i < noes; i++) {
entry& e = *oes[i]; entry& e = *oes[i];
if (e.tag != CONSTANT_Class) continue; // wrong sort if (e.tag != CONSTANT_Class) continue; // wrong sort
for (inner_class* ic = cp.getIC(&e); for (inner_class* ic = cp.getIC(&e);
...@@ -4442,10 +4651,10 @@ void unpacker::write_classfile_tail() { ...@@ -4442,10 +4651,10 @@ void unpacker::write_classfile_tail() {
// Note: extra_ics will be freed up by next call to get_next_file(). // 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]; inner_class& extra_ic = extra_ics[i];
extra_ic.inner = class_InnerClasses_RC.getRef(); extra_ic.inner = class_InnerClasses_RC.getRef();
CHECK; CHECK_0;
// Find the corresponding equivalent global IC: // Find the corresponding equivalent global IC:
inner_class* global_ic = cp.getIC(extra_ic.inner); inner_class* global_ic = cp.getIC(extra_ic.inner);
int flags = class_InnerClasses_F.getInt(); int flags = class_InnerClasses_F.getInt();
...@@ -4493,7 +4702,7 @@ void unpacker::write_classfile_tail() { ...@@ -4493,7 +4702,7 @@ void unpacker::write_classfile_tail() {
putu2(local_ics); putu2(local_ics);
PTRLIST_QSORT(requested_ics, raw_address_cmp); PTRLIST_QSORT(requested_ics, raw_address_cmp);
int num_global_ics = requested_ics.length(); 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; inner_class* ic;
if (i < 0) if (i < 0)
ic = (inner_class*) requested_ics.get(num_global_ics+i); ic = (inner_class*) requested_ics.get(num_global_ics+i);
...@@ -4512,17 +4721,99 @@ void unpacker::write_classfile_tail() { ...@@ -4512,17 +4721,99 @@ void unpacker::write_classfile_tail() {
} }
// Tidy up global 'requested' bits: // 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); inner_class* ic = (inner_class*) requested_ics.get(i);
ic->requested = false; ic->requested = false;
} }
requested_ics.empty(); 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; 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(); close_output();
cp.computeOutputIndexes();
// rewrite CP references in the tail // rewrite CP references in the tail
cp.computeOutputIndexes();
int nextref = 0; int nextref = 0;
for (i = 0; i < (int)class_fixup_type.size(); i++) { for (i = 0; i < (int)class_fixup_type.size(); i++) {
int type = class_fixup_type.getByte(i); int type = class_fixup_type.getByte(i);
...@@ -4579,9 +4870,18 @@ void unpacker::write_classfile_head() { ...@@ -4579,9 +4870,18 @@ void unpacker::write_classfile_head() {
case CONSTANT_Methodref: case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref: case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType: case CONSTANT_NameandType:
case CONSTANT_InvokeDynamic:
putu2(e.refs[0]->getOutputIndex()); putu2(e.refs[0]->getOutputIndex());
putu2(e.refs[1]->getOutputIndex()); putu2(e.refs[1]->getOutputIndex());
break; 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: default:
abort(ERROR_INTERNAL); abort(ERROR_INTERNAL);
} }
...@@ -4620,11 +4920,11 @@ unpacker::file* unpacker::get_next_file() { ...@@ -4620,11 +4920,11 @@ unpacker::file* unpacker::get_next_file() {
entry* e = file_name.getRef(); entry* e = file_name.getRef();
CHECK_0; CHECK_0;
cur_file.name = e->utf8String(); 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); 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 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; cur_file.options |= file_options.getInt() & ~suppress_file_options;
} else if (classes_written < class_count) { } else if (classes_written < class_count) {
// there is a class for a missing file record // there is a class for a missing file record
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -22,9 +22,6 @@ ...@@ -22,9 +22,6 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
// Global Structures // Global Structures
struct jar; struct jar;
struct gunzip; struct gunzip;
...@@ -70,6 +67,9 @@ struct cpool { ...@@ -70,6 +67,9 @@ struct cpool {
cpindex tag_index[CONSTANT_Limit]; cpindex tag_index[CONSTANT_Limit];
ptrlist tag_extras[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* member_indexes; // indexed by 2*CONSTANT_Class.inord
cpindex* getFieldIndex(entry* classRef); cpindex* getFieldIndex(entry* classRef);
cpindex* getMethodIndex(entry* classRef); cpindex* getMethodIndex(entry* classRef);
...@@ -82,6 +82,7 @@ struct cpool { ...@@ -82,6 +82,7 @@ struct cpool {
int outputIndexLimit; // index limit after renumbering int outputIndexLimit; // index limit after renumbering
ptrlist outputEntries; // list of entry* needing output idx assigned ptrlist outputEntries; // list of entry* needing output idx assigned
ptrlist requested_bsms; // which bsms need output?
entry** hashTab; entry** hashTab;
uint hashTabLength; uint hashTabLength;
...@@ -100,24 +101,36 @@ struct cpool { ...@@ -100,24 +101,36 @@ struct cpool {
entry* sym[s_LIMIT]; entry* sym[s_LIMIT];
// read counts from hdr, allocate main arrays // read counts from hdr, allocate main arrays
enum { NUM_COUNTS = 12 }; void init(unpacker* u, int counts[CONSTANT_Limit]);
void init(unpacker* u, int counts[NUM_COUNTS]);
// pointer to outer unpacker, for error checks etc. // pointer to outer unpacker, for error checks etc.
unpacker* u; unpacker* u;
int getCount(byte tag) { int getCount(byte tag) {
assert((uint)tag < CONSTANT_Limit); if ((uint)tag >= CONSTANT_GroupFirst) {
return tag_count[tag]; 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) { cpindex* getIndex(byte tag) {
assert((uint)tag < CONSTANT_Limit); if ((uint)tag >= CONSTANT_GroupFirst) {
return &tag_index[tag]; 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 cpindex* getKQIndex(); // uses cur_descr
void expandSignatures(); void expandSignatures();
void initGroupIndexes();
void initMemberIndexes(); void initMemberIndexes();
int initLoadableValues(entry** loadable_entries);
void computeOutputOrder(); void computeOutputOrder();
void computeOutputIndexes(); void computeOutputIndexes();
...@@ -234,6 +247,7 @@ struct unpacker { ...@@ -234,6 +247,7 @@ struct unpacker {
int cur_descr_flags; // flags corresponding to cur_descr int cur_descr_flags; // flags corresponding to cur_descr
int cur_class_minver, cur_class_majver; int cur_class_minver, cur_class_majver;
bool cur_class_has_local_ics; bool cur_class_has_local_ics;
int cur_class_local_bsm_count;
fillbytes cur_classfile_head; fillbytes cur_classfile_head;
fillbytes cur_classfile_tail; fillbytes cur_classfile_tail;
int files_written; // also tells which file we're working on int files_written; // also tells which file we're working on
...@@ -412,7 +426,7 @@ struct unpacker { ...@@ -412,7 +426,7 @@ struct unpacker {
void abort(const char* s = null); void abort(const char* s = null);
bool aborting() { return abort_message != null; } bool aborting() { return abort_message != null; }
static unpacker* current(); // find current instance static unpacker* current(); // find current instance
void checkLegacy(const char* name);
// Output management // Output management
void set_output(fillbytes* which) { void set_output(fillbytes* which) {
assert(wp == null); assert(wp == null);
...@@ -464,6 +478,8 @@ struct unpacker { ...@@ -464,6 +478,8 @@ struct unpacker {
void write_bc_ops(); void write_bc_ops();
void write_members(int num, int attrc); // attrc=ATTR_CONTEXT_FIELD/METHOD void write_members(int num, int attrc); // attrc=ATTR_CONTEXT_FIELD/METHOD
int write_attrs(int attrc, julong indexBits); int write_attrs(int attrc, julong indexBits);
int write_ics(int naOffset, int na);
int write_bsms(int naOffset, int na);
// The readers // The readers
void read_bands(); void read_bands();
...@@ -484,6 +500,9 @@ struct unpacker { ...@@ -484,6 +500,9 @@ struct unpacker {
void read_single_refs(band& cp_band, byte refTag, entry* cpMap, int len); 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_double_refs(band& cp_band, byte ref1Tag, byte ref2Tag, entry* cpMap, int len);
void read_signature_values(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); } inline void cpool::abort(const char* msg) { u->abort(msg); }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -27,7 +27,7 @@ import java.util.Arrays; ...@@ -27,7 +27,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
/* /*
* @test * @test
* @bug 6982312 * @bug 6746111
* @summary tests various classfile format and attribute handling by pack200 * @summary tests various classfile format and attribute handling by pack200
* @compile -XDignore.symbol.file Utils.java AttributeTests.java * @compile -XDignore.symbol.file Utils.java AttributeTests.java
* @run main AttributeTests * @run main AttributeTests
...@@ -36,40 +36,8 @@ import java.util.List; ...@@ -36,40 +36,8 @@ import java.util.List;
public class AttributeTests { public class AttributeTests {
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
test6982312();
test6746111(); 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<String> cmds = new ArrayList<String>();
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 * this test checks to see if we get the expected strings for output
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -51,6 +51,9 @@ public class PackageVersionTest { ...@@ -51,6 +51,9 @@ public class PackageVersionTest {
public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160; public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160;
public final static int JAVA6_PACKAGE_MINOR_VERSION = 1; 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) { public static void main(String... args) {
if (!javaHome.getName().endsWith("jre")) { if (!javaHome.getName().endsWith("jre")) {
throw new RuntimeException("Error: requires an SDK to run"); throw new RuntimeException("Error: requires an SDK to run");
...@@ -68,9 +71,8 @@ public class PackageVersionTest { ...@@ -68,9 +71,8 @@ public class PackageVersionTest {
verifyPack("Test6.class", JAVA6_PACKAGE_MAJOR_VERSION, verifyPack("Test6.class", JAVA6_PACKAGE_MAJOR_VERSION,
JAVA6_PACKAGE_MINOR_VERSION); JAVA6_PACKAGE_MINOR_VERSION);
// TODO: change this to the java7 package version as needed. verifyPack("Test7.class", JAVA7_PACKAGE_MAJOR_VERSION,
verifyPack("Test7.class", JAVA6_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION);
JAVA6_PACKAGE_MINOR_VERSION);
// test for resource file, ie. no class files // test for resource file, ie. no class files
verifyPack("Test6.java", JAVA5_PACKAGE_MAJOR_VERSION, verifyPack("Test6.java", JAVA5_PACKAGE_MAJOR_VERSION,
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -129,8 +129,10 @@ class Utils { ...@@ -129,8 +129,10 @@ class Utils {
init(); init();
List<String> cmds = new ArrayList<String>(); List<String> cmds = new ArrayList<String>();
cmds.add(getJavaCmd()); cmds.add(getJavaCmd());
cmds.add("-jar"); cmds.add("-cp");
cmds.add(VerifierJar.getName()); 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(reference.getAbsolutePath());
cmds.add(specimen.getAbsolutePath()); cmds.add(specimen.getAbsolutePath());
cmds.add("-O"); cmds.add("-O");
...@@ -142,8 +144,10 @@ class Utils { ...@@ -142,8 +144,10 @@ class Utils {
init(); init();
List<String> cmds = new ArrayList<String>(); List<String> cmds = new ArrayList<String>();
cmds.add(getJavaCmd()); cmds.add(getJavaCmd());
cmds.add("-jar"); cmds.add("-cp");
cmds.add(VerifierJar.getName()); 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(reference.getName());
cmds.add(specimen.getName()); cmds.add(specimen.getName());
cmds.add("-O"); cmds.add("-O");
......
...@@ -2,19 +2,19 @@ The files contained in the golden.jar have been harvested from many ...@@ -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), different sources, some are hand-crafted invalid class files (odds directory),
or from random JDK builds. or from random JDK builds.
Generally these files serve to ensure the integrity of the packer and unpacker Generally these files serve to ensure the integrity of the packer and
by, unpacker by,
1. maximizing the test coverage. * maximizing the test coverage.
2. exercising all the Bands in the pack200 specification. * exercising all the Bands in the pack200 specification.
2. testing the behavior of the packer with invalid classes. * testing the behavior of the packer with invalid classes.
3. testing the archive integrity, ordering and description (date, sizes, * testing the archive integrity, ordering and description (date, sizes,
CRC etc.) CRC etc.)
Build: Build:
To rebuild this JAR follow these steps: To rebuild this JAR follow these steps:
1. unzip the golden.jar to some directory lets call it "example" 1. unzip the golden.jar to some directory lets call it "example"
2. now we can add any directories with files into 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 % sh BUILDME.sh example
Note: the BUILDME.sh is known to work on all Unix platforms as well as Windows Note: the BUILDME.sh is known to work on all Unix platforms as well as Windows
...@@ -32,7 +32,7 @@ Test: ...@@ -32,7 +32,7 @@ Test:
Basic: Basic:
% pack200 --repack test.jar golden.jar % pack200 --repack test.jar golden.jar
Advanced: Advanced: inspection of band contents
Create a pack.conf as follows: Create a pack.conf as follows:
% cat pack.conf % cat pack.conf
com.sun.java.util.jar.pack.dump.bands=true com.sun.java.util.jar.pack.dump.bands=true
...@@ -41,5 +41,6 @@ Test: ...@@ -41,5 +41,6 @@ Test:
--verbose golden.jar.pack golden.jar --verbose golden.jar.pack golden.jar
This command will dump the Bands in a unique directory BD_XXXXXX, 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 one can then inspect the directory to ensure all of the bands are being
generated. Familiarity of the Pack200 specification is suggested. generated. Familiarity of the Pack200 specification is strongly
\ No newline at end of file suggested.
<project name="PackageVerify" default="dist" basedir=".."> <project name="PackageVerify" default="dist" basedir="..">
<!-- Requires ant 1.6.1+ and JDK 1.6+--> <!-- Requires ant 1.6.1+ and JDK 1.7+-->
<!-- set global properties for this build --> <!-- set global properties for this build -->
<property name="src" value="${basedir}/src"/> <property name="src" value="${basedir}/src"/>
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
<target name="compile" depends="init"> <target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} --> <!-- Compile the java code from ${src} into ${build} -->
<javac <javac
source="1.6" source="1.7"
srcdir="${src}" srcdir="${src}"
destdir="${build}/classes" destdir="${build}/classes"
verbose="no" verbose="no"
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
<target name="doc" depends="init, compile"> <target name="doc" depends="init, compile">
<javadoc <javadoc
source="1.6" source="1.7"
sourcepath="${src}" sourcepath="${src}"
destdir="${api}" destdir="${api}"
/> />
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -24,16 +24,58 @@ ...@@ -24,16 +24,58 @@
*/ */
package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- 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.*;
import java.util.jar.*;
import java.lang.reflect.*;
import java.io.*; import java.io.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import xmlkit.XMLKit.Element; import xmlkit.XMLKit.Element;
/* /*
* @author jrose * @author jrose, ksrini
*/ */
public class ClassReader extends ClassSyntax { public class ClassReader {
private static final CommandLineParser CLP = new CommandLineParser("" private static final CommandLineParser CLP = new CommandLineParser(""
+ "-source: +> = \n" + "-source: +> = \n"
...@@ -41,23 +83,24 @@ public class ClassReader extends ClassSyntax { ...@@ -41,23 +83,24 @@ public class ClassReader extends ClassSyntax {
+ "-encoding: +> = \n" + "-encoding: +> = \n"
+ "-jcov $ \n -nojcov !-jcov \n" + "-jcov $ \n -nojcov !-jcov \n"
+ "-verbose $ \n -noverbose !-verbose \n" + "-verbose $ \n -noverbose !-verbose \n"
+ "-pretty $ \n -nopretty !-pretty \n"
+ "-keepPath $ \n -nokeepPath !-keepPath \n" + "-keepPath $ \n -nokeepPath !-keepPath \n"
+ "-keepCP $ \n -nokeepCP !-keepCP \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" + "-keepOrder $ \n -nokeepOrder !-keepOrder \n"
+ "-keepSizes $ \n -nokeepSizes !-keepSizes \n"
+ "-continue $ \n -nocontinue !-continue \n" + "-continue $ \n -nocontinue !-continue \n"
+ "-attrDef & \n"
+ "-@ >-@ . \n" + "-@ >-@ . \n"
+ "- +? \n" + "- +? \n"
+ "\n"); + "\n");
// Protected state for representing the class file.
protected Element cfile; // <ClassFile ...>
protected Element cpool; // <ConstantPool ...>
protected Element klass; // <Class ...>
protected List<String> thePool; // stringified flattened Constant Pool
public static void main(String[] ava) throws IOException { public static void main(String[] ava) throws IOException {
ArrayList<String> av = new ArrayList<String>(Arrays.asList(ava)); ArrayList<String> av = new ArrayList<>(Arrays.asList(ava));
HashMap<String, String> props = new HashMap<String, String>(); HashMap<String, String> props = new HashMap<>();
props.put("-encoding:", "UTF8"); // default props.put("-encoding:", "UTF8"); // default
props.put("-keepOrder", null); // CLI default props.put("-keepOrder", null); // CLI default
props.put("-pretty", "1"); // CLI default props.put("-pretty", "1"); // CLI default
...@@ -80,7 +123,7 @@ public class ClassReader extends ClassSyntax { ...@@ -80,7 +123,7 @@ public class ClassReader extends ClassSyntax {
} }
*/ */
if (av.isEmpty()) { if (av.isEmpty()) {
av.add("doit"); //to enter this loop av.add(""); //to enter this loop
} }
boolean readList = false; boolean readList = false;
for (String a : av) { for (String a : av) {
...@@ -119,7 +162,7 @@ public class ClassReader extends ClassSyntax { ...@@ -119,7 +162,7 @@ public class ClassReader extends ClassSyntax {
private static void doFile(String a, private static void doFile(String a,
File source, File dest, File source, File dest,
ClassReader options, String encoding, ClassReader options, String encoding,
boolean contError) throws IOException { boolean contError) throws IOException {
if (!contError) { if (!contError) {
doFile(a, source, dest, options, encoding); doFile(a, source, dest, options, encoding);
} else { } else {
...@@ -127,40 +170,49 @@ public class ClassReader extends ClassSyntax { ...@@ -127,40 +170,49 @@ public class ClassReader extends ClassSyntax {
doFile(a, source, dest, options, encoding); doFile(a, source, dest, options, encoding);
} catch (Exception ee) { } catch (Exception ee) {
System.out.println("Error processing " + source + ": " + ee); System.out.println("Error processing " + source + ": " + ee);
ee.printStackTrace();
} }
} }
} }
private static void doJar(String a, File source, File dest, ClassReader options, private static void doJar(String a, File source, File dest,
String encoding, Boolean contError) throws IOException { ClassReader options, String encoding,
Boolean contError) throws IOException {
try { try {
JarFile jf = new JarFile(source); JarFile jf = new JarFile(source);
for (JarEntry je : Collections.list((Enumeration<JarEntry>) jf.entries())) { for (JarEntry je : Collections.list(jf.entries())) {
String name = je.getName(); String name = je.getName();
if (!name.endsWith(".class")) { if (!name.endsWith(".class")) {
continue; 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) { } catch (IOException ioe) {
if (contError) { throw ioe;
System.out.println("Error processing " + source + ": " + ioe);
} else {
throw ioe;
}
} }
} }
private static void doStream(String a, InputStream in, File dest, 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); File f = new File(a);
ClassReader cr = new ClassReader(options); 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; OutputStream out;
if (dest == null) { if (dest == null) {
//System.out.println(e.prettyString());
out = System.out; out = System.out;
} else { } else {
File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
...@@ -202,20 +254,8 @@ public class ClassReader extends ClassSyntax { ...@@ -202,20 +254,8 @@ public class ClassReader extends ClassSyntax {
} }
public static BufferedReader makeReader(InputStream in, String encoding) throws IOException { public static BufferedReader makeReader(InputStream in,
// encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name String encoding) throws IOException {
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;
}
Reader inw; Reader inw;
in = new BufferedInputStream(in); // add buffering in = new BufferedInputStream(in); // add buffering
if (encoding == null) { if (encoding == null) {
...@@ -226,20 +266,8 @@ public class ClassReader extends ClassSyntax { ...@@ -226,20 +266,8 @@ public class ClassReader extends ClassSyntax {
return new BufferedReader(inw); // add buffering return new BufferedReader(inw); // add buffering
} }
public static Writer makeWriter(OutputStream out, String encoding) throws IOException { public static Writer makeWriter(OutputStream out,
// encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name String encoding) throws IOException {
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;
}
Writer outw; Writer outw;
if (encoding == null) { if (encoding == null) {
outw = new OutputStreamWriter(out); outw = new OutputStreamWriter(out);
...@@ -252,12 +280,9 @@ public class ClassReader extends ClassSyntax { ...@@ -252,12 +280,9 @@ public class ClassReader extends ClassSyntax {
public Element result() { public Element result() {
return cfile; return cfile;
} }
protected InputStream in; protected InputStream in;
protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024); protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024);
protected byte cpTag[];
protected String cpName[];
protected String[] callables; // varies
public static final String REF_PREFIX = "#";
// input options // input options
public boolean pretty = false; public boolean pretty = false;
public boolean verbose = false; public boolean verbose = false;
...@@ -270,7 +295,7 @@ public class ClassReader extends ClassSyntax { ...@@ -270,7 +295,7 @@ public class ClassReader extends ClassSyntax {
public boolean keepSizes = false; public boolean keepSizes = false;
public ClassReader() { public ClassReader() {
super.cfile = new Element("ClassFile"); cfile = new Element("ClassFile");
} }
public ClassReader(ClassReader options) { public ClassReader(ClassReader options) {
...@@ -283,12 +308,7 @@ public class ClassReader extends ClassSyntax { ...@@ -283,12 +308,7 @@ public class ClassReader extends ClassSyntax {
verbose = options.verbose; verbose = options.verbose;
keepPath = options.keepPath; keepPath = options.keepPath;
keepCP = options.keepCP; keepCP = options.keepCP;
keepBytes = options.keepBytes;
parseBytes = options.parseBytes;
resolveRefs = options.resolveRefs;
keepSizes = options.keepSizes;
keepOrder = options.keepOrder; keepOrder = options.keepOrder;
attrTypes = options.attrTypes;
} }
public void copyOptionsFrom(Map<String, String> options) { public void copyOptionsFrom(Map<String, String> options) {
...@@ -304,274 +324,177 @@ public class ClassReader extends ClassSyntax { ...@@ -304,274 +324,177 @@ public class ClassReader extends ClassSyntax {
if (options.containsKey("-keepCP")) { if (options.containsKey("-keepCP")) {
keepCP = (options.get("-keepCP") != null); 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")) { if (options.containsKey("-keepOrder")) {
keepOrder = (options.get("-keepOrder") != null); keepOrder = (options.get("-keepOrder") != null);
} }
if (options.containsKey("-attrDef")) { }
addAttrTypes(options.get("-attrDef").split(" "));
} protected String getCpString(int i) {
if (options.get("-jcov") != null) { return thePool.get(i);
addJcovAttrTypes();
}
} }
public Element readFrom(InputStream in) throws IOException { public Element readFrom(InputStream in) throws IOException {
this.in = in; try {
// read the file header this.in = in;
int magic = u4(); ClassFile c = ClassFile.read(in);
if (magic != 0xCAFEBABE) { // read the file header
throw new RuntimeException("bad magic number " + Integer.toHexString(magic)); if (c.magic != 0xCAFEBABE) {
} throw new RuntimeException("bad magic number " +
cfile.setAttr("magic", "" + magic); Integer.toHexString(c.magic));
int minver = u2(); }
int majver = u2(); cfile.setAttr("magic", "" + c.magic);
cfile.setAttr("minver", "" + minver); int minver = c.minor_version;
cfile.setAttr("majver", "" + majver); int majver = c.major_version;
readCP(); cfile.setAttr("minver", "" + minver);
readClass(); cfile.setAttr("majver", "" + majver);
return result(); readCP(c);
readClass(c);
return result();
} catch (InvalidDescriptor | ConstantPoolException ex) {
throw new IOException("Fatal error", ex);
}
} }
public Element readFrom(File file) throws IOException { public Element readFrom(File file) throws IOException {
InputStream in = null; try (InputStream strm = new FileInputStream(file)) {
try { Element e = readFrom(new BufferedInputStream(strm));
in = new FileInputStream(file);
Element e = readFrom(new BufferedInputStream(in));
if (keepPath) { if (keepPath) {
e.setAttr("path", file.toString()); e.setAttr("path", file.toString());
} }
return e; 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"); klass = new Element("Class");
cfile.add(klass); cfile.add(klass);
int flags = u2(); String thisk = c.getName();
String thisk = cpRef();
String superk = cpRef();
klass.setAttr("name", thisk); klass.setAttr("name", thisk);
boolean flagsSync = ((flags & Modifier.SYNCHRONIZED) != 0);
flags &= ~Modifier.SYNCHRONIZED; AccessFlags af = new AccessFlags(c.access_flags.flags);
String flagString = flagString(flags, klass); klass.setAttr("flags", flagString(af, klass));
if (!flagsSync) { if (!"java/lang/Object".equals(thisk)) {
if (flagString.length() > 0) { klass.setAttr("super", c.getSuperclassName());
flagString += " "; }
} for (int i : c.interfaces) {
flagString += "!synchronized"; 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); if (!keepOrder) {
klass.setAttr("super", superk); fields.sort();
for (int len = u2(), i = 0; i < len; i++) {
String interk = cpRef();
klass.add(new Element("Interface", "name", interk));
} }
Element fields = readMembers("Field");
klass.addAll(fields); 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) { if (!keepOrder) {
methods.sort(); methods.sort();
} }
klass.addAll(methods); klass.addAll(methods);
readAttributesFor(klass); }
klass.trimToSize();
if (keepSizes) { private AccessFlags.Kind getKind(Element e) {
attachTo(cfile, formatAttrSizes()); switch(e.getName()) {
} case "Class":
if (paddingSize != 0) { return AccessFlags.Kind.Class;
cfile.setAttr("padding", "" + paddingSize); case "InnerClass":
} return AccessFlags.Kind.InnerClass;
} case "Field":
return AccessFlags.Kind.Field ;
private Element readMembers(String kind) throws IOException { case "Method":
int len = u2(); return AccessFlags.Kind.Method;
Element members = new Element(len); default: throw new RuntimeException("should not reach here");
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);
} }
return members;
} }
protected String flagString(int flags, Element holder) { protected String flagString(int flags, Element holder) {
// Superset of Modifier.toString. return flagString(new AccessFlags(flags), holder);
int kind = 0; }
if (holder.getName() == "Field") { protected String flagString(AccessFlags af, Element holder) {
kind = 1; return flagString(af, holder.getName());
} }
if (holder.getName() == "Method") { protected String flagString(int flags, String kind) {
kind = 2; return flagString(new AccessFlags(flags), kind);
} }
StringBuffer sb = new StringBuffer(); protected String flagString(AccessFlags af, String kind) {
for (int i = 0; flags != 0; i++, flags >>>= 1) { Set<String> mods = null;
if ((flags & 1) != 0) { switch (kind) {
if (sb.length() > 0) { case "Class":
sb.append(' '); mods = af.getClassFlags();
} break;
if (i < modifierNames.length) { case "InnerClass":
String[] names = modifierNames[i]; mods = af.getInnerClassFlags();
String name = (kind < names.length) ? names[kind] : null; break;
for (String name2 : names) { case "Field":
if (name != null) { mods = af.getFieldFlags();
break; break;
} case "Method":
name = name2; mods = af.getMethodFlags();
} break;
sb.append(name); default:
} else { throw new RuntimeException("should not reach here");
sb.append("#").append(1 << i); }
} StringBuilder sb = new StringBuilder();
} for (String x : mods) {
} sb.append(x.substring(x.indexOf('_') + 1).toLowerCase()).append(" ");
return sb.toString(); }
return sb.toString().trim();
} }
private void readAttributesFor(Element x) throws IOException {
Element prevCurrent; protected void readAttributesFor(ClassFile c, Attributes attrs, Element x) {
Element y = new Element(); Element container = new Element();
if (x.getName() == "Code") { AttributeVisitor av = new AttributeVisitor(this, c);
prevCurrent = currentCode; for (Attribute a : attrs) {
currentCode = x; av.visit(a, container);
} 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)); // <Bridge>, 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("<Code>...")) {
// delve into Code attribute
aval = readCode();
} else if (type.equals("<Frame>...")) {
// delve into StackMap attribute
aval = readStackMap(false);
} else if (type.equals("<FrameX>...")) {
// 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;
} }
if (!keepOrder) { if (!keepOrder) {
y.sort(); container.sort();
y.sortAttrs();
} }
//System.out.println("attachTo 2 "+x+" <- "+y); x.addAll(container);
attachTo(x, y);
} }
private int fileSize = 0; private int fileSize = 0;
private int paddingSize = 0; private HashMap<String, int[]> attrSizes = new HashMap<>();
private HashMap<String, int[]> attrSizes = new HashMap<String, int[]>();
private Element formatAttrSizes() {
Element e = new Element("Sizes");
e.setAttr("fileSize", "" + fileSize);
for (Map.Entry<String, int[]> 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 void attachTo(Element x, Object aval0) { private void attachTo(Element x, Object aval0) {
if (aval0 == null) { if (aval0 == null) {
return; return;
} }
//System.out.println("attachTo "+x+" : "+aval0);
if (!(aval0 instanceof Element)) { if (!(aval0 instanceof Element)) {
x.add(aval0); x.add(aval0);
return; return;
...@@ -589,7 +512,6 @@ public class ClassReader extends ClassSyntax { ...@@ -589,7 +512,6 @@ public class ClassReader extends ClassSyntax {
} }
private void attachAttrTo(Element x, String aname, String aval) { private void attachAttrTo(Element x, String aname, String aval) {
//System.out.println("attachAttrTo "+x+" : "+aname+"="+aval);
String aval0 = x.getAttr(aname); String aval0 = x.getAttr(aname);
if (aval0 != null) { if (aval0 != null) {
aval = aval0 + " " + aval; aval = aval0 + " " + aval;
...@@ -597,407 +519,1003 @@ public class ClassReader extends ClassSyntax { ...@@ -597,407 +519,1003 @@ public class ClassReader extends ClassSyntax {
x.setAttr(aname, aval); x.setAttr(aname, aval);
} }
private Element readAttributeCallables(String type) throws IOException { private void readCP(ClassFile c) throws IOException {
assert (callables == null); cpool = new Element("ConstantPool", c.constant_pool.size());
callables = getBodies(type); ConstantPoolVisitor cpv = new ConstantPoolVisitor(cpool, c,
Element res = readAttribute(callables[0]); c.constant_pool.size());
callables = null; for (int i = 1 ; i < c.constant_pool.size() ; i++) {
return res; try {
} cpv.visit(c.constant_pool.get(i), i);
} catch (InvalidIndex ex) {
private Element readAttribute(String type) throws IOException { // can happen periodically when accessing doubles etc. ignore it
//System.out.println("readAttribute "+type); // ex.printStackTrace();
Element aval = new Element(); }
String nextAttrName = null; }
for (int len = type.length(), next, i = 0; i < len; i = next) { thePool = cpv.getPoolList();
String value; if (verbose) {
switch (type.charAt(i)) { for (int i = 0; i < thePool.size(); i++) {
case '<': System.out.println("[" + i + "]: " + thePool.get(i));
assert (nextAttrName == null); }
next = type.indexOf('>', ++i); }
String form = type.substring(i, next++); if (keepCP) {
if (form.indexOf('=') < 0) { cfile.add(cpool);
// elem_placement = '<' elemname '>' }
assert (aval.attrSize() == 0); }
assert (aval.isAnonymous()); }
aval.setName(form.intern());
} else { class ConstantPoolVisitor implements ConstantPool.Visitor<String, Integer> {
// attr_placement = '<' attrname '=' (value)? '>' final List<String> slist;
int eqPos = form.indexOf('='); final Element xpool;
nextAttrName = form.substring(0, eqPos).intern(); final ClassFile cf;
if (eqPos != form.length() - 1) { final ConstantPool cfpool;
value = form.substring(eqPos + 1); final List<String> bsmlist;
attachAttrTo(aval, nextAttrName, value);
nextAttrName = null;
} public ConstantPoolVisitor(Element xpool, ClassFile cf, int size) {
// ...else subsequent type parsing will find the attr value slist = new ArrayList<>(size);
// and add it as "nextAttrName". for (int i = 0 ; i < size; i++) {
} slist.add(null);
continue; }
case '(': this.xpool = xpool;
next = type.indexOf(')', ++i); this.cf = cf;
int callee = Integer.parseInt(type.substring(i, next++)); this.cfpool = cf.constant_pool;
attachTo(aval, readAttribute(callables[callee])); bsmlist = readBSM();
continue; }
case 'N': // replication = 'N' int '[' type ... ']'
{ public List<String> getPoolList() {
int count = getInt(type.charAt(i + 1), false); return Collections.unmodifiableList(slist);
assert (count >= 0); }
next = i + 2;
String type1 = getBody(type, next); public List<String> getBSMList() {
next += type1.length() + 2; // skip body and brackets return Collections.unmodifiableList(bsmlist);
for (int j = 0; j < count; j++) { }
attachTo(aval, readAttribute(type1));
} public String visit(CPInfo c, int index) {
} return c.accept(this, index);
continue; }
case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']'
int tagValue; private List<String> readBSM() {
if (type.charAt(++i) == 'S') { BootstrapMethods_attribute bsmAttr =
tagValue = getInt(type.charAt(++i), true); (BootstrapMethods_attribute) cf.getAttribute(Attribute.BootstrapMethods);
} else { if (bsmAttr != null) {
tagValue = getInt(type.charAt(i), false); List<String> 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" bsmStr = value;
++i; // skip the int type char for (int idx : bsms.bootstrap_arguments) {
// union_case = '(' uc_tag (',' uc_tag)* ')' '[' body ']' value = slist.get(idx);
// uc_tag = ('-')? digit+ if (value == null) {
for (boolean foundCase = false;; i = next) { value = visit(cfpool.get(idx), idx);
assert (type.charAt(i) == '('); slist.set(idx, value);
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 = bsmStr.concat("," + value);
} }
continue; out.add(bsmStr);
case 'B': } catch (InvalidIndex ex) {
case 'H': ex.printStackTrace();
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);
} }
return out;
} }
//System.out.println("readAttribute => "+aval); return new ArrayList<>(0);
assert (nextAttrName == null);
return aval;
} }
private int getInt(char ch, boolean signed) throws IOException { @Override
if (signed) { public String visitClass(CONSTANT_Class_info c, Integer p) {
switch (ch) { String value = slist.get(p);
case 'B': if (value == null) {
return (byte) u1(); try {
case 'H': value = visit(cfpool.get(c.name_index), c.name_index);
return (short) u2(); slist.set(p, value);
case 'I': xpool.add(new Element("CONSTANT_Class",
return (int) u4(); new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
} }
} else { }
switch (ch) { return value;
case 'B': }
return u1();
case 'H': @Override
return u2(); public String visitDouble(CONSTANT_Double_info c, Integer p) {
case 'I': String value = slist.get(p);
return u4(); 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 value;
return 0; }
}
@Override
private Element readCode() throws IOException { public String visitFloat(CONSTANT_Float_info c, Integer p) {
int stack = u2(); String value = slist.get(p);
int local = u2(); if (value == null) {
int length = u4(); value = Float.toString(c.value);
StringBuilder sb = new StringBuilder(length); slist.set(p, value);
for (int i = 0; i < length; i++) { xpool.add(new Element("CONSTANT_Float",
sb.append((char) u1()); new String[]{"id", p.toString()},
} value));
String bytecodes = sb.toString(); }
Element e = new Element("Code", return value;
"stack", "" + stack, }
"local", "" + local);
Element bytes = new Element("Bytes", (String[]) null, bytecodes); @Override
if (keepBytes) { public String visitInteger(CONSTANT_Integer_info cnstnt, Integer p) {
e.add(bytes); String value = slist.get(p);
} if (value == null) {
if (parseBytes) { value = Integer.toString(cnstnt.value);
e.add(parseByteCodes(bytecodes)); slist.set(p, value);
} xpool.add(new Element("CONSTANT_Integer",
for (int len = u2(), i = 0; i < len; i++) { new String[]{"id", p.toString()},
int start = u2(); value));
int end = u2(); }
int catsh = u2(); return value;
String clasz = cpRef(); }
e.add(new Element("Handler",
"start", "" + start, @Override
"end", "" + end, public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info c,
"catch", "" + catsh, Integer p) {
"class", clasz)); 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); return value;
e.trimToSize();
return e;
} }
private Element parseByteCodes(String bytecodes) { @Override
Element e = InstructionSyntax.parse(bytecodes); public String visitInvokeDynamic(CONSTANT_InvokeDynamic_info c, Integer p) {
for (Element ins : e.elements()) { String value = slist.get(p);
Number ref = ins.getAttrNumber("ref"); if (value == null) {
if (ref != null && resolveRefs) { try {
int id = ref.intValue(); value = bsmlist.get(c.bootstrap_method_attr_index) + " "
String val = cpName(id); + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
if (ins.getName().startsWith("ldc")) { slist.set(p, value);
// Yuck: Arb. string cannot be an XML attribute. xpool.add(new Element("CONSTANT_InvokeDynamic",
ins.add(val); new String[]{"id", p.toString()},
val = ""; value));
byte tag = (id >= 0 && id < cpTag.length) ? cpTag[id] : 0;
if (tag != 0) { } catch (ConstantPoolException ex) {
ins.setAttrLong("tag", tag); ex.printStackTrace();
}
}
if (ins.getName() == "invokeinterface"
&& computeInterfaceNum(val) == ins.getAttrLong("num")) {
ins.setAttr("num", null); // garbage bytes
}
ins.setAttr("ref", null);
ins.setAttr("val", val);
} }
} }
return e; return value;
} }
private Element readStackMap(boolean hasXOption) throws IOException { @Override
Element result = new Element(); public String visitLong(CONSTANT_Long_info c, Integer p) {
Element bytes = currentCode.findElement("Bytes"); String value = slist.get(p);
assert (bytes != null && bytes.size() == 1); if (value == null) {
int byteLength = ((String) bytes.get(0)).length(); value = Long.toString(c.value);
boolean uoffsetIsU4 = (byteLength >= (1 << 16)); slist.set(p, value);
boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); xpool.add(new Element("CONSTANT_Long",
boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); new String[]{"id", p.toString()},
if (hasXOption || uoffsetIsU4 || ulocalvarIsU4 || ustackIsU4) { value));
Element flags = new Element("StackMapFlags"); }
if (hasXOption) { return value;
flags.setAttr("hasXOption", "true"); }
@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); }
} return value;
int frame_count = (uoffsetIsU4 ? u4() : u2()); }
for (int i = 0; i < frame_count; i++) {
int bci = (uoffsetIsU4 ? u4() : u2()); @Override
int flags = (hasXOption ? u1() : 0); public String visitString(CONSTANT_String_info c, Integer p) {
Element frame = new Element("Frame"); try {
result.add(frame);
if (flags != 0) { String value = slist.get(p);
frame.setAttr("flags", "" + flags); 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); return value;
// Scan local and stack types in this frame: } catch (ConstantPoolException ex) {
final int LOCALS = 0, STACK = 1; throw new RuntimeException("Fatal error", ex);
for (int j = LOCALS; j <= STACK; j++) { }
int typeSize; }
if (j == LOCALS) {
typeSize = (ulocalvarIsU4 ? u4() : u2()); @Override
} else { // STACK public String visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p) {
typeSize = (ustackIsU4 ? u4() : u2()); String value = slist.get(p);
} if (value == null) {
Element types = new Element(j == LOCALS ? "Local" : "Stack"); value = cnstnt.value;
for (int k = 0; k < typeSize; k++) { slist.set(p, value);
int tag = u1(); xpool.add(new Element("CONSTANT_Utf8",
Element type = new Element(itemTagName(tag)); new String[]{"id", p.toString()},
types.add(type); value));
switch (tag) { }
case ITEM_Object: return value;
type.setAttr("class", cpRef());
break; }
case ITEM_Uninitialized: }
case ITEM_ReturnAddress:
type.setAttr("bci", "" + (uoffsetIsU4 ? u4() : u2())); class AttributeVisitor implements Attribute.Visitor<Element, Element> {
break; 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) { bme.trimToSize();
frame.add(types); 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; }
}
@Override
private void readCP() throws IOException { public Element visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva,
int cpLen = u2(); Element p) {
cpTag = new byte[cpLen]; Element e = new Element(x.getCpString(rva.attribute_name_index));
cpName = new String[cpLen]; parseAnnotations(rva.annotations, e);
int cpTem[][] = new int[cpLen][]; e.trimToSize();
for (int i = 1; i < cpLen; i++) { p.add(e);
cpTag[i] = (byte) u1(); return null;
switch (cpTag[i]) { }
case CONSTANT_Utf8:
buf.reset(); @Override
for (int len = u2(), j = 0; j < len; j++) { public Element visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria,
buf.write(u1()); Element p) {
} Element e = new Element(x.getCpString(ria.attribute_name_index));
cpName[i] = buf.toString(UTF8_ENCODING); parseAnnotations(ria.annotations, e);
break; e.trimToSize();
case CONSTANT_Integer: p.add(e);
cpName[i] = String.valueOf((int) u4()); 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<Element, Void> {
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; break;
case CONSTANT_Float: case StackMapTable_attribute.verification_type_info.ITEM_Integer:
cpName[i] = String.valueOf(Float.intBitsToFloat(u4())); ve = new Element("ITEM_Integer");
break; break;
case CONSTANT_Long: case StackMapTable_attribute.verification_type_info.ITEM_Float:
cpName[i] = String.valueOf(u8()); ve = new Element("ITEM_Float");
i += 1;
break; break;
case CONSTANT_Double: case StackMapTable_attribute.verification_type_info.ITEM_Long:
cpName[i] = String.valueOf(Double.longBitsToDouble(u8())); ve = new Element("ITEM_Long");
i += 1;
break; break;
case CONSTANT_Class: case StackMapTable_attribute.verification_type_info.ITEM_Double:
case CONSTANT_String: ve = new Element("ITEM_Double");
cpTem[i] = new int[]{u2()};
break; break;
case CONSTANT_Fieldref: case StackMapTable_attribute.verification_type_info.ITEM_Null:
case CONSTANT_Methodref: ve = new Element("ITEM_Null");
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameAndType:
cpTem[i] = new int[]{u2(), u2()};
break; break;
} case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
} ve = new Element("ITEM_Uninitialized");
for (int i = 1; i < cpLen; i++) { offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset;
switch (cpTag[i]) { ve.setAttr("offset", "" + offset);
case CONSTANT_Class:
case CONSTANT_String:
cpName[i] = cpName[cpTem[i][0]];
break; break;
case CONSTANT_NameAndType: case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]]; ve = new Element("ITEM_UnitializedtThis");
break; break;
} case StackMapTable_attribute.verification_type_info.ITEM_Object:
} ve = new Element("ITEM_Object");
// do fieldref et al after nameandtype are all resolved index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index;
for (int i = 1; i < cpLen; i++) { ve.setAttr("class", x.getCpString(index));
switch (cpTag[i]) {
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]];
break; 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); container.trimToSize();
for (int i = 0; i < cpName.length; i++) { return container;
if (cpName[i] == null) {
continue;
}
cpool.add(new Element(cpTagName(cpTag[i]),
new String[]{"id", "" + i},
cpName[i]));
}
if (keepCP) {
cfile.add(cpool);
}
} }
}
private String cpRef() throws IOException { class InstructionVisitor implements Instruction.KindVisitor<Element, Void> {
int ref = u2();
if (resolveRefs) { final ClassReader x;
return cpName(ref); final ClassFile cf;
} else {
return REF_PREFIX + ref; 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) { @Override
if (id >= 0 && id < cpName.length) { public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) {
return cpName[id]; // 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 { } else {
return "[CP#" + Integer.toHexString(id) + "]"; ie.setAttr("ref", x.getCpString(i1));
ie.setAttr("val", "" + i2);
} }
return ie;
} }
private long u8() throws IOException { @Override
return ((long) u4() << 32) + (((long) u4() << 32) >>> 32); 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 { @Override
return (u2() << 16) + u2(); 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 { @Override
return (u1() << 8) + u1(); 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 { @Override
int x = in.read(); public Element visitTableSwitch(Instruction i, int i1, int i2, int i3,
if (x < 0) { int[] ints, Void p) {
paddingSize++; Element ie = new Element(i.getMnemonic());
return 0; // error recovery int pc = i.getPC();
} ie.setAttr("lab", "" + (pc + i1));
fileSize++; for (int k : ints) {
assert (x == (x & 0xFF)); Element c = new Element("Case");
return x; 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<Element, Element> {
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;
}
}
/*
* 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<String> cpTagNames;
static {
Set<String> set = new HashSet<String>(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<String> itemTagNames;
static {
Set<String> set = new HashSet<String>(Arrays.asList(itemTagName));
set.remove(null);
itemTagNames = Collections.unmodifiableSet(set);
}
protected static final HashMap<String, String> attrTypesBacking;
protected static final Map<String, String> attrTypesInit;
static {
HashMap<String, String> at = new HashMap<String, String>();
//at.put("*.Deprecated", "<deprecated=true>");
//at.put("*.Synthetic", "<synthetic=true>");
////at.put("Field.ConstantValue", "<constantValue=>KQH");
//at.put("Class.SourceFile", "<sourceFile=>RUH");
at.put("Method.Bridge", "<Bridge>");
at.put("Method.Varargs", "<Varargs>");
at.put("Class.Enum", "<Enum>");
at.put("*.Signature", "<Signature>RSH");
//at.put("*.Deprecated", "<Deprecated>");
//at.put("*.Synthetic", "<Synthetic>");
at.put("Field.ConstantValue", "<ConstantValue>KQH");
at.put("Class.SourceFile", "<SourceFile>RUH");
at.put("Class.InnerClasses", "NH[<InnerClass><class=>RCH<outer=>RCH<name=>RUH<flags=>FH]");
at.put("Code.LineNumberTable", "NH[<LineNumber><bci=>PH<line=>H]");
at.put("Code.LocalVariableTable", "NH[<LocalVariable><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
at.put("Code.LocalVariableTypeTable", "NH[<LocalVariableType><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
at.put("Method.Exceptions", "NH[<Exception><name=>RCH]");
at.put("Method.Code", "<Code>...");
at.put("Code.StackMapTable", "<Frame>...");
//at.put("Code.StkMapX", "<FrameX>...");
if (true) {
at.put("Code.StackMapTable",
"[NH[<Frame>(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"
+ ")[<SameLocals1StackItemFrame>(4)]"
+ "(247)[<SameLocals1StackItemExtended>H(4)]"
+ "(248)[<Chop3>H]"
+ "(249)[<Chop2>H]"
+ "(250)[<Chop1>H]"
+ "(251)[<SameFrameExtended>H]"
+ "(252)[<Append1>H(4)]"
+ "(253)[<Append2>H(4)(4)]"
+ "(254)[<Append3>H(4)(4)(4)]"
+ "(255)[<FullFrame>H(2)(3)]"
+ "()[<SameFrame>]]"
+ "[NH[<Local>(4)]]"
+ "[NH[<Stack>(4)]]"
+ "[TB"
+ ("(0)[<Top>]"
+ "(1)[<ItemInteger>](2)[<ItemFloat>](3)[<ItemDouble>](4)[<ItemLong>]"
+ "(5)[<ItemNull>](6)[<ItemUninitializedThis>]"
+ "(7)[<ItemObject><class=>RCH]"
+ "(8)[<ItemUninitialized><bci=>PH]"
+ "()[<ItemUnknown>]]"));
}
at.put("Class.EnclosingMethod", "<EnclosingMethod><class=>RCH<desc=>RDH");//RDNH
// Layouts of metadata attrs:
String vpf = "[<RuntimeVisibleAnnotation>";
String ipf = "[<RuntimeInvisibleAnnotation>";
String apf = "[<Annotation>";
String mdanno2 = ""
+ "<type=>RSHNH[<Member><name=>RUH(3)]]"
+ ("[TB"
+ "(\\B,\\C,\\I,\\S,\\Z)[<value=>KIH]"
+ "(\\D)[<value=>KDH]"
+ "(\\F)[<value=>KFH]"
+ "(\\J)[<value=>KJH]"
+ "(\\c)[<class=>RSH]"
+ "(\\e)[<type=>RSH<name=>RUH]"
+ "(\\s)[<String>RUH]"
+ "(\\@)[(2)]"
+ "(\\[)[NH[<Element>(3)]]"
+ "()[]"
+ "]");
String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2;
String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2;
String vparamanno = ""
+ "[NB[<RuntimeVisibleParameterAnnotation>(1)]][NH[(2)]]"
+ apf + mdanno2;
String iparamanno = ""
+ "[NB[<RuntimeInvisibleParameterAnnotation>(1)]][NH[(2)]]"
+ apf + mdanno2;
String mdannodef = "[<AnnotationDefault>(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[<Coverage><bci=>PH<type=>H<line=>I<pos=>I]",
"Code.CharacterRangeTable=NH[<CharacterRange><bci=>PH<endbci=>POH<from=>I<to=>I<flag=>H]",
"Class.SourceID=<SourceID><id=>RUH",
"Class.CompilationID=<CompilationID><id=>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<String> nonAttrTags;
static {
HashSet<String> tagSet = new HashSet<String>();
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<String> 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<String, String> 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<String, String> at) {
for (String attr : at.keySet()) {
checkAttr(attr);
}
}
private void modAttrs() {
if (attrTypes == attrTypesInit) {
// Make modifiable.
attrTypes = new HashMap<String, String>(attrTypesBacking);
}
}
public void addAttrType(String attr, String fmt) {
checkAttr(attr);
modAttrs();
attrTypes.put(attr, fmt);
}
public void addAttrTypes(Map<String, String> at) {
checkAttrs(at);
modAttrs();
attrTypes.putAll(at);
}
public Map<String, String> getAttrTypes() {
if (attrTypes == attrTypesInit) {
return attrTypes;
}
return Collections.unmodifiableMap(attrTypes);
}
public void setAttrTypes(Map<String, String> 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<String> bodies = new ArrayList<String>();
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; // <ClassFile ...>
protected Element cpool; // <ConstantPool ...>
protected Element klass; // <Class ...>
protected Element currentMember; // varies during scans
protected Element currentCode; // varies during scans
}
/*
* 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<String> av = new ArrayList<String>(Arrays.asList(ava));
HashMap<String, String> props = new HashMap<String, String>();
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<String, String> attrTypesByTag;
protected OutputStream out;
protected HashMap<String, int[]> cpMap = new HashMap<String, int[]>();
protected ArrayList<ByteArrayOutputStream> attrBufs = new ArrayList<ByteArrayOutputStream>();
private void setupAttrTypes() {
attrTypesByTag = new HashMap<String, String>();
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<String> attrNames = new LinkedHashSet<String>();
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("<Code>...")) {
writeCode((Element) attrs.get(0)); // assume only 1
} else if (type.equals("<Frame>...")) {
writeStackMap(attrs, false);
} else if (type.equals("<FrameX>...")) {
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);
}
}
/*
* 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<String, String> labels = new HashMap<String, String>();
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 <ifop lab=1f> <goto_w target> <label pc=1f>
int target = pc + opnd;
putInt(sbuf, nextpc - pc, -2);
assert (sbuf.length() == pc + GOTO_LEN);
sbuf.append((char) GOTO_W);
putInt(sbuf, target - (pc + GOTO_LEN), 4);
} else if (op == 0xaa || //tableswitch
op == 0xab) { //lookupswitch
ins = insElems[id];
for (int pad = switchBase(pc) - (pc + 1); pad > 0; pad--) {
sbuf.append((char) 0);
}
assert (pc + opnd == insLocs[(int) ins.getAttrLong("lab")]);
putInt(sbuf, opnd, 4); // default label
if (op == 0xaa) { //tableswitch
Element cas0 = (Element) ins.get(0);
int lowCase = (int) cas0.getAttrLong("num");
Element casN = (Element) ins.get(ins.size() - 1);
int highCase = (int) casN.getAttrLong("num");
assert (highCase - lowCase + 1 == ins.size());
putInt(sbuf, lowCase, 4);
putInt(sbuf, highCase, 4);
int caseForAssert = lowCase;
for (Element cas : ins.elements()) {
int target = insLocs[(int) cas.getAttrLong("lab")];
assert (cas.getAttrLong("num") == caseForAssert++);
putInt(sbuf, target - pc, 4);
}
} else { //lookupswitch
int caseCount = ins.size();
putInt(sbuf, caseCount, 4);
for (Element cas : ins.elements()) {
int target = insLocs[(int) cas.getAttrLong("lab")];
putInt(sbuf, (int) cas.getAttrLong("num"), 4);
putInt(sbuf, target - pc, 4);
}
}
assert (nextpc == sbuf.length());
} else {
putInt(sbuf, opnd, -(nextpc - (pc + 1)));
}
} else if (nextpc == pc + 1) {
// a single-byte instruction
sbuf.append((char) op);
} else {
// picky stuff
boolean wide = isWide[id];
if (wide) {
sbuf.append((char) WIDE);
pc++;
}
sbuf.append((char) op);
int opnd1;
int opnd2 = opnd;
switch (op) {
case 0x84: //iinc
ins = insElems[id];
opnd1 = (int) ins.getAttrLong("loc");
if (isWide[id]) {
putInt(sbuf, opnd1, 2);
putInt(sbuf, opnd2, 2);
} else {
putInt(sbuf, opnd1, 1);
putInt(sbuf, opnd2, 1);
}
break;
case 0xc5: //multianewarray
ins = insElems[id];
opnd1 = getCPIndex(ins, 'c', getCPI);
putInt(sbuf, opnd1, 2);
putInt(sbuf, opnd2, 1);
break;
case 0xb9: //invokeinterface
ins = insElems[id];
opnd1 = getCPIndex(ins, 'n', getCPI);
putInt(sbuf, opnd1, 2);
opnd2 = (int) ins.getAttrLong("num");
if (opnd2 == 0) {
opnd2 = ClassSyntax.computeInterfaceNum(ins.getAttr("val"));
}
putInt(sbuf, opnd2, 2);
break;
default:
// put the single operand and be done
putInt(sbuf, opnd, nextpc - (pc + 1));
break;
}
}
}
assert (sbuf.length() == insLocs[insCount]);
return sbuf.toString();
}
static int getCPIndex(Element ins, char ctype,
ClassSyntax.GetCPIndex getCPI) {
int x = (int) ins.getAttrLong("ref");
if (x == 0 && getCPI != null) {
String val = ins.getAttr("val");
if (val == null || val.equals("")) {
val = ins.getText().toString();
}
byte tag;
switch (ctype) {
case 'k':
tag = (byte) ins.getAttrLong("tag");
break;
case 'c':
tag = ClassSyntax.CONSTANT_Class;
break;
case 'f':
tag = ClassSyntax.CONSTANT_Fieldref;
break;
case 'm':
tag = ClassSyntax.CONSTANT_Methodref;
break;
case 'n':
tag = ClassSyntax.CONSTANT_InterfaceMethodref;
break;
default:
throw new Error("bad ctype " + ctype + " in " + ins);
}
x = getCPI.getCPIndex(tag, val);
//System.out.println("getCPIndex "+ins+" => "+tag+"/"+val+" => "+x);
} else {
assert (x > 0);
}
return x;
}
static void putInt(StringBuffer sbuf, int x, int len) {
//System.out.println("putInt x="+x+" len="+len);
boolean isSigned = false;
if (len < 0) {
len = -len;
isSigned = true;
}
assert (len == 1 || len == 2 || len == 4);
int insig = ((4 - len) * 8); // how many insignificant bits?
int sx = x << insig;
;
assert (x == (isSigned ? (sx >> insig) : (sx >>> insig)));
for (int i = 0; i < len; i++) {
sbuf.append((char) (sx >>> 24));
sx <<= 8;
}
}
}
/*
* 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;
import java.util.Map;
/*
* @author jrose
*/
public abstract class InstructionSyntax {
InstructionSyntax() {
}
static final String[] bcNames;
static final String[] bcFormats;
static final String[] bcWideFormats;
static final HashMap<String, Integer> bcCodes;
static final HashMap<String, Element> abbrevs;
static final HashMap<Element, String> rabbrevs;
static {
TokenList tl = new TokenList(
" nop aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3"
+ " iconst_4 iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2"
+ " dconst_0 dconst_1 bipush/s sipush/ss ldc/k ldc_w/kk ldc2_w/kk"
+ " iload/wl lload/wl fload/wl dload/wl aload/wl iload_0 iload_1"
+ " iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1"
+ " fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1"
+ " aload_2 aload_3 iaload laload faload daload aaload baload caload"
+ " saload istore/wl lstore/wl fstore/wl dstore/wl astore/wl"
+ " istore_0 istore_1 istore_2 istore_3 lstore_0 lstore_1 lstore_2"
+ " lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 dstore_1"
+ " dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore"
+ " lastore fastore dastore aastore bastore castore sastore pop pop2"
+ " dup dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd"
+ " isub lsub fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem"
+ " lrem frem drem ineg lneg fneg dneg ishl lshl ishr lshr iushr"
+ " lushr iand land ior lor ixor lxor iinc/wls i2l i2f i2d l2i l2f"
+ " l2d f2i f2l f2d d2i d2l d2f i2b i2c i2s lcmp fcmpl fcmpg dcmpl"
+ " dcmpg ifeq/oo ifne/oo iflt/oo ifge/oo ifgt/oo ifle/oo"
+ " if_icmpeq/oo if_icmpne/oo if_icmplt/oo if_icmpge/oo if_icmpgt/oo"
+ " if_icmple/oo if_acmpeq/oo if_acmpne/oo goto/oo jsr/oo ret/wl"
+ " tableswitch/oooot lookupswitch/oooot ireturn lreturn freturn dreturn areturn"
+ " return getstatic/kf putstatic/kf getfield/kf putfield/kf"
+ " invokevirtual/km invokespecial/km invokestatic/km"
+ " invokeinterface/knxx xxxunusedxxx new/kc newarray/x anewarray/kc"
+ " arraylength athrow checkcast/kc instanceof/kc monitorenter"
+ " monitorexit wide multianewarray/kcx ifnull/oo ifnonnull/oo"
+ " goto_w/oooo jsr_w/oooo");
assert (tl.size() == 202); // this many instructions!
HashMap<String, Integer> map = new HashMap<String, Integer>(tl.size());
String[] names = tl.toArray(new String[tl.size()]);
String[] formats = new String[names.length];
String[] wideFormats = new String[names.length];
StringBuilder sbuf = new StringBuilder();
sbuf.append('i'); // all op formats begin with "i"
int i = 0;
for (String ins : names) {
assert (ins == ins.trim()); // no whitespace
int sfx = ins.indexOf('/');
String format = "i";
String wideFormat = null;
if (sfx >= 0) {
format = ins.substring(sfx + 1);
ins = ins.substring(0, sfx);
if (format.charAt(0) == 'w') {
format = format.substring(1);
sbuf.setLength(1);
for (int j = 0; j < format.length(); j++) {
// double everything except the initial 'i'
sbuf.append(format.charAt(j));
sbuf.append(format.charAt(j));
}
wideFormat = sbuf.toString().intern();
}
sbuf.setLength(1);
sbuf.append(format);
format = sbuf.toString().intern();
}
ins = ins.intern();
names[i] = ins;
formats[i] = format;
wideFormats[i] = (wideFormat != null) ? wideFormat : format;
//System.out.println(ins+" "+format+" "+wideFormat);
map.put(ins, i++);
}
//map = Collections.unmodifiableMap(map);
HashMap<String, Element> abb = new HashMap<String, Element>(tl.size() / 2);
abb.put("iconst_m1", new Element("bipush", "num", "-1"));
for (String ins : names) {
int sfx = ins.indexOf('_');
if (sfx >= 0 && Character.isDigit(ins.charAt(sfx + 1))) {
String pfx = ins.substring(0, sfx).intern();
String num = ins.substring(sfx + 1);
String att = pfx.endsWith("const") ? "num" : "loc";
Element exp = new Element(pfx, att, num).deepFreeze();
abb.put(ins, exp);
}
}
//abb = Collections.unmodifiableMap(abb);
HashMap<Element, String> rabb = new HashMap<Element, String>(tl.size() / 2);
for (Map.Entry<String, Element> e : abb.entrySet()) {
rabb.put(e.getValue(), e.getKey());
}
//rabb = Collections.unmodifiableMap(rabb);
bcNames = names;
bcFormats = formats;
bcWideFormats = wideFormats;
bcCodes = map;
abbrevs = abb;
rabbrevs = rabb;
}
public static String opName(int op) {
if (op >= 0 && op < bcNames.length) {
return bcNames[op];
}
return "unknown#" + op;
}
public static String opFormat(int op) {
return opFormat(op, false);
}
public static String opFormat(int op, boolean isWide) {
if (op >= 0 && op < bcFormats.length) {
return (isWide ? bcWideFormats[op] : bcFormats[op]);
}
return "?";
}
public static int opCode(String opName) {
Integer op = (Integer) bcCodes.get(opName);
if (op != null) {
return op.intValue();
}
return -1;
}
public static Element expandAbbrev(String opName) {
return abbrevs.get(opName);
}
public static String findAbbrev(Element op) {
return rabbrevs.get(op);
}
public static int invertBranchOp(int op) {
assert (opFormat(op).indexOf('o') >= 0);
final int IFMIN = 0x99;
final int IFMAX = 0xa6;
final int IFMIN2 = 0xc6;
final int IFMAX2 = 0xc7;
assert (bcNames[IFMIN] == "ifeq");
assert (bcNames[IFMAX] == "if_acmpne");
assert (bcNames[IFMIN2] == "ifnonnull");
assert (bcNames[IFMAX2] == "ifnull");
int rop;
if (op >= IFMIN && op <= IFMAX) {
rop = IFMIN + ((op - IFMIN) ^ 1);
} else if (op >= IFMIN2 && op <= IFMAX2) {
rop = IFMIN2 + ((op - IFMIN2) ^ 1);
} else {
assert (false);
rop = op;
}
assert (opFormat(rop).indexOf('o') >= 0);
return rop;
}
public static Element parse(String bytes) {
Element e = new Element("Instructions", bytes.length());
boolean willBeWide;
boolean isWide = false;
Element[] tempMap = new Element[bytes.length()];
for (int pc = 0, nextpc; pc < bytes.length(); pc = nextpc) {
int op = bytes.charAt(pc);
Element i = new Element(opName(op));
nextpc = pc + 1;
int locarg = 0;
int cparg = 0;
int intarg = 0;
int labelarg = 0;
willBeWide = false;
switch (op) {
case 0xc4: //wide
willBeWide = true;
break;
case 0x10: //bipush
intarg = nextpc++;
intarg *= -1; //mark signed
break;
case 0x11: //sipush
intarg = nextpc;
nextpc += 2;
intarg *= -1; //mark signed
break;
case 0x12: //ldc
cparg = nextpc++;
break;
case 0x13: //ldc_w
case 0x14: //ldc2_w
case 0xb2: //getstatic
case 0xb3: //putstatic
case 0xb4: //getfield
case 0xb5: //putfield
case 0xb6: //invokevirtual
case 0xb7: //invokespecial
case 0xb8: //invokestatic
case 0xbb: //new
case 0xbd: //anewarray
case 0xc0: //checkcast
case 0xc1: //instanceof
cparg = nextpc;
nextpc += 2;
break;
case 0xb9: //invokeinterface
cparg = nextpc;
nextpc += 2;
intarg = nextpc;
nextpc += 2;
break;
case 0xc5: //multianewarray
cparg = nextpc;
nextpc += 2;
intarg = nextpc++;
break;
case 0x15: //iload
case 0x16: //lload
case 0x17: //fload
case 0x18: //dload
case 0x19: //aload
case 0x36: //istore
case 0x37: //lstore
case 0x38: //fstore
case 0x39: //dstore
case 0x3a: //astore
case 0xa9: //ret
locarg = nextpc++;
if (isWide) {
nextpc++;
}
break;
case 0x84: //iinc
locarg = nextpc++;
if (isWide) {
nextpc++;
}
intarg = nextpc++;
if (isWide) {
nextpc++;
}
intarg *= -1; //mark signed
break;
case 0x99: //ifeq
case 0x9a: //ifne
case 0x9b: //iflt
case 0x9c: //ifge
case 0x9d: //ifgt
case 0x9e: //ifle
case 0x9f: //if_icmpeq
case 0xa0: //if_icmpne
case 0xa1: //if_icmplt
case 0xa2: //if_icmpge
case 0xa3: //if_icmpgt
case 0xa4: //if_icmple
case 0xa5: //if_acmpeq
case 0xa6: //if_acmpne
case 0xa7: //goto
case 0xa8: //jsr
labelarg = nextpc;
nextpc += 2;
break;
case 0xbc: //newarray
intarg = nextpc++;
break;
case 0xc6: //ifnull
case 0xc7: //ifnonnull
labelarg = nextpc;
nextpc += 2;
break;
case 0xc8: //goto_w
case 0xc9: //jsr_w
labelarg = nextpc;
nextpc += 4;
break;
// save the best for last:
case 0xaa: //tableswitch
nextpc = parseSwitch(bytes, pc, true, i);
break;
case 0xab: //lookupswitch
nextpc = parseSwitch(bytes, pc, false, i);
break;
}
String format = null;
assert ((format = opFormat(op, isWide)) != null);
//System.out.println("pc="+pc+" len="+(nextpc - pc)+" w="+isWide+" op="+op+" name="+opName(op)+" format="+format);
assert ((nextpc - pc) == format.length() || format.indexOf('t') >= 0);
// Parse out instruction fields.
if (locarg != 0) {
int len = nextpc - locarg;
if (intarg != 0) {
len /= 2; // split
}
i.setAttr("loc", "" + getInt(bytes, locarg, len));
assert ('l' == format.charAt(locarg - pc + 0));
assert ('l' == format.charAt(locarg - pc + len - 1));
}
if (cparg != 0) {
int len = nextpc - cparg;
if (len > 2) {
len = 2;
}
i.setAttr("ref", "" + getInt(bytes, cparg, len));
assert ('k' == format.charAt(cparg - pc + 0));
}
if (intarg != 0) {
boolean isSigned = (intarg < 0);
if (isSigned) {
intarg *= -1;
}
int len = nextpc - intarg;
i.setAttr("num", "" + getInt(bytes, intarg, isSigned ? -len : len));
assert ((isSigned ? 's' : 'x') == format.charAt(intarg - pc + 0));
assert ((isSigned ? 's' : 'x') == format.charAt(intarg - pc + len - 1));
}
if (labelarg != 0) {
int len = nextpc - labelarg;
int offset = getInt(bytes, labelarg, -len);
int target = pc + offset;
i.setAttr("lab", "" + target);
assert ('o' == format.charAt(labelarg - pc + 0));
assert ('o' == format.charAt(labelarg - pc + len - 1));
}
e.add(i);
tempMap[pc] = i;
isWide = willBeWide;
}
// Mark targets of branches.
for (Element i : e.elements()) {
for (int j = -1; j < i.size(); j++) {
Element c = (j < 0) ? i : (Element) i.get(j);
Number targetNum = c.getAttrNumber("lab");
if (targetNum != null) {
int target = targetNum.intValue();
Element ti = null;
if (target >= 0 && target < tempMap.length) {
ti = tempMap[target];
}
if (ti != null) {
ti.setAttr("pc", "" + target);
} else {
c.setAttr("lab.error", "");
}
}
}
}
// Shrink to fit:
for (Element i : e.elements()) {
i.trimToSize();
}
e.trimToSize();
/*
String assem = assemble(e);
if (!assem.equals(bytes)) {
System.out.println("Bytes: "+bytes);
System.out.println("Insns: "+e);
System.out.println("Assem: "+parse(assem));
}
*/
return e;
}
static int switchBase(int pc) {
int apc = pc + 1;
apc += (-apc) & 3;
return apc;
}
static int parseSwitch(String s, int pc, boolean isTable, Element i) {
int apc = switchBase(pc);
int defLabel = pc + getInt(s, apc + 4 * 0, 4);
i.setAttr("lab", "" + defLabel);
if (isTable) {
int lowCase = getInt(s, apc + 4 * 1, 4);
int highCase = getInt(s, apc + 4 * 2, 4);
int caseCount = highCase - lowCase + 1;
for (int n = 0; n < caseCount; n++) {
Element c = new Element("Case", 4);
int caseVal = lowCase + n;
int caseLab = getInt(s, apc + 4 * (3 + n), 4) + pc;
c.setAttr("num", "" + caseVal);
c.setAttr("lab", "" + caseLab);
assert (c.getExtraCapacity() == 0);
i.add(c);
}
return apc + 4 * (3 + caseCount);
} else {
int caseCount = getInt(s, apc + 4 * 1, 4);
for (int n = 0; n < caseCount; n++) {
Element c = new Element("Case", 4);
int caseVal = getInt(s, apc + 4 * (2 + (2 * n) + 0), 4);
int caseLab = getInt(s, apc + 4 * (2 + (2 * n) + 1), 4) + pc;
c.setAttr("num", "" + caseVal);
c.setAttr("lab", "" + caseLab);
assert (c.getExtraCapacity() == 0);
i.add(c);
}
return apc + 4 * (2 + 2 * caseCount);
}
}
static int getInt(String s, int pc, int len) {
//System.out.println("getInt s["+s.length()+"] pc="+pc+" len="+len);
int result = s.charAt(pc);
if (len < 0) {
len = -len;
result = (byte) result;
}
if (!(len == 1 || len == 2 || len == 4)) {
System.out.println("len=" + len);
}
assert (len == 1 || len == 2 || len == 4);
for (int i = 1; i < len; i++) {
result <<= 8;
result += s.charAt(pc + i) & 0xFF;
}
return result;
}
public static String assemble(Element instructions) {
return InstructionAssembler.assemble(instructions, null, null);
}
public static String assemble(Element instructions, String pcAttrName) {
return InstructionAssembler.assemble(instructions, pcAttrName, null);
}
public static String assemble(Element instructions, ClassSyntax.GetCPIndex getCPI) {
return InstructionAssembler.assemble(instructions, null, getCPI);
}
public static String assemble(Element instructions, String pcAttrName,
ClassSyntax.GetCPIndex getCPI) {
return InstructionAssembler.assemble(instructions, pcAttrName, getCPI);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册