blob: 000a643f873351d6e3756136d47973b925368356 [file] [log] [blame]
/*
* 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);
}
}
}
}