/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* Writer for a package file.
* @author John Rose
*/
// Caller has specified archive version in the package:
}
boolean ok = false;
try {
if (verbose > 0) {
}
setup();
if (verbose > 0) {
}
// writeFileHeader() is done last, since it has ultimate counts
// writeBandHeaders() is called after all other bands are done
writeFiles();
// choose codings (fill band_headers if needed)
if (verbose > 0) {
}
// now we can write the headers:
ok = true;
//if (verbose > 0) ee.printStackTrace();
// Write partial output only if we are verbose.
}
}
void setup() {
requiredEntries = new HashSet<>();
}
void setArchiveOptions() {
// Decide on some archive options early.
// Does not decide on: AO_HAVE_SPECIAL_FORMATS,
// AO_HAVE_CP_NUMBERS, AO_HAVE_FILE_HEADERS.
// Also, AO_HAVE_FILE_OPTIONS may be forced on later.
int minOptions = -1;
int maxOptions = 0;
// Import defaults from package (deflate hint, etc.).
if (minModtime == NO_MODTIME) {
} else {
}
minOptions &= options;
maxOptions |= options;
}
// Make everything else be a positive offset from here.
}
// Put them into a band.
}
// If the archive deflation is set do not bother with each file.
// Every file has the deflate_hint set.
// Set it for the whole archive, and omit options.
}
if (minOptions != maxOptions
}
}
// Decide on default version number (majority rule).
int bestCount = 0;
int bestVersion = -1;
var = new int[1];
}
//System.out.println("version="+version+" count="+count);
}
}
int bestMinver = (char)(bestVersion);
if (verbose > 0)
if (verbose > 0)
// Now add explicit pseudo-attrs. to classes with odd versions.
if (verbose > 1) {
+" doesn't match package version "
+pkgVer);
}
// Note: Does not add in "natural" order. (Who cares?)
cls.addAttribute(a);
}
}
// Decide if we are transmitting a huge resource file:
if (verbose > 0)
break;
}
}
// Decide if code attributes typically have sub-attributes.
// In that case, to preserve compact 1-byte code headers,
// we must declare unconditional presence of code flags.
int cost0 = 0;
int cost1 = 0;
// cost of a useless unconditional flags byte
cost1 += 1;
// cost of inflating a short header
cost0 += 3;
}
}
}
}
}
if (verbose > 0)
}
pkg.checkVersion();
}
// Local routine used to format fixed-format scalars
// in the file_header:
for (int i = 0; i < 4; i++) {
res <<= 8;
}
}
}
// for debug only: number of words optimized away
int headerDiscountForDebug = 0;
// AO_HAVE_SPECIAL_FORMATS is set if non-default
// coding techniques are used, or if there are
// compressor-defined attributes transmitted.
if (!haveSpecial) {
if (haveSpecial)
}
if (!haveSpecial)
// AO_HAVE_FILE_HEADERS is set if there is any
// file or segment envelope information present.
if (!haveFiles) {
if (haveFiles)
}
if (!haveFiles)
// AO_HAVE_CP_NUMBERS is set if there are any numbers
// in the global constant pool. (Numbers are in 15% of classes.)
if (!haveNumbers) {
if (haveNumbers)
}
if (!haveNumbers)
if (verbose > 0)
final int DUMMY = 0;
if (haveFiles) {
}
// Done with unsized part of header....
if (haveFiles) {
} else {
}
if (haveSpecial) {
} else {
}
// Sanity: Make sure we came out to 26 (less optional fields):
assert(archive_header_0.length() +
== AH_LENGTH - headerDiscountForDebug);
// Figure out all the sizes now, first cut:
archiveSize0 = 0;
// Second cut:
// Make the adjustments:
// Patch the header:
if (haveFiles) {
}
if (verbose > 1)
}
switch (tag) {
case CONSTANT_Utf8:
// The null string is always first.
if (count > 0)
break;
case CONSTANT_Integer:
case CONSTANT_Float:
case CONSTANT_Long:
case CONSTANT_Double:
// Omit counts for numbers if possible.
if (!haveNumbers) {
assert(count == 0);
continue;
}
break;
}
}
}
}
// (The following observations are out of date; they apply only to
// "banding" the constant pool itself. Later revisions of this algorithm
// applied the banding technique to every part of the package file,
// applying the benefits more broadly.)
// Note: Keeping the data separate in passes (or "bands") allows the
// compressor to issue significantly shorter indexes for repeated data.
// The difference in zipped size is 4%, which is remarkable since the
// unzipped sizes are the same (only the byte order differs).
// After moving similar data into bands, it becomes natural to delta-encode
// each band. (This is especially useful if we sort the constant pool first.)
// Delta encoding saves an extra 5% in the output size (13% of the CP itself).
// Because a typical delta usees much less data than a byte, the savings after
// zipping is even better: A zipped delta-encoded package is 8% smaller than
// a zipped non-delta-encoded package. Thus, in the zipped file, a banded,
// delta-encoded constant pool saves over 11% (of the total file size) compared
// with a zipped unbanded file.
if (verbose > 0)
if (optDumpBands) {
}
}
switch (tag) {
case CONSTANT_Utf8:
break;
case CONSTANT_Integer:
}
break;
case CONSTANT_Float:
}
break;
case CONSTANT_Long:
}
break;
case CONSTANT_Double:
}
break;
case CONSTANT_String:
}
break;
case CONSTANT_Class:
}
break;
case CONSTANT_Signature:
break;
case CONSTANT_NameandType:
}
break;
case CONSTANT_Fieldref:
break;
case CONSTANT_Methodref:
break;
break;
default:
assert(false);
}
}
}
return; // nothing to write
// The first element must always be the empty string.
final int SUFFIX_SKIP_1 = 1;
final int PREFIX_SKIP_2 = 2;
// Fetch the char arrays, first of all.
}
// First band: Write lengths of shared prefixes.
char[] prevChars = {};
int prefix = 0;
prefix++;
if (i >= PREFIX_SKIP_2)
else
assert(prefix == 0);
}
// Second band: Write lengths of unshared suffixes.
// Third band: Write the char values in the unshared suffixes.
boolean isPacked = false;
if (suffix == 0) {
// Zero suffix length is special flag to indicate
// separate treatment in cp_Utf8_big bands.
// This suffix length never occurs naturally,
// except in the one case of a zero-length string.
// (If it occurs, it is the first, due to sorting.)
// The zero length string must, paradoxically, be
// encoded as a zero-length cp_Utf8_big band.
// This wastes exactly (& tolerably) one null byte.
isPacked = (i >= SUFFIX_SKIP_1);
// Do not bother to add an empty "(Utf8_big_0)" band.
// Also, the initial empty string does not require a band.
int numWide = 0;
for (int n = 0; n < suffix; n++) {
numWide++;
}
}
if (numWide > 100) {
// Try packing the chars with an alternate encoding.
}
}
if (i < SUFFIX_SKIP_1) {
// No output.
assert(!isPacked);
assert(suffix == 0);
} else if (isPacked) {
// Mark packed string with zero-length suffix count.
// This tells the unpacker to go elsewhere for the suffix bits.
// Fourth band: Write unshared suffix with alternate coding.
} else {
// Normal string. Save suffix in third and fourth bands.
for (int n = 0; n < suffix; n++) {
}
}
}
if (verbose > 0) {
}
}
for (int n = 0; n < suffix; n++) {
}
}
if (verbose > 1)
Utils.log.fine("big string["+i+"] len="+suffix+" #wide="+numWide+" size="+sizes[BYTE_SIZE]+"/z="+sizes[ZIP_SIZE]+" coding "+special);
if (special != charRegular) {
if (verbose > 1)
Utils.log.fine("big string["+i+"] normalSize="+normalSizes[BYTE_SIZE]+"/z="+normalSizes[ZIP_SIZE]+" win="+(specialZipSize<normalZipSize-minWin));
return true;
}
}
return false;
}
}
}
}
void writeMemberRefs(byte tag, Entry[] cpMap, CPRefBand cp_class, CPRefBand cp_desc) throws IOException {
}
}
if (numFiles == 0) return;
int options = archiveOptions;
if (!haveOptions) {
if (file.isClassStub()) {
haveOptions = true;
break;
}
}
}
}
if (haveSizeHi)
if (haveModtime)
if (haveOptions)
if (verbose > 1)
}
if (verbose > 0)
}
@SuppressWarnings("unchecked")
void collectAttributeLayouts() {
maxFlags = new int[ATTR_CONTEXT_LIMIT];
for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
}
// Collect maxFlags and allLayouts.
}
}
}
}
// If there are many species of attributes, use 63-bit flags.
for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
boolean haveLongFlags = haveFlagsHi(i);
- 12 /*typical flag bits in use*/
+ 4 /*typical number of OK overflows*/;
archiveOptions |= mask;
haveLongFlags = true;
if (verbose > 0)
}
if (verbose > 1) {
}
assert(haveFlagsHi(i) == haveLongFlags);
}
// Standard indexes can never conflict with flag bits. Assert it.
for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
}
// Collect counts for both predefs. and custom defs.
// Decide on custom, local attribute definitions.
backCountTable = new HashMap<>();
attrCounts = new int[ATTR_CONTEXT_LIMIT][];
for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
// Now the remaining defs in allLayouts[i] need attr. indexes.
// Fill up unused flag bits with new defs.
// Unused bits are those which are not used by predefined attrs,
// and which are always clear in the classfiles.
assert(attrIndexLimit[i] > 0);
int nextLoBit = 0;
// Sort by count, most frequent first.
// Predefs. participate in this sort, though it does not matter.
// Primary sort key is count, reversed.
if (r != 0) return r;
}
});
int index;
if (predefIndex != null) {
// The index is already set.
} else if (avHiBits != 0) {
avHiBits >>>= 1;
nextLoBit += 1;
}
// Update attrIndexTable:
} else {
// Update attrIndexTable:
}
// Now that we know the index, record the count of this def.
// For all callables in the def, keep a tally of back-calls.
}
}
if (predefIndex == null) {
// Make sure the package CP can name the local attribute.
if (verbose > 0) {
if (index < attrIndexLimit[i])
else
}
}
}
}
// Later, when emitting attr_definition_bands, we will look at
// attrDefSeen and attrDefs at position 32/63 and beyond.
// The attrIndexTable will provide elements of xxx_attr_indexes bands.
// Done with scratch variables:
allLayouts = null;
}
// Scratch variables for processing attributes and flags.
int[] maxFlags;
// Make note of which flags appear in the class file.
// Set them in maxFlags.
for (Attribute a : h.getAttributes()) {
}
}
}
}
@SuppressWarnings("unchecked")
for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) {
for (int j = 0; j < limit; j++) {
int header = i; // ctype
if (j < attrIndexLimit[i]) {
// (...else header is simply ctype, with zero high bits.)
// either undefined or predefined; nothing to write
continue;
}
}
}
}
// Sort the new attr defs into some "natural" order.
// Primary sort key is attr def header.
if (r != 0) return r;
// Secondary sort key is attribute index.
// (This must be so, in order to keep overflow attr order.)
}
});
{
attrDefsWritten[i] = def;
// Check that we are transmitting that correct attribute index:
boolean debug = false;
assert(debug = true);
if (debug) {
}
}
}
}
}
// Write the four xxx_attr_calls bands.
continue; // wrong pass
if (totalCount == 0)
continue; // irrelevant
if (bc[j] >= 0) {
} else {
}
}
}
if (!predef) break;
}
}
}
void trimClassAttributes() {
// Replace "obvious" SourceFile attrs by null.
}
}
void collectInnerClasses() {
// Capture inner classes, removing them from individual classes.
// Irregular inner classes must stay local, though.
// First, collect a consistent global set.
if (!cls.hasInnerClasses()) continue;
// Different ICs. Choose the better to make global.
}
}
}
// Note: The InnerClasses attribute must be in a valid order,
// so that A$B always occurs earlier than A$B$C. This is an
// important side-effect of sorting lexically by class name.
// Next, empty out of every local set the consistent entries.
// Calculate whether there is any remaining need to have a local
// set, and whether it needs to be locked.
}
}
if (!ic.predictable) {
}
if (!ic.predictable) {
}
}
}
/** If there are any extra InnerClasses entries to write which are
* not already implied by the global table, put them into a
* local attribute. This is expected to be rare.
*/
// Is it redundant with the global version?
// A zero flag means copy a global IC here.
} else {
if (flags == 0)
}
}
}
// Note: This code respects the order in which caller put classes.
if (verbose > 0)
int nwritten = 0;
// Collect the class body, sans bytecodes.
if (verbose > 1)
// Encode rare case of null superClass as thisClass:
}
nwritten++;
}
}
}
writeCodeHeader(m.code);
writeByteCodes(m.code);
}
}
}
int na = c.attributeSize();
int sc = shortCodeHeader(c);
// We must write flags, and can only do so for long headers.
if (verbose > 2) {
Utils.log.fine("Code sizes info "+c.max_stack+" "+c.max_locals+" "+c.getHandlerCount()+" "+siglen+" "+na+(sc > 0 ? " SHORT="+sc : ""));
}
if (sc == LONG_CODE_HEADER) {
} else {
assert(c.getHandlerCount() < shortCodeHeader_h_limit);
}
}
// Encode end as offset from start, and catch as offset from end,
// because they are strongly correlated.
}
}
// Generic routines for writing attributes and flags of
// classes, fields, methods, and codes.
if (h.attributes == null) {
if (haveLongFlags)
return;
}
if (verbose > 3)
long flagsToAdd = 0;
int overflowCount = 0;
for (Attribute a : h.attributes) {
if (verbose > 3)
if (verbose > 3)
} else {
// an overflow attr.
overflowCount += 1;
if (verbose > 3)
// System.out.println("overflow @"+index);
}
if (def == attrInnerClassesEmpty) {
// Special logic to write this attr.
writeLocalInnerClasses((Class) h);
continue;
}
// Empty attr; nothing more to write here.
continue;
}
// Write one attribute of type def into ab.
new Attribute.ValueStream() {
}
}
}
public void noteBackCall(int whichCallable) {
}
});
}
if (overflowCount > 0) {
}
if (haveLongFlags)
else
: (h+".flags="
}
// temporary scratch variables for processing code blocks
curCode = c;
}
private void endCode() {
}
// Return an _invokeinit_op variant, if the instruction matches one,
// else -1.
return -1;
return _invokeinit_op+_invokeinit_new_option;
return -1;
}
// Return a _self_linker_op variant, if the instruction matches one,
// else -1.
return self_bc;
return self_bc + _self_linker_super_flag;
return -1;
}
// true if the previous instruction is an aload to absorb
boolean prevAload = false;
// class of most recent new; helps compress <init> calls
// %%% Add a stress mode which issues _ref/_byte_escape.
if (i.isNonstandard()
|| i.getBC() != _xxxunusedxxx)) {
// Crash and burn with a complaint if there are funny
// bytecodes in this class file.
+" contains an unrecognized bytecode "+i
+"; please use the pass-file option on this class.";
throw new IOException(complaint);
}
if (i.isWide()) {
if (verbose > 1) {
}
}
// Begin "bc_linker" compression.
// Try to group aload_0 with a following operation.
prevAload = true;
continue;
}
}
// Test for <init> invocations:
if (init_bc >= 0) {
if (prevAload) {
// get rid of it
prevAload = false; //used up
}
// Write special bytecode.
// Write operand to a separate band.
continue;
}
int self_bc = selfOpVariant(i);
if (self_bc >= 0) {
prevAload = false; //used up
if (isAload)
// Write special bytecode.
// Write field or method ref to a separate band.
continue;
}
assert(!prevAload);
// End "bc_linker" compression.
// Normal bytecode.
switch (bc) {
case _tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label))
case _lookupswitch: // apc: (df, nc, nc*(case, label))
// Note that we do not write the alignment bytes.
// write a length specification into the bytecode stream
for (int j = 0; j < caseCount; j++) {
}
// Transmit case values in their own band.
if (bc == _tableswitch) {
} else {
for (int j = 0; j < caseCount; j++) {
}
}
// Done with the switch.
continue;
}
switch (bc) {
case _xxxunusedxxx: // %%% pretend this is invokedynamic
{
i.setNonstandardLength(3);
// transmit the opcode, carefully:
// transmit the CP reference, carefully:
continue;
}
}
int branch = i.getBranchLabel();
if (branch >= 0) {
continue;
}
switch (i.getCPTag()) {
case CONSTANT_Literal:
case CONSTANT_Integer:
switch (bc) {
default: assert(false);
}
break;
case CONSTANT_Float:
switch (bc) {
default: assert(false);
}
break;
case CONSTANT_Long:
break;
case CONSTANT_Double:
break;
case CONSTANT_String:
switch (bc) {
default: assert(false);
}
break;
case CONSTANT_Class:
switch (bc) {
default: assert(false);
}
break;
default:
assert(false);
}
break;
case CONSTANT_Class:
// Use a special shorthand for the current class:
bc_which = bc_classref; break;
case CONSTANT_Fieldref:
bc_which = bc_fieldref; break;
case CONSTANT_Methodref:
bc_which = bc_methodref; break;
bc_which = bc_imethodref; break;
default:
assert(false);
}
// handle trailing junk
if (bc == _multianewarray) {
// Just dump the byte into the bipush pile
} else if (bc == _invokeinterface) {
assert(i.getLength() == 5);
// Make sure the discarded bytes are sane:
} else {
// Make sure there is nothing else to write.
}
continue;
}
int slot = i.getLocalSlot();
if (slot >= 0) {
int con = i.getConstant();
if (!i.isWide()) {
} else {
}
} else {
assert(con == 0);
}
continue;
}
// Generic instruction. Copy the body.
// Do a few remaining multi-byte instructions.
switch (bc) {
case _sipush:
break;
case _bipush:
break;
case _newarray:
break;
default:
assert(false); // that's it
}
}
}
codeHist[_end_marker]++;
endCode();
}
void printCodeHist() {
assert(verbose > 0);
int totalBytes = 0;
}
}
}
}
if (count == 0) continue;
}
}
}