--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/com/sun/java/util/jar/pack/PackageReader.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,2376 @@
+/*
+ * Copyright (c) 2001, 2016, 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;
+ }
+
+ static final 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<>() {
+ 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<>();
+ // flesh out the local constant pool
+ ConstantPool.completeReferencesIn(cpRefs, true, bsms);
+
+ // add the bsm and references as required
+ if (!bsms.isEmpty()) {
+ cls.addAttribute(Package.attrBootstrapMethodsEmpty.canonicalInstance());
+ cpRefs.add(Package.getRefString("BootstrapMethods"));
+ Collections.sort(bsms);
+ cls.setBootstrapMethods(bsms);
+ }
+
+ // Now that we know all our local class references,
+ // compute the InnerClasses attribute.
+ // An InnerClasses attribute usually gets added here,
+ // although it might already have been present.
+ 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);
+ }
+
+ // 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);
+ }
+ }
+ }
+}