diff options
author | jfrijters <jfrijters> | 2014-10-29 09:08:02 +0300 |
---|---|---|
committer | jfrijters <jfrijters> | 2014-10-29 09:08:02 +0300 |
commit | 1a9aa65dd656c1cb1d627af452e36e3e3a22fb73 (patch) | |
tree | e3c77976ea8f18c7b850d9a1d1c690da197b5f8a /openjdk | |
parent | 21158e4a082be12993c5ba7c6a8bc99c823a9b20 (diff) |
Prepare for temp fork to workaround a bug.
Diffstat (limited to 'openjdk')
-rw-r--r-- | openjdk/com/sun/java/util/jar/pack/PackageReader.java | 2383 |
1 files changed, 2383 insertions, 0 deletions
diff --git a/openjdk/com/sun/java/util/jar/pack/PackageReader.java b/openjdk/com/sun/java/util/jar/pack/PackageReader.java new file mode 100644 index 00000000..000a643f --- /dev/null +++ b/openjdk/com/sun/java/util/jar/pack/PackageReader.java @@ -0,0 +1,2383 @@ +/* + * Copyright (c) 2001, 2013, 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 com.sun.java.util.jar.pack; + +import com.sun.java.util.jar.pack.ConstantPool.*; +import com.sun.java.util.jar.pack.Package.Class; +import com.sun.java.util.jar.pack.Package.File; +import com.sun.java.util.jar.pack.Package.InnerClass; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.PrintStream; +import java.io.FilterInputStream; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; +import static com.sun.java.util.jar.pack.Constants.*; + +/** + * Reader for a package file. + * + * @see PackageWriter + * @author John Rose + */ +class PackageReader extends BandStructure { + Package pkg; + byte[] bytes; + LimitedBuffer in; + Package.Version packageVersion; + + PackageReader(Package pkg, InputStream in) throws IOException { + this.pkg = pkg; + this.in = new LimitedBuffer(in); + } + + /** A buffered input stream which is careful not to + * read its underlying stream ahead of a given mark, + * called the 'readLimit'. This property declares + * the maximum number of characters that future reads + * can consume from the underlying stream. + */ + static + class LimitedBuffer extends BufferedInputStream { + long served; // total number of charburgers served + int servedPos; // ...as of this value of super.pos + long limit; // current declared limit + long buffered; + public boolean atLimit() { + boolean z = (getBytesServed() == limit); + assert(!z || limit == buffered); + return z; + } + public long getBytesServed() { + return served + (pos - servedPos); + } + public void setReadLimit(long newLimit) { + if (newLimit == -1) + limit = -1; + else + limit = getBytesServed() + newLimit; + } + public long getReadLimit() { + if (limit == -1) + return limit; + else + return limit - getBytesServed(); + } + public int read() throws IOException { + if (pos < count) { + // fast path + return buf[pos++] & 0xFF; + } + served += (pos - servedPos); + int ch = super.read(); + servedPos = pos; + if (ch >= 0) served += 1; + assert(served <= limit || limit == -1); + return ch; + } + public int read(byte b[], int off, int len) throws IOException { + served += (pos - servedPos); + int nr = super.read(b, off, len); + servedPos = pos; + if (nr >= 0) served += nr; + //assert(served <= limit || limit == -1); + return nr; + } + public long skip(long n) throws IOException { + throw new RuntimeException("no skipping"); + } + LimitedBuffer(InputStream originalIn) { + super(null, 1<<14); + servedPos = pos; + super.in = new FilterInputStream(originalIn) { + public int read() throws IOException { + if (buffered == limit) + return -1; + ++buffered; + return super.read(); + } + public int read(byte b[], int off, int len) throws IOException { + if (buffered == limit) + return -1; + if (limit != -1) { + long remaining = limit - buffered; + if (len > remaining) + len = (int)remaining; + } + int nr = super.read(b, off, len); + if (nr >= 0) buffered += nr; + return nr; + } + }; + } + } + + void read() throws IOException { + boolean ok = false; + try { + // pack200_archive: + // file_header + // *band_headers :BYTE1 + // cp_bands + // attr_definition_bands + // ic_bands + // class_bands + // bc_bands + // file_bands + readFileHeader(); + readBandHeaders(); + readConstantPool(); // cp_bands + readAttrDefs(); + readInnerClasses(); + Class[] classes = readClasses(); + readByteCodes(); + readFiles(); // file_bands + assert(archiveSize1 == 0 || in.atLimit()); + assert(archiveSize1 == 0 || + in.getBytesServed() == archiveSize0+archiveSize1); + all_bands.doneDisbursing(); + + // As a post-pass, build constant pools and inner classes. + for (int i = 0; i < classes.length; i++) { + reconstructClass(classes[i]); + } + + ok = true; + } catch (Exception ee) { + Utils.log.warning("Error on input: "+ee, ee); + if (verbose > 0) + Utils.log.info("Stream offsets:"+ + " served="+in.getBytesServed()+ + " buffered="+in.buffered+ + " limit="+in.limit); + //if (verbose > 0) ee.printStackTrace(); + if (ee instanceof IOException) throw (IOException)ee; + if (ee instanceof RuntimeException) throw (RuntimeException)ee; + throw new Error("error unpacking", ee); + } + } + + // Temporary count values, until band decoding gets rolling. + int[] tagCount = new int[CONSTANT_Limit]; + int numFiles; + int numAttrDefs; + int numInnerClasses; + int numClasses; + + void readFileHeader() throws IOException { + // file_header: + // archive_magic archive_header + readArchiveMagic(); + readArchiveHeader(); + } + + // Local routine used to parse fixed-format scalars + // in the file_header: + private int getMagicInt32() throws IOException { + int res = 0; + for (int i = 0; i < 4; i++) { + res <<= 8; + res |= (archive_magic.getByte() & 0xFF); + } + return res; + } + + final static int MAGIC_BYTES = 4; + + void readArchiveMagic() throws IOException { + // Read a minimum of bytes in the first gulp. + in.setReadLimit(MAGIC_BYTES + AH_LENGTH_MIN); + + // archive_magic: + // #archive_magic_word :BYTE1[4] + archive_magic.expectLength(MAGIC_BYTES); + archive_magic.readFrom(in); + + // read and check magic numbers: + int magic = getMagicInt32(); + if (pkg.magic != magic) { + throw new IOException("Unexpected package magic number: got " + + magic + "; expected " + pkg.magic); + } + archive_magic.doneDisbursing(); + } + + // Fixed 6211177, converted to throw IOException + void checkArchiveVersion() throws IOException { + Package.Version versionFound = null; + for (Package.Version v : new Package.Version[] { + JAVA8_PACKAGE_VERSION, + JAVA7_PACKAGE_VERSION, + JAVA6_PACKAGE_VERSION, + JAVA5_PACKAGE_VERSION + }) { + if (packageVersion.equals(v)) { + versionFound = v; + break; + } + } + if (versionFound == null) { + String expVer = JAVA8_PACKAGE_VERSION.toString() + + "OR" + + JAVA7_PACKAGE_VERSION.toString() + + " OR " + + JAVA6_PACKAGE_VERSION.toString() + + " OR " + + JAVA5_PACKAGE_VERSION.toString(); + throw new IOException("Unexpected package minor version: got " + + packageVersion.toString() + "; expected " + expVer); + } + } + + void readArchiveHeader() throws IOException { + // archive_header: + // #archive_minver :UNSIGNED5[1] + // #archive_majver :UNSIGNED5[1] + // #archive_options :UNSIGNED5[1] + // (archive_file_counts) ** (#have_file_headers) + // (archive_special_counts) ** (#have_special_formats) + // cp_counts + // class_counts + // + // archive_file_counts: + // #archive_size_hi :UNSIGNED5[1] + // #archive_size_lo :UNSIGNED5[1] + // #archive_next_count :UNSIGNED5[1] + // #archive_modtime :UNSIGNED5[1] + // #file_count :UNSIGNED5[1] + // + // class_counts: + // #ic_count :UNSIGNED5[1] + // #default_class_minver :UNSIGNED5[1] + // #default_class_majver :UNSIGNED5[1] + // #class_count :UNSIGNED5[1] + // + // archive_special_counts: + // #band_headers_size :UNSIGNED5[1] + // #attr_definition_count :UNSIGNED5[1] + // + archive_header_0.expectLength(AH_LENGTH_0); + archive_header_0.readFrom(in); + + int minver = archive_header_0.getInt(); + int majver = archive_header_0.getInt(); + packageVersion = Package.Version.of(majver, minver); + checkArchiveVersion(); + this.initHighestClassVersion(JAVA7_MAX_CLASS_VERSION); + + archiveOptions = archive_header_0.getInt(); + archive_header_0.doneDisbursing(); + + // detect archive optional fields in archive header + boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS); + boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS); + boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS); + boolean haveCPExtra = testBit(archiveOptions, AO_HAVE_CP_EXTRAS); + initAttrIndexLimit(); + + // now we are ready to use the data: + archive_header_S.expectLength(haveFiles? AH_LENGTH_S: 0); + archive_header_S.readFrom(in); + if (haveFiles) { + long sizeHi = archive_header_S.getInt(); + long sizeLo = archive_header_S.getInt(); + archiveSize1 = (sizeHi << 32) + ((sizeLo << 32) >>> 32); + // Set the limit, now, up to the file_bits. + in.setReadLimit(archiveSize1); // for debug only + } else { + archiveSize1 = 0; + in.setReadLimit(-1); // remove limitation + } + archive_header_S.doneDisbursing(); + archiveSize0 = in.getBytesServed(); + + int remainingHeaders = AH_LENGTH_MIN - AH_LENGTH_0 - AH_LENGTH_S; + if (haveFiles) remainingHeaders += AH_FILE_HEADER_LEN; + if (haveSpecial) remainingHeaders += AH_SPECIAL_FORMAT_LEN; + if (haveNumbers) remainingHeaders += AH_CP_NUMBER_LEN; + if (haveCPExtra) remainingHeaders += AH_CP_EXTRA_LEN; + archive_header_1.expectLength(remainingHeaders); + archive_header_1.readFrom(in); + + if (haveFiles) { + archiveNextCount = archive_header_1.getInt(); + pkg.default_modtime = archive_header_1.getInt(); + numFiles = archive_header_1.getInt(); + } else { + archiveNextCount = 0; + numFiles = 0; + } + + if (haveSpecial) { + band_headers.expectLength(archive_header_1.getInt()); + numAttrDefs = archive_header_1.getInt(); + } else { + band_headers.expectLength(0); + numAttrDefs = 0; + } + + readConstantPoolCounts(haveNumbers, haveCPExtra); + + numInnerClasses = archive_header_1.getInt(); + + minver = (short) archive_header_1.getInt(); + majver = (short) archive_header_1.getInt(); + pkg.defaultClassVersion = Package.Version.of(majver, minver); + numClasses = archive_header_1.getInt(); + + archive_header_1.doneDisbursing(); + + // set some derived archive bits + if (testBit(archiveOptions, AO_DEFLATE_HINT)) { + pkg.default_options |= FO_DEFLATE_HINT; + } + } + + void readBandHeaders() throws IOException { + band_headers.readFrom(in); + bandHeaderBytePos = 1; // Leave room to pushback the initial XB byte. + bandHeaderBytes = new byte[bandHeaderBytePos + band_headers.length()]; + for (int i = bandHeaderBytePos; i < bandHeaderBytes.length; i++) { + bandHeaderBytes[i] = (byte) band_headers.getByte(); + } + band_headers.doneDisbursing(); + } + + void readConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException { + // size the constant pools: + for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { + // cp_counts: + // #cp_Utf8_count :UNSIGNED5[1] + // (cp_number_counts) ** (#have_cp_numbers) + // #cp_String_count :UNSIGNED5[1] + // #cp_Class_count :UNSIGNED5[1] + // #cp_Signature_count :UNSIGNED5[1] + // #cp_Descr_count :UNSIGNED5[1] + // #cp_Field_count :UNSIGNED5[1] + // #cp_Method_count :UNSIGNED5[1] + // #cp_Imethod_count :UNSIGNED5[1] + // (cp_attr_counts) ** (#have_cp_attr_counts) + // + // cp_number_counts: + // #cp_Int_count :UNSIGNED5[1] + // #cp_Float_count :UNSIGNED5[1] + // #cp_Long_count :UNSIGNED5[1] + // #cp_Double_count :UNSIGNED5[1] + // + // cp_extra_counts: + // #cp_MethodHandle_count :UNSIGNED5[1] + // #cp_MethodType_count :UNSIGNED5[1] + // #cp_InvokeDynamic_count :UNSIGNED5[1] + // #cp_BootstrapMethod_count :UNSIGNED5[1] + // + byte tag = ConstantPool.TAGS_IN_ORDER[k]; + if (!haveNumbers) { + // These four counts are optional. + switch (tag) { + case CONSTANT_Integer: + case CONSTANT_Float: + case CONSTANT_Long: + case CONSTANT_Double: + 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(); + } + } + + protected Index getCPIndex(byte tag) { + return pkg.cp.getIndexByTag(tag); + } + Index initCPIndex(byte tag, Entry[] cpMap) { + if (verbose > 3) { + for (int i = 0; i < cpMap.length; i++) { + Utils.log.fine("cp.add "+cpMap[i]); + } + } + Index index = ConstantPool.makeIndex(ConstantPool.tagName(tag), cpMap); + if (verbose > 1) Utils.log.fine("Read "+index); + pkg.cp.initIndexByTag(tag, index); + return index; + } + + void checkLegacy(String bandname) { + if (packageVersion.lessThan(JAVA7_PACKAGE_VERSION)) { + throw new RuntimeException("unexpected band " + bandname); + } + } + void readConstantPool() throws IOException { + // cp_bands: + // cp_Utf8 + // *cp_Int :UDELTA5 + // *cp_Float :UDELTA5 + // cp_Long + // cp_Double + // *cp_String :UDELTA5 (cp_Utf8) + // *cp_Class :UDELTA5 (cp_Utf8) + // cp_Signature + // cp_Descr + // cp_Field + // cp_Method + // cp_Imethod + + if (verbose > 0) Utils.log.info("Reading CP"); + + for (int k = 0; k < ConstantPool.TAGS_IN_ORDER.length; k++) { + byte tag = ConstantPool.TAGS_IN_ORDER[k]; + int len = tagCount[tag]; + + Entry[] cpMap = new Entry[len]; + if (verbose > 0) + Utils.log.info("Reading "+cpMap.length+" "+ConstantPool.tagName(tag)+" entries..."); + + switch (tag) { + case CONSTANT_Utf8: + readUtf8Bands(cpMap); + break; + case CONSTANT_Integer: + cp_Int.expectLength(cpMap.length); + cp_Int.readFrom(in); + for (int i = 0; i < cpMap.length; i++) { + int x = cp_Int.getInt(); // coding handles signs OK + cpMap[i] = ConstantPool.getLiteralEntry(x); + } + cp_Int.doneDisbursing(); + break; + case CONSTANT_Float: + cp_Float.expectLength(cpMap.length); + cp_Float.readFrom(in); + for (int i = 0; i < cpMap.length; i++) { + int x = cp_Float.getInt(); + float fx = Float.intBitsToFloat(x); + cpMap[i] = ConstantPool.getLiteralEntry(fx); + } + cp_Float.doneDisbursing(); + break; + case CONSTANT_Long: + // cp_Long: + // *cp_Long_hi :UDELTA5 + // *cp_Long_lo :DELTA5 + cp_Long_hi.expectLength(cpMap.length); + cp_Long_hi.readFrom(in); + cp_Long_lo.expectLength(cpMap.length); + cp_Long_lo.readFrom(in); + for (int i = 0; i < cpMap.length; i++) { + long hi = cp_Long_hi.getInt(); + long lo = cp_Long_lo.getInt(); + long x = (hi << 32) + ((lo << 32) >>> 32); + cpMap[i] = ConstantPool.getLiteralEntry(x); + } + cp_Long_hi.doneDisbursing(); + cp_Long_lo.doneDisbursing(); + break; + case CONSTANT_Double: + // cp_Double: + // *cp_Double_hi :UDELTA5 + // *cp_Double_lo :DELTA5 + cp_Double_hi.expectLength(cpMap.length); + cp_Double_hi.readFrom(in); + cp_Double_lo.expectLength(cpMap.length); + cp_Double_lo.readFrom(in); + for (int i = 0; i < cpMap.length; i++) { + long hi = cp_Double_hi.getInt(); + long lo = cp_Double_lo.getInt(); + long x = (hi << 32) + ((lo << 32) >>> 32); + double dx = Double.longBitsToDouble(x); + cpMap[i] = ConstantPool.getLiteralEntry(dx); + } + cp_Double_hi.doneDisbursing(); + cp_Double_lo.doneDisbursing(); + break; + case CONSTANT_String: + cp_String.expectLength(cpMap.length); + cp_String.readFrom(in); + cp_String.setIndex(getCPIndex(CONSTANT_Utf8)); + for (int i = 0; i < cpMap.length; i++) { + cpMap[i] = ConstantPool.getLiteralEntry(cp_String.getRef().stringValue()); + } + cp_String.doneDisbursing(); + break; + case CONSTANT_Class: + cp_Class.expectLength(cpMap.length); + cp_Class.readFrom(in); + cp_Class.setIndex(getCPIndex(CONSTANT_Utf8)); + for (int i = 0; i < cpMap.length; i++) { + cpMap[i] = ConstantPool.getClassEntry(cp_Class.getRef().stringValue()); + } + cp_Class.doneDisbursing(); + break; + case CONSTANT_Signature: + readSignatureBands(cpMap); + break; + case CONSTANT_NameandType: + // cp_Descr: + // *cp_Descr_type :DELTA5 (cp_Signature) + // *cp_Descr_name :UDELTA5 (cp_Utf8) + cp_Descr_name.expectLength(cpMap.length); + cp_Descr_name.readFrom(in); + cp_Descr_name.setIndex(getCPIndex(CONSTANT_Utf8)); + cp_Descr_type.expectLength(cpMap.length); + cp_Descr_type.readFrom(in); + cp_Descr_type.setIndex(getCPIndex(CONSTANT_Signature)); + for (int i = 0; i < cpMap.length; i++) { + Entry ref = cp_Descr_name.getRef(); + Entry ref2 = cp_Descr_type.getRef(); + cpMap[i] = ConstantPool.getDescriptorEntry((Utf8Entry)ref, + (SignatureEntry)ref2); + } + cp_Descr_name.doneDisbursing(); + cp_Descr_type.doneDisbursing(); + break; + case CONSTANT_Fieldref: + readMemberRefs(tag, cpMap, cp_Field_class, cp_Field_desc); + break; + case CONSTANT_Methodref: + readMemberRefs(tag, cpMap, cp_Method_class, cp_Method_desc); + break; + case CONSTANT_InterfaceMethodref: + readMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); + break; + case CONSTANT_MethodHandle: + if (cpMap.length > 0) { + checkLegacy(cp_MethodHandle_refkind.name()); + } + cp_MethodHandle_refkind.expectLength(cpMap.length); + cp_MethodHandle_refkind.readFrom(in); + cp_MethodHandle_member.expectLength(cpMap.length); + cp_MethodHandle_member.readFrom(in); + cp_MethodHandle_member.setIndex(getCPIndex(CONSTANT_AnyMember)); + for (int i = 0; i < cpMap.length; i++) { + byte refKind = (byte) cp_MethodHandle_refkind.getInt(); + MemberEntry memRef = (MemberEntry) cp_MethodHandle_member.getRef(); + cpMap[i] = ConstantPool.getMethodHandleEntry(refKind, memRef); + } + cp_MethodHandle_refkind.doneDisbursing(); + cp_MethodHandle_member.doneDisbursing(); + break; + case CONSTANT_MethodType: + if (cpMap.length > 0) { + checkLegacy(cp_MethodType.name()); + } + cp_MethodType.expectLength(cpMap.length); + cp_MethodType.readFrom(in); + cp_MethodType.setIndex(getCPIndex(CONSTANT_Signature)); + for (int i = 0; i < cpMap.length; i++) { + SignatureEntry typeRef = (SignatureEntry) cp_MethodType.getRef(); + cpMap[i] = ConstantPool.getMethodTypeEntry(typeRef); + } + cp_MethodType.doneDisbursing(); + break; + case CONSTANT_InvokeDynamic: + if (cpMap.length > 0) { + checkLegacy(cp_InvokeDynamic_spec.name()); + } + cp_InvokeDynamic_spec.expectLength(cpMap.length); + cp_InvokeDynamic_spec.readFrom(in); + cp_InvokeDynamic_spec.setIndex(getCPIndex(CONSTANT_BootstrapMethod)); + cp_InvokeDynamic_desc.expectLength(cpMap.length); + cp_InvokeDynamic_desc.readFrom(in); + cp_InvokeDynamic_desc.setIndex(getCPIndex(CONSTANT_NameandType)); + for (int i = 0; i < cpMap.length; i++) { + BootstrapMethodEntry bss = (BootstrapMethodEntry) cp_InvokeDynamic_spec.getRef(); + DescriptorEntry descr = (DescriptorEntry) cp_InvokeDynamic_desc.getRef(); + cpMap[i] = ConstantPool.getInvokeDynamicEntry(bss, descr); + } + cp_InvokeDynamic_spec.doneDisbursing(); + cp_InvokeDynamic_desc.doneDisbursing(); + break; + case CONSTANT_BootstrapMethod: + if (cpMap.length > 0) { + checkLegacy(cp_BootstrapMethod_ref.name()); + } + cp_BootstrapMethod_ref.expectLength(cpMap.length); + cp_BootstrapMethod_ref.readFrom(in); + cp_BootstrapMethod_ref.setIndex(getCPIndex(CONSTANT_MethodHandle)); + cp_BootstrapMethod_arg_count.expectLength(cpMap.length); + cp_BootstrapMethod_arg_count.readFrom(in); + int totalArgCount = cp_BootstrapMethod_arg_count.getIntTotal(); + cp_BootstrapMethod_arg.expectLength(totalArgCount); + cp_BootstrapMethod_arg.readFrom(in); + cp_BootstrapMethod_arg.setIndex(getCPIndex(CONSTANT_LoadableValue)); + for (int i = 0; i < cpMap.length; i++) { + MethodHandleEntry bsm = (MethodHandleEntry) cp_BootstrapMethod_ref.getRef(); + int argc = cp_BootstrapMethod_arg_count.getInt(); + Entry[] argRefs = new Entry[argc]; + for (int j = 0; j < argc; j++) { + argRefs[j] = cp_BootstrapMethod_arg.getRef(); + } + cpMap[i] = ConstantPool.getBootstrapMethodEntry(bsm, argRefs); + } + cp_BootstrapMethod_ref.doneDisbursing(); + cp_BootstrapMethod_arg_count.doneDisbursing(); + cp_BootstrapMethod_arg.doneDisbursing(); + break; + default: + throw new AssertionError("unexpected CP tag in package"); + } + + Index index = initCPIndex(tag, cpMap); + + if (optDumpBands) { + try (PrintStream ps = new PrintStream(getDumpStream(index, ".idx"))) { + printArrayTo(ps, index.cpMap, 0, index.cpMap.length); + } + } + } + + 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(); + } + + void readUtf8Bands(Entry[] cpMap) throws IOException { + // cp_Utf8: + // *cp_Utf8_prefix :DELTA5 + // *cp_Utf8_suffix :UNSIGNED5 + // *cp_Utf8_chars :CHAR3 + // *cp_Utf8_big_suffix :DELTA5 + // (*cp_Utf8_big_chars :DELTA5) + // ** length(cp_Utf8_big_suffix) + int len = cpMap.length; + if (len == 0) + return; // nothing to read + + // Bands have implicit leading zeroes, for the empty string: + final int SUFFIX_SKIP_1 = 1; + final int PREFIX_SKIP_2 = 2; + + // First band: Read lengths of shared prefixes. + cp_Utf8_prefix.expectLength(Math.max(0, len - PREFIX_SKIP_2)); + cp_Utf8_prefix.readFrom(in); + + // Second band: Read lengths of unshared suffixes: + cp_Utf8_suffix.expectLength(Math.max(0, len - SUFFIX_SKIP_1)); + cp_Utf8_suffix.readFrom(in); + + char[][] suffixChars = new char[len][]; + int bigSuffixCount = 0; + + // Third band: Read the char values in the unshared suffixes: + cp_Utf8_chars.expectLength(cp_Utf8_suffix.getIntTotal()); + cp_Utf8_chars.readFrom(in); + for (int i = 0; i < len; i++) { + int suffix = (i < SUFFIX_SKIP_1)? 0: cp_Utf8_suffix.getInt(); + if (suffix == 0 && i >= SUFFIX_SKIP_1) { + // chars are packed in cp_Utf8_big_chars + bigSuffixCount += 1; + continue; + } + suffixChars[i] = new char[suffix]; + for (int j = 0; j < suffix; j++) { + int ch = cp_Utf8_chars.getInt(); + assert(ch == (char)ch); + suffixChars[i][j] = (char)ch; + } + } + cp_Utf8_chars.doneDisbursing(); + + // Fourth band: Go back and size the specially packed strings. + int maxChars = 0; + cp_Utf8_big_suffix.expectLength(bigSuffixCount); + cp_Utf8_big_suffix.readFrom(in); + cp_Utf8_suffix.resetForSecondPass(); + for (int i = 0; i < len; i++) { + int suffix = (i < SUFFIX_SKIP_1)? 0: cp_Utf8_suffix.getInt(); + int prefix = (i < PREFIX_SKIP_2)? 0: cp_Utf8_prefix.getInt(); + if (suffix == 0 && i >= SUFFIX_SKIP_1) { + assert(suffixChars[i] == null); + suffix = cp_Utf8_big_suffix.getInt(); + } else { + assert(suffixChars[i] != null); + } + if (maxChars < prefix + suffix) + maxChars = prefix + suffix; + } + char[] buf = new char[maxChars]; + + // Fifth band(s): Get the specially packed characters. + cp_Utf8_suffix.resetForSecondPass(); + cp_Utf8_big_suffix.resetForSecondPass(); + for (int i = 0; i < len; i++) { + if (i < SUFFIX_SKIP_1) continue; + int suffix = cp_Utf8_suffix.getInt(); + if (suffix != 0) continue; // already input + suffix = cp_Utf8_big_suffix.getInt(); + suffixChars[i] = new char[suffix]; + if (suffix == 0) { + // Do not bother to add an empty "(Utf8_big_0)" band. + continue; + } + IntBand packed = cp_Utf8_big_chars.newIntBand("(Utf8_big_"+i+")"); + packed.expectLength(suffix); + packed.readFrom(in); + for (int j = 0; j < suffix; j++) { + int ch = packed.getInt(); + assert(ch == (char)ch); + suffixChars[i][j] = (char)ch; + } + packed.doneDisbursing(); + } + cp_Utf8_big_chars.doneDisbursing(); + + // Finally, sew together all the prefixes and suffixes. + cp_Utf8_prefix.resetForSecondPass(); + cp_Utf8_suffix.resetForSecondPass(); + cp_Utf8_big_suffix.resetForSecondPass(); + for (int i = 0; i < len; i++) { + int prefix = (i < PREFIX_SKIP_2)? 0: cp_Utf8_prefix.getInt(); + int suffix = (i < SUFFIX_SKIP_1)? 0: cp_Utf8_suffix.getInt(); + if (suffix == 0 && i >= SUFFIX_SKIP_1) + suffix = cp_Utf8_big_suffix.getInt(); + + // by induction, the buffer is already filled with the prefix + System.arraycopy(suffixChars[i], 0, buf, prefix, suffix); + + cpMap[i] = ConstantPool.getUtf8Entry(new String(buf, 0, prefix+suffix)); + } + + cp_Utf8_prefix.doneDisbursing(); + cp_Utf8_suffix.doneDisbursing(); + cp_Utf8_big_suffix.doneDisbursing(); + } + + Map<Utf8Entry, SignatureEntry> utf8Signatures; + + void readSignatureBands(Entry[] cpMap) throws IOException { + // cp_Signature: + // *cp_Signature_form :DELTA5 (cp_Utf8) + // *cp_Signature_classes :UDELTA5 (cp_Class) + cp_Signature_form.expectLength(cpMap.length); + cp_Signature_form.readFrom(in); + cp_Signature_form.setIndex(getCPIndex(CONSTANT_Utf8)); + int[] numSigClasses = new int[cpMap.length]; + for (int i = 0; i < cpMap.length; i++) { + Utf8Entry formRef = (Utf8Entry) cp_Signature_form.getRef(); + numSigClasses[i] = ConstantPool.countClassParts(formRef); + } + cp_Signature_form.resetForSecondPass(); + cp_Signature_classes.expectLength(getIntTotal(numSigClasses)); + cp_Signature_classes.readFrom(in); + cp_Signature_classes.setIndex(getCPIndex(CONSTANT_Class)); + utf8Signatures = new HashMap<>(); + for (int i = 0; i < cpMap.length; i++) { + Utf8Entry formRef = (Utf8Entry) cp_Signature_form.getRef(); + ClassEntry[] classRefs = new ClassEntry[numSigClasses[i]]; + for (int j = 0; j < classRefs.length; j++) { + classRefs[j] = (ClassEntry) cp_Signature_classes.getRef(); + } + SignatureEntry se = ConstantPool.getSignatureEntry(formRef, classRefs); + cpMap[i] = se; + utf8Signatures.put(se.asUtf8Entry(), se); + } + cp_Signature_form.doneDisbursing(); + cp_Signature_classes.doneDisbursing(); + } + + void readMemberRefs(byte tag, Entry[] cpMap, CPRefBand cp_class, CPRefBand cp_desc) throws IOException { + // cp_Field: + // *cp_Field_class :DELTA5 (cp_Class) + // *cp_Field_desc :UDELTA5 (cp_Descr) + // cp_Method: + // *cp_Method_class :DELTA5 (cp_Class) + // *cp_Method_desc :UDELTA5 (cp_Descr) + // cp_Imethod: + // *cp_Imethod_class :DELTA5 (cp_Class) + // *cp_Imethod_desc :UDELTA5 (cp_Descr) + cp_class.expectLength(cpMap.length); + cp_class.readFrom(in); + cp_class.setIndex(getCPIndex(CONSTANT_Class)); + cp_desc.expectLength(cpMap.length); + cp_desc.readFrom(in); + cp_desc.setIndex(getCPIndex(CONSTANT_NameandType)); + for (int i = 0; i < cpMap.length; i++) { + ClassEntry mclass = (ClassEntry ) cp_class.getRef(); + DescriptorEntry mdescr = (DescriptorEntry) cp_desc.getRef(); + cpMap[i] = ConstantPool.getMemberEntry(tag, mclass, mdescr); + } + cp_class.doneDisbursing(); + cp_desc.doneDisbursing(); + } + + void readFiles() throws IOException { + // file_bands: + // *file_name :UNSIGNED5 (cp_Utf8) + // *file_size_hi :UNSIGNED5 + // *file_size_lo :UNSIGNED5 + // *file_modtime :DELTA5 + // *file_options :UNSIGNED5 + // *file_bits :BYTE1 + if (verbose > 0) + Utils.log.info(" ...building "+numFiles+" files..."); + file_name.expectLength(numFiles); + file_size_lo.expectLength(numFiles); + int options = archiveOptions; + boolean haveSizeHi = testBit(options, AO_HAVE_FILE_SIZE_HI); + boolean haveModtime = testBit(options, AO_HAVE_FILE_MODTIME); + boolean haveOptions = testBit(options, AO_HAVE_FILE_OPTIONS); + if (haveSizeHi) + file_size_hi.expectLength(numFiles); + if (haveModtime) + file_modtime.expectLength(numFiles); + if (haveOptions) + file_options.expectLength(numFiles); + + file_name.readFrom(in); + file_size_hi.readFrom(in); + file_size_lo.readFrom(in); + file_modtime.readFrom(in); + file_options.readFrom(in); + file_bits.setInputStreamFrom(in); + + Iterator<Class> nextClass = pkg.getClasses().iterator(); + + // Compute file lengths before reading any file bits. + long totalFileLength = 0; + long[] fileLengths = new long[numFiles]; + for (int i = 0; i < numFiles; i++) { + long size = ((long)file_size_lo.getInt() << 32) >>> 32; + if (haveSizeHi) + size += (long)file_size_hi.getInt() << 32; + fileLengths[i] = size; + totalFileLength += size; + } + assert(in.getReadLimit() == -1 || in.getReadLimit() == totalFileLength); + + byte[] buf = new byte[1<<16]; + for (int i = 0; i < numFiles; i++) { + // %%% Use a big temp file for file bits? + Utf8Entry name = (Utf8Entry) file_name.getRef(); + long size = fileLengths[i]; + File file = pkg.new File(name); + file.modtime = pkg.default_modtime; + file.options = pkg.default_options; + if (haveModtime) + file.modtime += file_modtime.getInt(); + if (haveOptions) + file.options |= file_options.getInt(); + if (verbose > 1) + Utils.log.fine("Reading "+size+" bytes of "+name.stringValue()); + long toRead = size; + while (toRead > 0) { + int nr = buf.length; + if (nr > toRead) nr = (int) toRead; + nr = file_bits.getInputStream().read(buf, 0, nr); + if (nr < 0) throw new EOFException(); + file.addBytes(buf, 0, nr); + toRead -= nr; + } + pkg.addFile(file); + if (file.isClassStub()) { + assert(file.getFileLength() == 0); + Class cls = nextClass.next(); + cls.initFile(file); + } + } + + // Do the rest of the classes. + while (nextClass.hasNext()) { + Class cls = nextClass.next(); + cls.initFile(null); // implicitly initialize to a trivial one + cls.file.modtime = pkg.default_modtime; + } + + file_name.doneDisbursing(); + file_size_hi.doneDisbursing(); + file_size_lo.doneDisbursing(); + file_modtime.doneDisbursing(); + file_options.doneDisbursing(); + file_bits.doneDisbursing(); + file_bands.doneDisbursing(); + + if (archiveSize1 != 0 && !in.atLimit()) { + throw new RuntimeException("Predicted archive_size "+ + archiveSize1+" != "+ + (in.getBytesServed()-archiveSize0)); + } + } + + void readAttrDefs() throws IOException { + // attr_definition_bands: + // *attr_definition_headers :BYTE1 + // *attr_definition_name :UNSIGNED5 (cp_Utf8) + // *attr_definition_layout :UNSIGNED5 (cp_Utf8) + attr_definition_headers.expectLength(numAttrDefs); + attr_definition_name.expectLength(numAttrDefs); + attr_definition_layout.expectLength(numAttrDefs); + attr_definition_headers.readFrom(in); + attr_definition_name.readFrom(in); + attr_definition_layout.readFrom(in); + try (PrintStream dump = !optDumpBands ? null + : new PrintStream(getDumpStream(attr_definition_headers, ".def"))) + { + for (int i = 0; i < numAttrDefs; i++) { + int header = attr_definition_headers.getByte(); + Utf8Entry name = (Utf8Entry) attr_definition_name.getRef(); + Utf8Entry layout = (Utf8Entry) attr_definition_layout.getRef(); + int ctype = (header & ADH_CONTEXT_MASK); + int index = (header >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB; + Attribute.Layout def = new Attribute.Layout(ctype, + name.stringValue(), + layout.stringValue()); + // Check layout string for Java 6 extensions. + String pvLayout = def.layoutForClassVersion(getHighestClassVersion()); + if (!pvLayout.equals(def.layout())) { + throw new IOException("Bad attribute layout in archive: "+def.layout()); + } + this.setAttributeLayoutIndex(def, index); + if (dump != null) dump.println(index+" "+def); + } + } + attr_definition_headers.doneDisbursing(); + attr_definition_name.doneDisbursing(); + attr_definition_layout.doneDisbursing(); + // Attribute layouts define bands, one per layout element. + // Create them now, all at once. + makeNewAttributeBands(); + attr_definition_bands.doneDisbursing(); + } + + void readInnerClasses() throws IOException { + // ic_bands: + // *ic_this_class :UDELTA5 (cp_Class) + // *ic_flags :UNSIGNED5 + // *ic_outer_class :DELTA5 (null or cp_Class) + // *ic_name :DELTA5 (null or cp_Utf8) + ic_this_class.expectLength(numInnerClasses); + ic_this_class.readFrom(in); + ic_flags.expectLength(numInnerClasses); + ic_flags.readFrom(in); + int longICCount = 0; + for (int i = 0; i < numInnerClasses; i++) { + int flags = ic_flags.getInt(); + boolean longForm = (flags & ACC_IC_LONG_FORM) != 0; + if (longForm) { + longICCount += 1; + } + } + ic_outer_class.expectLength(longICCount); + ic_outer_class.readFrom(in); + ic_name.expectLength(longICCount); + ic_name.readFrom(in); + ic_flags.resetForSecondPass(); + List<InnerClass> icList = new ArrayList<>(numInnerClasses); + for (int i = 0; i < numInnerClasses; i++) { + int flags = ic_flags.getInt(); + boolean longForm = (flags & ACC_IC_LONG_FORM) != 0; + flags &= ~ACC_IC_LONG_FORM; + ClassEntry thisClass = (ClassEntry) ic_this_class.getRef(); + ClassEntry outerClass; + Utf8Entry thisName; + if (longForm) { + outerClass = (ClassEntry) ic_outer_class.getRef(); + thisName = (Utf8Entry) ic_name.getRef(); + } else { + String n = thisClass.stringValue(); + String[] parse = Package.parseInnerClassName(n); + assert(parse != null); + String pkgOuter = parse[0]; + //String number = parse[1]; + String name = parse[2]; + if (pkgOuter == null) + outerClass = null; + else + outerClass = ConstantPool.getClassEntry(pkgOuter); + if (name == null) + thisName = null; + else + thisName = ConstantPool.getUtf8Entry(name); + } + InnerClass ic = + new InnerClass(thisClass, outerClass, thisName, flags); + assert(longForm || ic.predictable); + icList.add(ic); + } + ic_flags.doneDisbursing(); + ic_this_class.doneDisbursing(); + ic_outer_class.doneDisbursing(); + ic_name.doneDisbursing(); + pkg.setAllInnerClasses(icList); + ic_bands.doneDisbursing(); + } + + void readLocalInnerClasses(Class cls) throws IOException { + int nc = class_InnerClasses_N.getInt(); + List<InnerClass> localICs = new ArrayList<>(nc); + for (int i = 0; i < nc; i++) { + ClassEntry thisClass = (ClassEntry) class_InnerClasses_RC.getRef(); + int flags = class_InnerClasses_F.getInt(); + if (flags == 0) { + // A zero flag means copy a global IC here. + InnerClass ic = pkg.getGlobalInnerClass(thisClass); + assert(ic != null); // must be a valid global IC reference + localICs.add(ic); + } else { + if (flags == ACC_IC_LONG_FORM) + flags = 0; // clear the marker bit + ClassEntry outer = (ClassEntry) class_InnerClasses_outer_RCN.getRef(); + Utf8Entry name = (Utf8Entry) class_InnerClasses_name_RUN.getRef(); + localICs.add(new InnerClass(thisClass, outer, name, flags)); + } + } + cls.setInnerClasses(localICs); + // cls.expandLocalICs may add more tuples to ics also, + // or may even delete tuples. + // We cannot do that now, because we do not know the + // full contents of the local constant pool yet. + } + + static final int NO_FLAGS_YET = 0; // placeholder for later flag read-in + + Class[] readClasses() throws IOException { + // class_bands: + // *class_this :DELTA5 (cp_Class) + // *class_super :DELTA5 (cp_Class) + // *class_interface_count :DELTA5 + // *class_interface :DELTA5 (cp_Class) + // ...(member bands)... + // class_attr_bands + // code_bands + Class[] classes = new Class[numClasses]; + if (verbose > 0) + Utils.log.info(" ...building "+classes.length+" classes..."); + + class_this.expectLength(numClasses); + class_super.expectLength(numClasses); + class_interface_count.expectLength(numClasses); + + class_this.readFrom(in); + class_super.readFrom(in); + class_interface_count.readFrom(in); + class_interface.expectLength(class_interface_count.getIntTotal()); + class_interface.readFrom(in); + for (int i = 0; i < classes.length; i++) { + ClassEntry thisClass = (ClassEntry) class_this.getRef(); + ClassEntry superClass = (ClassEntry) class_super.getRef(); + ClassEntry[] interfaces = new ClassEntry[class_interface_count.getInt()]; + for (int j = 0; j < interfaces.length; j++) { + interfaces[j] = (ClassEntry) class_interface.getRef(); + } + // Packer encoded rare case of null superClass as thisClass: + if (superClass == thisClass) superClass = null; + Class cls = pkg.new Class(NO_FLAGS_YET, + thisClass, superClass, interfaces); + classes[i] = cls; + } + class_this.doneDisbursing(); + class_super.doneDisbursing(); + class_interface_count.doneDisbursing(); + class_interface.doneDisbursing(); + readMembers(classes); + countAndReadAttrs(ATTR_CONTEXT_CLASS, Arrays.asList(classes)); + pkg.trimToSize(); + readCodeHeaders(); + //code_bands.doneDisbursing(); // still need to read code attrs + //class_bands.doneDisbursing(); // still need to read code attrs + return classes; + } + + private int getOutputIndex(Entry e) { + // Output CPs do not contain signatures. + assert(e.tag != CONSTANT_Signature); + int k = pkg.cp.untypedIndexOf(e); + // In the output ordering, input signatures can serve + // in place of Utf8s. + if (k >= 0) + return k; + if (e.tag == CONSTANT_Utf8) { + Entry se = utf8Signatures.get(e); + return pkg.cp.untypedIndexOf(se); + } + return -1; + } + + Comparator<Entry> entryOutputOrder = new Comparator<Entry>() { + public int compare(Entry e0, Entry e1) { + int k0 = getOutputIndex(e0); + int k1 = getOutputIndex(e1); + if (k0 >= 0 && k1 >= 0) + // If both have keys, use the keys. + return k0 - k1; + if (k0 == k1) + // If neither have keys, use their native tags & spellings. + return e0.compareTo(e1); + // Otherwise, the guy with the key comes first. + return (k0 >= 0)? 0-1: 1-0; + } + }; + + void reconstructClass(Class cls) { + if (verbose > 1) Utils.log.fine("reconstruct "+cls); + + // check for local .ClassFile.version + Attribute retroVersion = cls.getAttribute(attrClassFileVersion); + if (retroVersion != null) { + cls.removeAttribute(retroVersion); + cls.version = parseClassFileVersionAttr(retroVersion); + } else { + cls.version = pkg.defaultClassVersion; + } + + // Replace null SourceFile by "obvious" string. + cls.expandSourceFile(); + + // record the local cp: + cls.setCPMap(reconstructLocalCPMap(cls)); + } + + Entry[] reconstructLocalCPMap(Class cls) { + Set<Entry> ldcRefs = ldcRefMap.get(cls); + Set<Entry> cpRefs = new HashSet<>(); + + // look for constant pool entries: + 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 + ConstantPool.completeReferencesIn(cpRefs, true, bsms); + + // Now that we know all our local class references, + // compute the InnerClasses attribute. + int changed = cls.expandLocalICs(); + + if (changed != 0) { + if (changed > 0) { + // Just visit the expanded InnerClasses attr. + cls.visitInnerClassRefs(VRM_CLASSIC, cpRefs); + } else { + // Have to recompute from scratch, because of deletions. + cpRefs.clear(); + cls.visitRefs(VRM_CLASSIC, cpRefs); + } + + // flesh out the local constant pool, again + ConstantPool.completeReferencesIn(cpRefs, true, bsms); + } + + // remove the attr previously set, otherwise add the bsm and + // references as required + if (bsms.isEmpty()) { + cls.attributes.remove(Package.attrBootstrapMethodsEmpty.canonicalInstance()); + } else { + cpRefs.add(Package.getRefString("BootstrapMethods")); + Collections.sort(bsms); + cls.setBootstrapMethods(bsms); + } + + // construct a local constant pool + int numDoubles = 0; + for (Entry e : cpRefs) { + if (e.isDoubleWord()) numDoubles++; + } + Entry[] cpMap = new Entry[1+numDoubles+cpRefs.size()]; + int fillp = 1; + + // Add all ldc operands first. + if (ldcRefs != null) { + assert(cpRefs.containsAll(ldcRefs)); + for (Entry e : ldcRefs) { + cpMap[fillp++] = e; + } + assert(fillp == 1+ldcRefs.size()); + cpRefs.removeAll(ldcRefs); + ldcRefs = null; // done with it + } + + // Next add all the two-byte references. + Set<Entry> wideRefs = cpRefs; + cpRefs = null; // do not use! + int narrowLimit = fillp; + for (Entry e : wideRefs) { + cpMap[fillp++] = e; + } + assert(fillp == narrowLimit+wideRefs.size()); + Arrays.sort(cpMap, 1, narrowLimit, entryOutputOrder); + Arrays.sort(cpMap, narrowLimit, fillp, entryOutputOrder); + + if (verbose > 3) { + Utils.log.fine("CP of "+this+" {"); + for (int i = 0; i < fillp; i++) { + Entry e = cpMap[i]; + Utils.log.fine(" "+((e==null)?-1:getOutputIndex(e)) + +" : "+e); + } + Utils.log.fine("}"); + } + + // Now repack backwards, introducing null elements. + int revp = cpMap.length; + for (int i = fillp; --i >= 1; ) { + Entry e = cpMap[i]; + if (e.isDoubleWord()) + cpMap[--revp] = null; + cpMap[--revp] = e; + } + assert(revp == 1); // do not process the initial null + + return cpMap; + } + + void readMembers(Class[] classes) throws IOException { + // class_bands: + // ... + // *class_field_count :DELTA5 + // *class_method_count :DELTA5 + // + // *field_descr :DELTA5 (cp_Descr) + // field_attr_bands + // + // *method_descr :MDELTA5 (cp_Descr) + // method_attr_bands + // ... + assert(classes.length == numClasses); + class_field_count.expectLength(numClasses); + class_method_count.expectLength(numClasses); + class_field_count.readFrom(in); + class_method_count.readFrom(in); + + // Make a pre-pass over field and method counts to size the descrs: + int totalNF = class_field_count.getIntTotal(); + int totalNM = class_method_count.getIntTotal(); + field_descr.expectLength(totalNF); + method_descr.expectLength(totalNM); + if (verbose > 1) Utils.log.fine("expecting #fields="+totalNF+ + " and #methods="+totalNM+" in #classes="+numClasses); + + List<Class.Field> fields = new ArrayList<>(totalNF); + field_descr.readFrom(in); + for (int i = 0; i < classes.length; i++) { + Class c = classes[i]; + int nf = class_field_count.getInt(); + for (int j = 0; j < nf; j++) { + Class.Field f = c.new Field(NO_FLAGS_YET, (DescriptorEntry) + field_descr.getRef()); + fields.add(f); + } + } + class_field_count.doneDisbursing(); + field_descr.doneDisbursing(); + countAndReadAttrs(ATTR_CONTEXT_FIELD, fields); + fields = null; // release to GC + + List<Class.Method> methods = new ArrayList<>(totalNM); + method_descr.readFrom(in); + for (int i = 0; i < classes.length; i++) { + Class c = classes[i]; + int nm = class_method_count.getInt(); + for (int j = 0; j < nm; j++) { + Class.Method m = c.new Method(NO_FLAGS_YET, (DescriptorEntry) + method_descr.getRef()); + methods.add(m); + } + } + class_method_count.doneDisbursing(); + method_descr.doneDisbursing(); + countAndReadAttrs(ATTR_CONTEXT_METHOD, methods); + + // Up to this point, Code attributes look like empty attributes. + // Now we start to special-case them. The empty canonical Code + // attributes stay in the method attribute lists, however. + allCodes = buildCodeAttrs(methods); + } + + Code[] allCodes; + List<Code> codesWithFlags; + Map<Class, Set<Entry>> ldcRefMap = new HashMap<>(); + + Code[] buildCodeAttrs(List<Class.Method> methods) { + List<Code> codes = new ArrayList<>(methods.size()); + for (Class.Method m : methods) { + if (m.getAttribute(attrCodeEmpty) != null) { + m.code = new Code(m); + codes.add(m.code); + } + } + Code[] a = new Code[codes.size()]; + codes.toArray(a); + return a; + } + + void readCodeHeaders() throws IOException { + // code_bands: + // *code_headers :BYTE1 + // + // *code_max_stack :UNSIGNED5 + // *code_max_na_locals :UNSIGNED5 + // *code_handler_count :UNSIGNED5 + // ... + // code_attr_bands + boolean attrsOK = testBit(archiveOptions, AO_HAVE_ALL_CODE_FLAGS); + code_headers.expectLength(allCodes.length); + code_headers.readFrom(in); + List<Code> longCodes = new ArrayList<>(allCodes.length / 10); + for (int i = 0; i < allCodes.length; i++) { + Code c = allCodes[i]; + int sc = code_headers.getByte(); + assert(sc == (sc & 0xFF)); + if (verbose > 2) + Utils.log.fine("codeHeader "+c+" = "+sc); + if (sc == LONG_CODE_HEADER) { + // We will read ms/ml/nh/flags from bands shortly. + longCodes.add(c); + continue; + } + // Short code header is the usual case: + c.setMaxStack( shortCodeHeader_max_stack(sc) ); + c.setMaxNALocals( shortCodeHeader_max_na_locals(sc) ); + c.setHandlerCount( shortCodeHeader_handler_count(sc) ); + assert(shortCodeHeader(c) == sc); + } + code_headers.doneDisbursing(); + code_max_stack.expectLength(longCodes.size()); + code_max_na_locals.expectLength(longCodes.size()); + code_handler_count.expectLength(longCodes.size()); + + // Do the long headers now. + code_max_stack.readFrom(in); + code_max_na_locals.readFrom(in); + code_handler_count.readFrom(in); + for (Code c : longCodes) { + c.setMaxStack( code_max_stack.getInt() ); + c.setMaxNALocals( code_max_na_locals.getInt() ); + c.setHandlerCount( code_handler_count.getInt() ); + } + code_max_stack.doneDisbursing(); + code_max_na_locals.doneDisbursing(); + code_handler_count.doneDisbursing(); + + readCodeHandlers(); + + if (attrsOK) { + // Code attributes are common (debug info not stripped). + codesWithFlags = Arrays.asList(allCodes); + } else { + // Code attributes are very sparse (debug info is stripped). + codesWithFlags = longCodes; + } + countAttrs(ATTR_CONTEXT_CODE, codesWithFlags); + // do readAttrs later, after BCs are scanned + } + + void readCodeHandlers() throws IOException { + // code_bands: + // ... + // *code_handler_start_P :BCI5 + // *code_handler_end_PO :BRANCH5 + // *code_handler_catch_PO :BRANCH5 + // *code_handler_class_RCN :UNSIGNED5 (null or cp_Class) + // ... + int nh = 0; + for (int i = 0; i < allCodes.length; i++) { + Code c = allCodes[i]; + nh += c.getHandlerCount(); + } + + ValueBand[] code_handler_bands = { + code_handler_start_P, + code_handler_end_PO, + code_handler_catch_PO, + code_handler_class_RCN + }; + + for (int i = 0; i < code_handler_bands.length; i++) { + code_handler_bands[i].expectLength(nh); + code_handler_bands[i].readFrom(in); + } + + for (int i = 0; i < allCodes.length; i++) { + Code c = allCodes[i]; + for (int j = 0, jmax = c.getHandlerCount(); j < jmax; j++) { + c.handler_class[j] = code_handler_class_RCN.getRef(); + // For now, just record the raw BCI codes. + // We must wait until we have instruction boundaries. + c.handler_start[j] = code_handler_start_P.getInt(); + c.handler_end[j] = code_handler_end_PO.getInt(); + c.handler_catch[j] = code_handler_catch_PO.getInt(); + } + } + for (int i = 0; i < code_handler_bands.length; i++) { + code_handler_bands[i].doneDisbursing(); + } + } + + void fixupCodeHandlers() { + // Actually decode (renumber) the BCIs now. + for (int i = 0; i < allCodes.length; i++) { + Code c = allCodes[i]; + for (int j = 0, jmax = c.getHandlerCount(); j < jmax; j++) { + int sum = c.handler_start[j]; + c.handler_start[j] = c.decodeBCI(sum); + sum += c.handler_end[j]; + c.handler_end[j] = c.decodeBCI(sum); + sum += c.handler_catch[j]; + c.handler_catch[j] = c.decodeBCI(sum); + } + } + } + + // Generic routines for reading attributes of + // classes, fields, methods, and codes. + // The holders is a global list, already collected, + // of attribute "customers". + void countAndReadAttrs(int ctype, Collection<? extends Attribute.Holder> holders) + throws IOException { + // class_attr_bands: + // *class_flags :UNSIGNED5 + // *class_attr_count :UNSIGNED5 + // *class_attr_indexes :UNSIGNED5 + // *class_attr_calls :UNSIGNED5 + // *class_Signature_RS :UNSIGNED5 (cp_Signature) + // class_metadata_bands + // *class_SourceFile_RU :UNSIGNED5 (cp_Utf8) + // *class_EnclosingMethod_RM :UNSIGNED5 (cp_Method) + // ic_local_bands + // *class_ClassFile_version_minor_H :UNSIGNED5 + // *class_ClassFile_version_major_H :UNSIGNED5 + // class_type_metadata_bands + // + // field_attr_bands: + // *field_flags :UNSIGNED5 + // *field_attr_count :UNSIGNED5 + // *field_attr_indexes :UNSIGNED5 + // *field_attr_calls :UNSIGNED5 + // *field_Signature_RS :UNSIGNED5 (cp_Signature) + // field_metadata_bands + // *field_ConstantValue_KQ :UNSIGNED5 (cp_Int, etc.; see note) + // field_type_metadata_bands + // + // method_attr_bands: + // *method_flags :UNSIGNED5 + // *method_attr_count :UNSIGNED5 + // *method_attr_indexes :UNSIGNED5 + // *method_attr_calls :UNSIGNED5 + // *method_Signature_RS :UNSIGNED5 (cp_Signature) + // method_metadata_bands + // *method_Exceptions_N :UNSIGNED5 + // *method_Exceptions_RC :UNSIGNED5 (cp_Class) + // *method_MethodParameters_NB: BYTE1 + // *method_MethodParameters_RUN: UNSIGNED5 (cp_Utf8) + // *method_MethodParameters_FH: UNSIGNED5 (flag) + // method_type_metadata_bands + // + // code_attr_bands: + // *code_flags :UNSIGNED5 + // *code_attr_count :UNSIGNED5 + // *code_attr_indexes :UNSIGNED5 + // *code_attr_calls :UNSIGNED5 + // *code_LineNumberTable_N :UNSIGNED5 + // *code_LineNumberTable_bci_P :BCI5 + // *code_LineNumberTable_line :UNSIGNED5 + // *code_LocalVariableTable_N :UNSIGNED5 + // *code_LocalVariableTable_bci_P :BCI5 + // *code_LocalVariableTable_span_O :BRANCH5 + // *code_LocalVariableTable_name_RU :UNSIGNED5 (cp_Utf8) + // *code_LocalVariableTable_type_RS :UNSIGNED5 (cp_Signature) + // *code_LocalVariableTable_slot :UNSIGNED5 + // code_type_metadata_bands + + countAttrs(ctype, holders); + readAttrs(ctype, holders); + } + + // Read flags and count the attributes that are to be placed + // on the given holders. + void countAttrs(int ctype, Collection<? extends Attribute.Holder> holders) + throws IOException { + // Here, xxx stands for one of class, field, method, code. + MultiBand xxx_attr_bands = attrBands[ctype]; + long flagMask = attrFlagMask[ctype]; + if (verbose > 1) { + Utils.log.fine("scanning flags and attrs for "+ + Attribute.contextName(ctype)+"["+holders.size()+"]"); + } + + // Fetch the attribute layout definitions which govern the bands + // we are about to read. + List<Attribute.Layout> defList = attrDefs.get(ctype); + Attribute.Layout[] defs = new Attribute.Layout[defList.size()]; + defList.toArray(defs); + IntBand xxx_flags_hi = getAttrBand(xxx_attr_bands, AB_FLAGS_HI); + IntBand xxx_flags_lo = getAttrBand(xxx_attr_bands, AB_FLAGS_LO); + IntBand xxx_attr_count = getAttrBand(xxx_attr_bands, AB_ATTR_COUNT); + IntBand xxx_attr_indexes = getAttrBand(xxx_attr_bands, AB_ATTR_INDEXES); + IntBand xxx_attr_calls = getAttrBand(xxx_attr_bands, AB_ATTR_CALLS); + + // Count up the number of holders which have overflow attrs. + int overflowMask = attrOverflowMask[ctype]; + int overflowHolderCount = 0; + boolean haveLongFlags = haveFlagsHi(ctype); + xxx_flags_hi.expectLength(haveLongFlags? holders.size(): 0); + xxx_flags_hi.readFrom(in); + xxx_flags_lo.expectLength(holders.size()); + xxx_flags_lo.readFrom(in); + assert((flagMask & overflowMask) == overflowMask); + for (Attribute.Holder h : holders) { + int flags = xxx_flags_lo.getInt(); + h.flags = flags; + if ((flags & overflowMask) != 0) + overflowHolderCount += 1; + } + + // For each holder with overflow attrs, read a count. + xxx_attr_count.expectLength(overflowHolderCount); + xxx_attr_count.readFrom(in); + xxx_attr_indexes.expectLength(xxx_attr_count.getIntTotal()); + xxx_attr_indexes.readFrom(in); + + // Now it's time to check flag bits that indicate attributes. + // We accumulate (a) a list of attribute types for each holder + // (class/field/method/code), and also we accumulate (b) a total + // count for each attribute type. + int[] totalCounts = new int[defs.length]; + for (Attribute.Holder h : holders) { + assert(h.attributes == null); + // System.out.println("flags="+h.flags+" using fm="+flagMask); + long attrBits = ((h.flags & flagMask) << 32) >>> 32; + // Clean up the flags now. + h.flags -= (int)attrBits; // strip attr bits + assert(h.flags == (char)h.flags); // 16 bits only now + assert((ctype != ATTR_CONTEXT_CODE) || h.flags == 0); + if (haveLongFlags) + attrBits += (long)xxx_flags_hi.getInt() << 32; + if (attrBits == 0) continue; // no attrs on this guy + + int noa = 0; // number of overflow attrs + long overflowBit = (attrBits & overflowMask); + assert(overflowBit >= 0); + attrBits -= overflowBit; + if (overflowBit != 0) { + noa = xxx_attr_count.getInt(); + } + + int nfa = 0; // number of flag attrs + long bits = attrBits; + for (int ai = 0; bits != 0; ai++) { + if ((bits & (1L<<ai)) == 0) continue; + bits -= (1L<<ai); + nfa += 1; + } + List<Attribute> ha = new ArrayList<>(nfa + noa); + h.attributes = ha; + bits = attrBits; // iterate again + for (int ai = 0; bits != 0; ai++) { + if ((bits & (1L<<ai)) == 0) continue; + bits -= (1L<<ai); + totalCounts[ai] += 1; + // This definition index is live in this holder. + if (defs[ai] == null) badAttrIndex(ai, ctype); + Attribute canonical = defs[ai].canonicalInstance(); + ha.add(canonical); + nfa -= 1; + } + assert(nfa == 0); + for (; noa > 0; noa--) { + int ai = xxx_attr_indexes.getInt(); + totalCounts[ai] += 1; + // This definition index is live in this holder. + if (defs[ai] == null) badAttrIndex(ai, ctype); + Attribute canonical = defs[ai].canonicalInstance(); + ha.add(canonical); + } + } + + xxx_flags_hi.doneDisbursing(); + xxx_flags_lo.doneDisbursing(); + xxx_attr_count.doneDisbursing(); + xxx_attr_indexes.doneDisbursing(); + + // Now each holder has a list of canonical attribute instances. + // For layouts with no elements, we are done. However, for + // layouts with bands, we must replace each canonical (empty) + // instance with a value-bearing one, initialized from the + // appropriate bands. + + // Make a small pass to detect and read backward call counts. + int callCounts = 0; + for (boolean predef = true; ; predef = false) { + for (int ai = 0; ai < defs.length; ai++) { + Attribute.Layout def = defs[ai]; + if (def == null) continue; // unused index + if (predef != isPredefinedAttr(ctype, ai)) + continue; // wrong pass + int totalCount = totalCounts[ai]; + if (totalCount == 0) + continue; // irrelevant + Attribute.Layout.Element[] cbles = def.getCallables(); + for (int j = 0; j < cbles.length; j++) { + assert(cbles[j].kind == Attribute.EK_CBLE); + if (cbles[j].flagTest(Attribute.EF_BACK)) + callCounts += 1; + } + } + if (!predef) break; + } + xxx_attr_calls.expectLength(callCounts); + xxx_attr_calls.readFrom(in); + + // Finally, size all the attribute bands. + for (boolean predef = true; ; predef = false) { + for (int ai = 0; ai < defs.length; ai++) { + Attribute.Layout def = defs[ai]; + if (def == null) continue; // unused index + if (predef != isPredefinedAttr(ctype, ai)) + continue; // wrong pass + int totalCount = totalCounts[ai]; + Band[] ab = attrBandTable.get(def); + if (def == attrInnerClassesEmpty) { + // Special case. + // Size the bands as if using the following layout: + // [RCH TI[ (0)[] ()[RCNH RUNH] ]]. + class_InnerClasses_N.expectLength(totalCount); + class_InnerClasses_N.readFrom(in); + int tupleCount = class_InnerClasses_N.getIntTotal(); + class_InnerClasses_RC.expectLength(tupleCount); + class_InnerClasses_RC.readFrom(in); + class_InnerClasses_F.expectLength(tupleCount); + class_InnerClasses_F.readFrom(in); + // Drop remaining columns wherever flags are zero: + tupleCount -= class_InnerClasses_F.getIntCount(0); + class_InnerClasses_outer_RCN.expectLength(tupleCount); + class_InnerClasses_outer_RCN.readFrom(in); + class_InnerClasses_name_RUN.expectLength(tupleCount); + class_InnerClasses_name_RUN.readFrom(in); + } else if (!optDebugBands && totalCount == 0) { + // Expect no elements at all. Skip quickly. however if we + // are debugging bands, read all bands regardless + for (int j = 0; j < ab.length; j++) { + ab[j].doneWithUnusedBand(); + } + } else { + // Read these bands in sequence. + boolean hasCallables = def.hasCallables(); + if (!hasCallables) { + readAttrBands(def.elems, totalCount, new int[0], ab); + } else { + Attribute.Layout.Element[] cbles = def.getCallables(); + // At first, record initial calls. + // Later, forward calls may also accumulate here: + int[] forwardCounts = new int[cbles.length]; + forwardCounts[0] = totalCount; + for (int j = 0; j < cbles.length; j++) { + assert(cbles[j].kind == Attribute.EK_CBLE); + int entryCount = forwardCounts[j]; + forwardCounts[j] = -1; // No more, please! + if (totalCount > 0 && cbles[j].flagTest(Attribute.EF_BACK)) + entryCount += xxx_attr_calls.getInt(); + readAttrBands(cbles[j].body, entryCount, forwardCounts, ab); + } + } + // mark them read, to satisfy asserts + if (optDebugBands && totalCount == 0) { + for (int j = 0; j < ab.length; j++) { + ab[j].doneDisbursing(); + } + } + } + } + if (!predef) break; + } + xxx_attr_calls.doneDisbursing(); + } + + void badAttrIndex(int ai, int ctype) throws IOException { + throw new IOException("Unknown attribute index "+ai+" for "+ + ATTR_CONTEXT_NAME[ctype]+" attribute"); + } + + void readAttrs(int ctype, Collection<? extends Attribute.Holder> holders) + throws IOException { + // Decode band values into attributes. + Set<Attribute.Layout> sawDefs = new HashSet<>(); + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + for (final Attribute.Holder h : holders) { + if (h.attributes == null) continue; + for (ListIterator<Attribute> j = h.attributes.listIterator(); j.hasNext(); ) { + Attribute a = j.next(); + Attribute.Layout def = a.layout(); + if (def.bandCount == 0) { + if (def == attrInnerClassesEmpty) { + // Special logic to read this attr. + readLocalInnerClasses((Class) h); + continue; + } + // Canonical empty attr works fine (e.g., Synthetic). + continue; + } + sawDefs.add(def); + boolean isCV = (ctype == ATTR_CONTEXT_FIELD && def == attrConstantValue); + if (isCV) setConstantValueIndex((Class.Field)h); + if (verbose > 2) + Utils.log.fine("read "+a+" in "+h); + final Band[] ab = attrBandTable.get(def); + // Read one attribute of type def from ab into a byte array. + buf.reset(); + Object fixups = a.unparse(new Attribute.ValueStream() { + public int getInt(int bandIndex) { + return ((IntBand) ab[bandIndex]).getInt(); + } + public Entry getRef(int bandIndex) { + return ((CPRefBand) ab[bandIndex]).getRef(); + } + public int decodeBCI(int bciCode) { + Code code = (Code) h; + return code.decodeBCI(bciCode); + } + }, buf); + // Replace the canonical attr with the one just read. + j.set(a.addContent(buf.toByteArray(), fixups)); + if (isCV) setConstantValueIndex(null); // clean up + } + } + + // Mark the bands we just used as done disbursing. + for (Attribute.Layout def : sawDefs) { + if (def == null) continue; // unused index + Band[] ab = attrBandTable.get(def); + for (int j = 0; j < ab.length; j++) { + ab[j].doneDisbursing(); + } + } + + if (ctype == ATTR_CONTEXT_CLASS) { + class_InnerClasses_N.doneDisbursing(); + class_InnerClasses_RC.doneDisbursing(); + class_InnerClasses_F.doneDisbursing(); + class_InnerClasses_outer_RCN.doneDisbursing(); + class_InnerClasses_name_RUN.doneDisbursing(); + } + + MultiBand xxx_attr_bands = attrBands[ctype]; + for (int i = 0; i < xxx_attr_bands.size(); i++) { + Band b = xxx_attr_bands.get(i); + if (b instanceof MultiBand) + b.doneDisbursing(); + } + xxx_attr_bands.doneDisbursing(); + } + + private + void readAttrBands(Attribute.Layout.Element[] elems, + int count, int[] forwardCounts, + Band[] ab) + throws IOException { + for (int i = 0; i < elems.length; i++) { + Attribute.Layout.Element e = elems[i]; + Band eBand = null; + if (e.hasBand()) { + eBand = ab[e.bandIndex]; + eBand.expectLength(count); + eBand.readFrom(in); + } + switch (e.kind) { + case Attribute.EK_REPL: + // Recursive call. + int repCount = ((IntBand)eBand).getIntTotal(); + // Note: getIntTotal makes an extra pass over this band. + readAttrBands(e.body, repCount, forwardCounts, ab); + break; + case Attribute.EK_UN: + int remainingCount = count; + for (int j = 0; j < e.body.length; j++) { + int caseCount; + if (j == e.body.length-1) { + caseCount = remainingCount; + } else { + caseCount = 0; + for (int j0 = j; + (j == j0) + || (j < e.body.length + && e.body[j].flagTest(Attribute.EF_BACK)); + j++) { + caseCount += ((IntBand)eBand).getIntCount(e.body[j].value); + } + --j; // back up to last occurrence of this body + } + remainingCount -= caseCount; + readAttrBands(e.body[j].body, caseCount, forwardCounts, ab); + } + assert(remainingCount == 0); + break; + case Attribute.EK_CALL: + assert(e.body.length == 1); + assert(e.body[0].kind == Attribute.EK_CBLE); + if (!e.flagTest(Attribute.EF_BACK)) { + // Backward calls are pre-counted, but forwards are not. + // Push the present count forward. + assert(forwardCounts[e.value] >= 0); + forwardCounts[e.value] += count; + } + break; + case Attribute.EK_CBLE: + assert(false); + break; + } + } + } + + void readByteCodes() throws IOException { + // bc_bands: + // *bc_codes :BYTE1 + // *bc_case_count :UNSIGNED5 + // *bc_case_value :DELTA5 + // *bc_byte :BYTE1 + // *bc_short :DELTA5 + // *bc_local :UNSIGNED5 + // *bc_label :BRANCH5 + // *bc_intref :DELTA5 (cp_Int) + // *bc_floatref :DELTA5 (cp_Float) + // *bc_longref :DELTA5 (cp_Long) + // *bc_doubleref :DELTA5 (cp_Double) + // *bc_stringref :DELTA5 (cp_String) + // *bc_classref :UNSIGNED5 (current class or cp_Class) + // *bc_fieldref :DELTA5 (cp_Field) + // *bc_methodref :UNSIGNED5 (cp_Method) + // *bc_imethodref :DELTA5 (cp_Imethod) + // *bc_thisfield :UNSIGNED5 (cp_Field, only for current class) + // *bc_superfield :UNSIGNED5 (cp_Field, only for current super) + // *bc_thismethod :UNSIGNED5 (cp_Method, only for current class) + // *bc_supermethod :UNSIGNED5 (cp_Method, only for current super) + // *bc_initref :UNSIGNED5 (cp_Field, only for most recent new) + // *bc_escref :UNSIGNED5 (cp_All) + // *bc_escrefsize :UNSIGNED5 + // *bc_escsize :UNSIGNED5 + // *bc_escbyte :BYTE1 + bc_codes.elementCountForDebug = allCodes.length; + bc_codes.setInputStreamFrom(in); + readByteCodeOps(); // reads from bc_codes and bc_case_count + bc_codes.doneDisbursing(); + + // All the operand bands have now been sized. Read them all in turn. + Band[] operand_bands = { + bc_case_value, + bc_byte, bc_short, + bc_local, bc_label, + bc_intref, bc_floatref, + bc_longref, bc_doubleref, bc_stringref, + bc_loadablevalueref, + bc_classref, bc_fieldref, + bc_methodref, bc_imethodref, + bc_indyref, + bc_thisfield, bc_superfield, + bc_thismethod, bc_supermethod, + bc_initref, + bc_escref, bc_escrefsize, bc_escsize + }; + for (int i = 0; i < operand_bands.length; i++) { + operand_bands[i].readFrom(in); + } + bc_escbyte.expectLength(bc_escsize.getIntTotal()); + bc_escbyte.readFrom(in); + + expandByteCodeOps(); + + // Done fetching values from operand bands: + bc_case_count.doneDisbursing(); + for (int i = 0; i < operand_bands.length; i++) { + operand_bands[i].doneDisbursing(); + } + bc_escbyte.doneDisbursing(); + bc_bands.doneDisbursing(); + + // We must delay the parsing of Code attributes until we + // have a complete model of bytecodes, for BCI encodings. + readAttrs(ATTR_CONTEXT_CODE, codesWithFlags); + // Ditto for exception handlers in codes. + fixupCodeHandlers(); + // Now we can finish with class_bands; cf. readClasses(). + code_bands.doneDisbursing(); + class_bands.doneDisbursing(); + } + + private void readByteCodeOps() throws IOException { + // scratch buffer for collecting code:: + byte[] buf = new byte[1<<12]; + // record of all switch opcodes (these are variable-length) + List<Integer> allSwitchOps = new ArrayList<>(); + for (int k = 0; k < allCodes.length; k++) { + Code c = allCodes[k]; + scanOneMethod: + for (int i = 0; ; i++) { + int bc = bc_codes.getByte(); + if (i + 10 > buf.length) buf = realloc(buf); + buf[i] = (byte)bc; + boolean isWide = false; + if (bc == _wide) { + bc = bc_codes.getByte(); + buf[++i] = (byte)bc; + isWide = true; + } + assert(bc == (0xFF & bc)); + // Adjust expectations of various band sizes. + switch (bc) { + case _tableswitch: + case _lookupswitch: + bc_case_count.expectMoreLength(1); + allSwitchOps.add(bc); + break; + case _iinc: + bc_local.expectMoreLength(1); + if (isWide) + bc_short.expectMoreLength(1); + else + bc_byte.expectMoreLength(1); + break; + case _sipush: + bc_short.expectMoreLength(1); + break; + case _bipush: + bc_byte.expectMoreLength(1); + break; + case _newarray: + bc_byte.expectMoreLength(1); + break; + case _multianewarray: + assert(getCPRefOpBand(bc) == bc_classref); + bc_classref.expectMoreLength(1); + bc_byte.expectMoreLength(1); + break; + case _ref_escape: + bc_escrefsize.expectMoreLength(1); + bc_escref.expectMoreLength(1); + break; + case _byte_escape: + bc_escsize.expectMoreLength(1); + // bc_escbyte will have to be counted too + break; + default: + if (Instruction.isInvokeInitOp(bc)) { + bc_initref.expectMoreLength(1); + break; + } + if (Instruction.isSelfLinkerOp(bc)) { + CPRefBand bc_which = selfOpRefBand(bc); + bc_which.expectMoreLength(1); + break; + } + if (Instruction.isBranchOp(bc)) { + bc_label.expectMoreLength(1); + break; + } + if (Instruction.isCPRefOp(bc)) { + CPRefBand bc_which = getCPRefOpBand(bc); + bc_which.expectMoreLength(1); + assert(bc != _multianewarray); // handled elsewhere + break; + } + if (Instruction.isLocalSlotOp(bc)) { + bc_local.expectMoreLength(1); + break; + } + break; + case _end_marker: + { + // Transfer from buf to a more permanent place: + c.bytes = realloc(buf, i); + break scanOneMethod; + } + } + } + } + + // To size instruction bands correctly, we need info on switches: + bc_case_count.readFrom(in); + for (Integer i : allSwitchOps) { + int bc = i.intValue(); + int caseCount = bc_case_count.getInt(); + bc_label.expectMoreLength(1+caseCount); // default label + cases + bc_case_value.expectMoreLength(bc == _tableswitch ? 1 : caseCount); + } + bc_case_count.resetForSecondPass(); + } + + private void expandByteCodeOps() throws IOException { + // scratch buffer for collecting code: + byte[] buf = new byte[1<<12]; + // scratch buffer for collecting instruction boundaries: + int[] insnMap = new int[1<<12]; + // list of label carriers, for label decoding post-pass: + int[] labels = new int[1<<10]; + // scratch buffer for registering CP refs: + Fixups fixupBuf = new Fixups(); + + for (int k = 0; k < allCodes.length; k++) { + Code code = allCodes[k]; + byte[] codeOps = code.bytes; + code.bytes = null; // just for now, while we accumulate bits + + Class curClass = code.thisClass(); + + Set<Entry> ldcRefSet = ldcRefMap.get(curClass); + if (ldcRefSet == null) + ldcRefMap.put(curClass, ldcRefSet = new HashSet<>()); + + ClassEntry thisClass = curClass.thisClass; + ClassEntry superClass = curClass.superClass; + ClassEntry newClass = null; // class of last _new opcode + + int pc = 0; // fill pointer in buf; actual bytecode PC + int numInsns = 0; + int numLabels = 0; + boolean hasEscs = false; + fixupBuf.clear(); + for (int i = 0; i < codeOps.length; i++) { + int bc = Instruction.getByte(codeOps, i); + int curPC = pc; + insnMap[numInsns++] = curPC; + if (pc + 10 > buf.length) buf = realloc(buf); + if (numInsns+10 > insnMap.length) insnMap = realloc(insnMap); + if (numLabels+10 > labels.length) labels = realloc(labels); + boolean isWide = false; + if (bc == _wide) { + buf[pc++] = (byte) bc; + bc = Instruction.getByte(codeOps, ++i); + isWide = true; + } + switch (bc) { + case _tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label)) + case _lookupswitch: // apc: (df, nc, nc*(case, label)) + { + int caseCount = bc_case_count.getInt(); + while ((pc + 30 + caseCount*8) > buf.length) + buf = realloc(buf); + buf[pc++] = (byte) bc; + //initialize apc, df, lo, hi bytes to reasonable bits: + Arrays.fill(buf, pc, pc+30, (byte)0); + Instruction.Switch isw = (Instruction.Switch) + Instruction.at(buf, curPC); + //isw.setDefaultLabel(getLabel(bc_label, code, curPC)); + isw.setCaseCount(caseCount); + if (bc == _tableswitch) { + isw.setCaseValue(0, bc_case_value.getInt()); + } else { + for (int j = 0; j < caseCount; j++) { + isw.setCaseValue(j, bc_case_value.getInt()); + } + } + // Make our getLabel calls later. + labels[numLabels++] = curPC; + pc = isw.getNextPC(); + continue; + } + case _iinc: + { + buf[pc++] = (byte) bc; + int local = bc_local.getInt(); + int delta; + if (isWide) { + delta = bc_short.getInt(); + Instruction.setShort(buf, pc, local); pc += 2; + Instruction.setShort(buf, pc, delta); pc += 2; + } else { + delta = (byte) bc_byte.getByte(); + buf[pc++] = (byte)local; + buf[pc++] = (byte)delta; + } + continue; + } + case _sipush: + { + int val = bc_short.getInt(); + buf[pc++] = (byte) bc; + Instruction.setShort(buf, pc, val); pc += 2; + continue; + } + case _bipush: + case _newarray: + { + int val = bc_byte.getByte(); + buf[pc++] = (byte) bc; + buf[pc++] = (byte) val; + continue; + } + case _ref_escape: + { + // Note that insnMap has one entry for this. + hasEscs = true; + int size = bc_escrefsize.getInt(); + Entry ref = bc_escref.getRef(); + if (size == 1) ldcRefSet.add(ref); + int fmt; + switch (size) { + case 1: fixupBuf.addU1(pc, ref); break; + case 2: fixupBuf.addU2(pc, ref); break; + default: assert(false); fmt = 0; + } + buf[pc+0] = buf[pc+1] = 0; + pc += size; + } + continue; + case _byte_escape: + { + // Note that insnMap has one entry for all these bytes. + hasEscs = true; + int size = bc_escsize.getInt(); + while ((pc + size) > buf.length) + buf = realloc(buf); + while (size-- > 0) { + buf[pc++] = (byte) bc_escbyte.getByte(); + } + } + continue; + default: + if (Instruction.isInvokeInitOp(bc)) { + int idx = (bc - _invokeinit_op); + int origBC = _invokespecial; + ClassEntry classRef; + switch (idx) { + case _invokeinit_self_option: + classRef = thisClass; break; + case _invokeinit_super_option: + classRef = superClass; break; + default: + assert(idx == _invokeinit_new_option); + classRef = newClass; break; + } + buf[pc++] = (byte) origBC; + int coding = bc_initref.getInt(); + // Find the nth overloading of <init> in classRef. + MemberEntry ref = pkg.cp.getOverloadingForIndex(CONSTANT_Methodref, classRef, "<init>", coding); + fixupBuf.addU2(pc, ref); + buf[pc+0] = buf[pc+1] = 0; + pc += 2; + assert(Instruction.opLength(origBC) == (pc - curPC)); + continue; + } + if (Instruction.isSelfLinkerOp(bc)) { + int idx = (bc - _self_linker_op); + boolean isSuper = (idx >= _self_linker_super_flag); + if (isSuper) idx -= _self_linker_super_flag; + boolean isAload = (idx >= _self_linker_aload_flag); + if (isAload) idx -= _self_linker_aload_flag; + int origBC = _first_linker_op + idx; + boolean isField = Instruction.isFieldOp(origBC); + CPRefBand bc_which; + ClassEntry which_cls = isSuper ? superClass : thisClass; + Index which_ix; + if (isField) { + bc_which = isSuper ? bc_superfield : bc_thisfield; + which_ix = pkg.cp.getMemberIndex(CONSTANT_Fieldref, which_cls); + } else { + bc_which = isSuper ? bc_supermethod : bc_thismethod; + which_ix = pkg.cp.getMemberIndex(CONSTANT_Methodref, which_cls); + } + assert(bc_which == selfOpRefBand(bc)); + MemberEntry ref = (MemberEntry) bc_which.getRef(which_ix); + if (isAload) { + buf[pc++] = (byte) _aload_0; + curPC = pc; + // Note: insnMap keeps the _aload_0 separate. + insnMap[numInsns++] = curPC; + } + buf[pc++] = (byte) origBC; + fixupBuf.addU2(pc, ref); + buf[pc+0] = buf[pc+1] = 0; + pc += 2; + assert(Instruction.opLength(origBC) == (pc - curPC)); + continue; + } + if (Instruction.isBranchOp(bc)) { + buf[pc++] = (byte) bc; + assert(!isWide); // no wide prefix for branches + int nextPC = curPC + Instruction.opLength(bc); + // Make our getLabel calls later. + labels[numLabels++] = curPC; + //Instruction.at(buf, curPC).setBranchLabel(getLabel(bc_label, code, curPC)); + while (pc < nextPC) buf[pc++] = 0; + continue; + } + if (Instruction.isCPRefOp(bc)) { + CPRefBand bc_which = getCPRefOpBand(bc); + Entry ref = bc_which.getRef(); + if (ref == null) { + if (bc_which == bc_classref) { + // Shorthand for class self-references. + ref = thisClass; + } else { + assert(false); + } + } + int origBC = bc; + int size = 2; + switch (bc) { + case _invokestatic_int: + origBC = _invokestatic; + break; + case _invokespecial_int: + origBC = _invokespecial; + break; + case _ildc: + case _cldc: + case _fldc: + case _sldc: + case _qldc: + origBC = _ldc; + size = 1; + ldcRefSet.add(ref); + break; + case _ildc_w: + case _cldc_w: + case _fldc_w: + case _sldc_w: + case _qldc_w: + origBC = _ldc_w; + break; + case _lldc2_w: + case _dldc2_w: + origBC = _ldc2_w; + break; + case _new: + newClass = (ClassEntry) ref; + break; + } + buf[pc++] = (byte) origBC; + int fmt; + switch (size) { + case 1: fixupBuf.addU1(pc, ref); break; + case 2: fixupBuf.addU2(pc, ref); break; + default: assert(false); fmt = 0; + } + buf[pc+0] = buf[pc+1] = 0; + pc += size; + if (origBC == _multianewarray) { + // Copy the trailing byte also. + int val = bc_byte.getByte(); + buf[pc++] = (byte) val; + } else if (origBC == _invokeinterface) { + int argSize = ((MemberEntry)ref).descRef.typeRef.computeSize(true); + buf[pc++] = (byte)( 1 + argSize ); + buf[pc++] = 0; + } else if (origBC == _invokedynamic) { + buf[pc++] = 0; + buf[pc++] = 0; + } + assert(Instruction.opLength(origBC) == (pc - curPC)); + continue; + } + if (Instruction.isLocalSlotOp(bc)) { + buf[pc++] = (byte) bc; + int local = bc_local.getInt(); + if (isWide) { + Instruction.setShort(buf, pc, local); + pc += 2; + if (bc == _iinc) { + int iVal = bc_short.getInt(); + Instruction.setShort(buf, pc, iVal); + pc += 2; + } + } else { + Instruction.setByte(buf, pc, local); + pc += 1; + if (bc == _iinc) { + int iVal = bc_byte.getByte(); + Instruction.setByte(buf, pc, iVal); + pc += 1; + } + } + assert(Instruction.opLength(bc) == (pc - curPC)); + continue; + } + // Random bytecode. Just copy it. + if (bc >= _bytecode_limit) + Utils.log.warning("unrecognized bytescode "+bc + +" "+Instruction.byteName(bc)); + assert(bc < _bytecode_limit); + buf[pc++] = (byte) bc; + assert(Instruction.opLength(bc) == (pc - curPC)); + continue; + } + } + // now make a permanent copy of the bytecodes + code.setBytes(realloc(buf, pc)); + code.setInstructionMap(insnMap, numInsns); + // fix up labels, now that code has its insnMap + Instruction ibr = null; // temporary branch instruction + for (int i = 0; i < numLabels; i++) { + int curPC = labels[i]; + // (Note: Passing ibr in allows reuse, a speed hack.) + ibr = Instruction.at(code.bytes, curPC, ibr); + if (ibr instanceof Instruction.Switch) { + Instruction.Switch isw = (Instruction.Switch) ibr; + isw.setDefaultLabel(getLabel(bc_label, code, curPC)); + int caseCount = isw.getCaseCount(); + for (int j = 0; j < caseCount; j++) { + isw.setCaseLabel(j, getLabel(bc_label, code, curPC)); + } + } else { + ibr.setBranchLabel(getLabel(bc_label, code, curPC)); + } + } + if (fixupBuf.size() > 0) { + if (verbose > 2) + Utils.log.fine("Fixups in code: "+fixupBuf); + code.addFixups(fixupBuf); + } + } + } +} |