8234542: code removal of Pack200 Tools and API

Reviewed-by: alanb, mchung, erikj
This commit is contained in:
Henry Jen 2019-12-10 00:36:30 +00:00
parent c198b4da32
commit 9ac2f8b654
107 changed files with 4 additions and 46217 deletions

View file

@ -1,298 +0,0 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.util.jar.pack;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Adaptive coding.
* See the section "Adaptive Encodings" in the Pack200 spec.
* @author John Rose
*/
class AdaptiveCoding implements CodingMethod {
CodingMethod headCoding;
int headLength;
CodingMethod tailCoding;
public AdaptiveCoding(int headLength, CodingMethod headCoding, CodingMethod tailCoding) {
assert(isCodableLength(headLength));
this.headLength = headLength;
this.headCoding = headCoding;
this.tailCoding = tailCoding;
}
public void setHeadCoding(CodingMethod headCoding) {
this.headCoding = headCoding;
}
public void setHeadLength(int headLength) {
assert(isCodableLength(headLength));
this.headLength = headLength;
}
public void setTailCoding(CodingMethod tailCoding) {
this.tailCoding = tailCoding;
}
public boolean isTrivial() {
return headCoding == tailCoding;
}
// CodingMethod methods.
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
writeArray(this, out, a, start, end);
}
// writeArrayTo must be coded iteratively, not recursively:
private static void writeArray(AdaptiveCoding run, OutputStream out, int[] a, int start, int end) throws IOException {
for (;;) {
int mid = start+run.headLength;
assert(mid <= end);
run.headCoding.writeArrayTo(out, a, start, mid);
start = mid;
if (run.tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) run.tailCoding;
continue;
}
break;
}
run.tailCoding.writeArrayTo(out, a, start, end);
}
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
readArray(this, in, a, start, end);
}
private static void readArray(AdaptiveCoding run, InputStream in, int[] a, int start, int end) throws IOException {
for (;;) {
int mid = start+run.headLength;
assert(mid <= end);
run.headCoding.readArrayFrom(in, a, start, mid);
start = mid;
if (run.tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) run.tailCoding;
continue;
}
break;
}
run.tailCoding.readArrayFrom(in, a, start, end);
}
public static final int KX_MIN = 0;
public static final int KX_MAX = 3;
public static final int KX_LG2BASE = 4;
public static final int KX_BASE = 16;
public static final int KB_MIN = 0x00;
public static final int KB_MAX = 0xFF;
public static final int KB_OFFSET = 1;
public static final int KB_DEFAULT = 3;
static int getKXOf(int K) {
for (int KX = KX_MIN; KX <= KX_MAX; KX++) {
if (((K - KB_OFFSET) & ~KB_MAX) == 0)
return KX;
K >>>= KX_LG2BASE;
}
return -1;
}
static int getKBOf(int K) {
int KX = getKXOf(K);
if (KX < 0) return -1;
K >>>= (KX * KX_LG2BASE);
return K-1;
}
static int decodeK(int KX, int KB) {
assert(KX_MIN <= KX && KX <= KX_MAX);
assert(KB_MIN <= KB && KB <= KB_MAX);
return (KB+KB_OFFSET) << (KX * KX_LG2BASE);
}
static int getNextK(int K) {
if (K <= 0) return 1; // 1st K value
int KX = getKXOf(K);
if (KX < 0) return Integer.MAX_VALUE;
// This is the increment we expect to apply:
int unit = 1 << (KX * KX_LG2BASE);
int mask = KB_MAX << (KX * KX_LG2BASE);
int K1 = K + unit;
K1 &= ~(unit-1); // cut off stray low-order bits
if (((K1 - unit) & ~mask) == 0) {
assert(getKXOf(K1) == KX);
return K1;
}
if (KX == KX_MAX) return Integer.MAX_VALUE;
KX += 1;
int mask2 = KB_MAX << (KX * KX_LG2BASE);
K1 |= (mask & ~mask2);
K1 += unit;
assert(getKXOf(K1) == KX);
return K1;
}
// Is K of the form ((KB:[0..255])+1) * 16^(KX:{0..3])?
public static boolean isCodableLength(int K) {
int KX = getKXOf(K);
if (KX < 0) return false;
int unit = 1 << (KX * KX_LG2BASE);
int mask = KB_MAX << (KX * KX_LG2BASE);
return ((K - unit) & ~mask) == 0;
}
public byte[] getMetaCoding(Coding dflt) {
//assert(!isTrivial()); // can happen
// See the isCodableLength restriction in CodingChooser.
ByteArrayOutputStream bytes = new ByteArrayOutputStream(10);
try {
makeMetaCoding(this, dflt, bytes);
} catch (IOException ee) {
throw new RuntimeException(ee);
}
return bytes.toByteArray();
}
private static void makeMetaCoding(AdaptiveCoding run, Coding dflt,
ByteArrayOutputStream bytes)
throws IOException {
for (;;) {
CodingMethod headCoding = run.headCoding;
int headLength = run.headLength;
CodingMethod tailCoding = run.tailCoding;
int K = headLength;
assert(isCodableLength(K));
int ADef = (headCoding == dflt)?1:0;
int BDef = (tailCoding == dflt)?1:0;
if (ADef+BDef > 1) BDef = 0; // arbitrary choice
int ABDef = 1*ADef + 2*BDef;
assert(ABDef < 3);
int KX = getKXOf(K);
int KB = getKBOf(K);
assert(decodeK(KX, KB) == K);
int KBFlag = (KB != KB_DEFAULT)?1:0;
bytes.write(_meta_run + KX + 4*KBFlag + 8*ABDef);
if (KBFlag != 0) bytes.write(KB);
if (ADef == 0) bytes.write(headCoding.getMetaCoding(dflt));
if (tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) tailCoding;
continue; // tail call, to avoid deep stack recursion
}
if (BDef == 0) bytes.write(tailCoding.getMetaCoding(dflt));
break;
}
}
public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
int op = bytes[pos++] & 0xFF;
if (op < _meta_run || op >= _meta_pop) return pos-1; // backup
AdaptiveCoding prevc = null;
for (boolean keepGoing = true; keepGoing; ) {
keepGoing = false;
assert(op >= _meta_run);
op -= _meta_run;
int KX = op % 4;
int KBFlag = (op / 4) % 2;
int ABDef = (op / 8);
assert(ABDef < 3);
int ADef = (ABDef & 1);
int BDef = (ABDef & 2);
CodingMethod[] ACode = {dflt}, BCode = {dflt};
int KB = KB_DEFAULT;
if (KBFlag != 0)
KB = bytes[pos++] & 0xFF;
if (ADef == 0) {
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, ACode);
}
if (BDef == 0 &&
((op = bytes[pos] & 0xFF) >= _meta_run) && op < _meta_pop) {
pos++;
keepGoing = true;
} else if (BDef == 0) {
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, BCode);
}
AdaptiveCoding newc = new AdaptiveCoding(decodeK(KX, KB),
ACode[0], BCode[0]);
if (prevc == null) {
res[0] = newc;
} else {
prevc.tailCoding = newc;
}
prevc = newc;
}
return pos;
}
private String keyString(CodingMethod m) {
if (m instanceof Coding)
return ((Coding)m).keyString();
return m.toString();
}
public String toString() {
StringBuilder res = new StringBuilder(20);
AdaptiveCoding run = this;
res.append("run(");
for (;;) {
res.append(run.headLength).append("*");
res.append(keyString(run.headCoding));
if (run.tailCoding instanceof AdaptiveCoding) {
run = (AdaptiveCoding) run.tailCoding;
res.append(" ");
continue;
}
break;
}
res.append(" **").append(keyString(run.tailCoding));
res.append(")");
return res.toString();
}
/*
public static void main(String av[]) {
int[][] samples = {
{1,2,3,4,5},
{254,255,256,256+1*16,256+2*16},
{0xfd,0xfe,0xff,0x100,0x110,0x120,0x130},
{0xfd0,0xfe0,0xff0,0x1000,0x1100,0x1200,0x1300},
{0xfd00,0xfe00,0xff00,0x10000,0x11000,0x12000,0x13000},
{0xfd000,0xfe000,0xff000,0x100000}
};
for (int i = 0; i < samples.length; i++) {
for (int j = 0; j < samples[i].length; j++) {
int K = samples[i][j];
int KX = getKXOf(K);
int KB = getKBOf(K);
System.out.println("K="+Integer.toHexString(K)+
" KX="+KX+" KB="+KB);
assert(isCodableLength(K));
assert(K == decodeK(KX, KB));
if (j == 0) continue;
int K1 = samples[i][j-1];
assert(K == getNextK(K1));
}
}
}
//*/
}

View file

@ -1,619 +0,0 @@
/*
* Copyright (c) 2001, 2019, 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.ClassEntry;
import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
import com.sun.java.util.jar.pack.ConstantPool.Entry;
import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry;
import com.sun.java.util.jar.pack.ConstantPool.MemberEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass;
import java.io.DataInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Reader for a class file that is being incorporated into a package.
* @author John Rose
*/
class ClassReader {
int verbose;
Package pkg;
Class cls;
long inPos;
long constantPoolLimit = -1;
DataInputStream in;
Map<Attribute.Layout, Attribute> attrDefs;
Map<Attribute.Layout, String> attrCommands;
String unknownAttrCommand = "error";;
ClassReader(Class cls, InputStream in) throws IOException {
this.pkg = cls.getPackage();
this.cls = cls;
this.verbose = pkg.verbose;
this.in = new DataInputStream(new FilterInputStream(in) {
public int read(byte b[], int off, int len) throws IOException {
int nr = super.read(b, off, len);
if (nr >= 0) inPos += nr;
return nr;
}
public int read() throws IOException {
int ch = super.read();
if (ch >= 0) inPos += 1;
return ch;
}
public long skip(long n) throws IOException {
long ns = super.skip(n);
if (ns >= 0) inPos += ns;
return ns;
}
});
}
public void setAttrDefs(Map<Attribute.Layout, Attribute> attrDefs) {
this.attrDefs = attrDefs;
}
public void setAttrCommands(Map<Attribute.Layout, String> attrCommands) {
this.attrCommands = attrCommands;
}
private void skip(int n, String what) throws IOException {
Utils.log.warning("skipping "+n+" bytes of "+what);
long skipped = 0;
while (skipped < n) {
long j = in.skip(n - skipped);
assert(j > 0);
skipped += j;
}
assert(skipped == n);
}
private int readUnsignedShort() throws IOException {
return in.readUnsignedShort();
}
private int readInt() throws IOException {
return in.readInt();
}
/** Read a 2-byte int, and return the <em>global</em> CP entry for it. */
private Entry readRef() throws IOException {
int i = in.readUnsignedShort();
return i == 0 ? null : cls.cpMap[i];
}
private Entry readRef(byte tag) throws IOException {
Entry e = readRef();
assert(!(e instanceof UnresolvedEntry));
checkTag(e, tag);
return e;
}
/** Throw a ClassFormatException if the entry does not match the expected tag type. */
private Entry checkTag(Entry e, byte tag) throws ClassFormatException {
if (e == null || !e.tagMatches(tag)) {
String where = (inPos == constantPoolLimit
? " in constant pool"
: " at pos: " + inPos);
String got = (e == null
? "null CP index"
: "type=" + ConstantPool.tagName(e.tag));
throw new ClassFormatException("Bad constant, expected type=" +
ConstantPool.tagName(tag) +
" got "+ got + ", in File: " + cls.file.nameString + where);
}
return e;
}
private Entry checkTag(Entry e, byte tag, boolean nullOK) throws ClassFormatException {
return nullOK && e == null ? null : checkTag(e, tag);
}
private Entry readRefOrNull(byte tag) throws IOException {
Entry e = readRef();
checkTag(e, tag, true);
return e;
}
private Utf8Entry readUtf8Ref() throws IOException {
return (Utf8Entry) readRef(CONSTANT_Utf8);
}
private ClassEntry readClassRef() throws IOException {
return (ClassEntry) readRef(CONSTANT_Class);
}
private ClassEntry readClassRefOrNull() throws IOException {
return (ClassEntry) readRefOrNull(CONSTANT_Class);
}
private SignatureEntry readSignatureRef() throws IOException {
// The class file stores a Utf8, but we want a Signature.
Entry e = readRef(CONSTANT_Signature);
return (e != null && e.getTag() == CONSTANT_Utf8)
? ConstantPool.getSignatureEntry(e.stringValue())
: (SignatureEntry) e;
}
void read() throws IOException {
boolean ok = false;
try {
readMagicNumbers();
readConstantPool();
readHeader();
readMembers(false); // fields
readMembers(true); // methods
readAttributes(ATTR_CONTEXT_CLASS, cls);
fixUnresolvedEntries();
cls.finishReading();
assert(0 >= in.read(new byte[1]));
ok = true;
} finally {
if (!ok) {
if (verbose > 0) Utils.log.warning("Erroneous data at input offset "+inPos+" of "+cls.file);
}
}
}
void readMagicNumbers() throws IOException {
cls.magic = in.readInt();
if (cls.magic != JAVA_MAGIC)
throw new Attribute.FormatException
("Bad magic number in class file "
+Integer.toHexString(cls.magic),
ATTR_CONTEXT_CLASS, "magic-number", "pass");
int minver = (short) readUnsignedShort();
int majver = (short) readUnsignedShort();
cls.version = Package.Version.of(majver, minver);
//System.out.println("ClassFile.version="+cls.majver+"."+cls.minver);
String bad = checkVersion(cls.version);
if (bad != null) {
throw new Attribute.FormatException
("classfile version too "+bad+": "
+cls.version+" in "+cls.file,
ATTR_CONTEXT_CLASS, "version", "pass");
}
}
private String checkVersion(Package.Version ver) {
int majver = ver.major;
int minver = ver.minor;
if (majver < pkg.minClassVersion.major ||
(majver == pkg.minClassVersion.major &&
minver < pkg.minClassVersion.minor)) {
return "small";
}
if (majver > pkg.maxClassVersion.major ||
(majver == pkg.maxClassVersion.major &&
minver > pkg.maxClassVersion.minor)) {
return "large";
}
return null; // OK
}
void readConstantPool() throws IOException {
int length = in.readUnsignedShort();
//System.err.println("reading CP, length="+length);
int[] fixups = new int[length*4];
int fptr = 0;
Entry[] cpMap = new Entry[length];
cpMap[0] = null;
for (int i = 1; i < length; i++) {
//System.err.println("reading CP elt, i="+i);
int tag = in.readByte();
switch (tag) {
case CONSTANT_Utf8:
cpMap[i] = ConstantPool.getUtf8Entry(in.readUTF());
break;
case CONSTANT_Integer:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readInt());
}
break;
case CONSTANT_Float:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readFloat());
}
break;
case CONSTANT_Long:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readLong());
cpMap[++i] = null;
}
break;
case CONSTANT_Double:
{
cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble());
cpMap[++i] = null;
}
break;
// just read the refs; do not attempt to resolve while reading
case CONSTANT_Class:
case CONSTANT_String:
case CONSTANT_MethodType:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = in.readUnsignedShort();
fixups[fptr++] = -1; // empty ref2
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = in.readUnsignedShort();
fixups[fptr++] = in.readUnsignedShort();
break;
case CONSTANT_InvokeDynamic:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = -1 ^ in.readUnsignedShort(); // not a ref
fixups[fptr++] = in.readUnsignedShort();
break;
case CONSTANT_MethodHandle:
fixups[fptr++] = i;
fixups[fptr++] = tag;
fixups[fptr++] = -1 ^ in.readUnsignedByte();
fixups[fptr++] = in.readUnsignedShort();
break;
default:
throw new ClassFormatException("Bad constant pool tag " +
tag + " in File: " + cls.file.nameString +
" at pos: " + inPos);
}
}
constantPoolLimit = inPos;
// Fix up refs, which might be out of order.
while (fptr > 0) {
if (verbose > 3)
Utils.log.fine("CP fixups ["+fptr/4+"]");
int flimit = fptr;
fptr = 0;
for (int fi = 0; fi < flimit; ) {
int cpi = fixups[fi++];
int tag = fixups[fi++];
int ref = fixups[fi++];
int ref2 = fixups[fi++];
if (verbose > 3)
Utils.log.fine(" cp["+cpi+"] = "+ConstantPool.tagName(tag)+"{"+ref+","+ref2+"}");
if (ref >= 0 && cpMap[ref] == null || ref2 >= 0 && cpMap[ref2] == null) {
// Defer.
fixups[fptr++] = cpi;
fixups[fptr++] = tag;
fixups[fptr++] = ref;
fixups[fptr++] = ref2;
continue;
}
switch (tag) {
case CONSTANT_Class:
cpMap[cpi] = ConstantPool.getClassEntry(cpMap[ref].stringValue());
break;
case CONSTANT_String:
cpMap[cpi] = ConstantPool.getStringEntry(cpMap[ref].stringValue());
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
ClassEntry mclass = (ClassEntry) checkTag(cpMap[ref], CONSTANT_Class);
DescriptorEntry mdescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType);
cpMap[cpi] = ConstantPool.getMemberEntry((byte)tag, mclass, mdescr);
break;
case CONSTANT_NameandType:
Utf8Entry mname = (Utf8Entry) checkTag(cpMap[ref], CONSTANT_Utf8);
Utf8Entry mtype = (Utf8Entry) checkTag(cpMap[ref2], CONSTANT_Signature);
cpMap[cpi] = ConstantPool.getDescriptorEntry(mname, mtype);
break;
case CONSTANT_MethodType:
cpMap[cpi] = ConstantPool.getMethodTypeEntry((Utf8Entry) checkTag(cpMap[ref], CONSTANT_Signature));
break;
case CONSTANT_MethodHandle:
byte refKind = (byte)(-1 ^ ref);
MemberEntry memRef = (MemberEntry) checkTag(cpMap[ref2], CONSTANT_AnyMember);
cpMap[cpi] = ConstantPool.getMethodHandleEntry(refKind, memRef);
break;
case CONSTANT_InvokeDynamic:
DescriptorEntry idescr = (DescriptorEntry) checkTag(cpMap[ref2], CONSTANT_NameandType);
cpMap[cpi] = new UnresolvedEntry((byte)tag, (-1 ^ ref), idescr);
// Note that ref must be resolved later, using the BootstrapMethods attribute.
break;
default:
assert(false);
}
}
assert(fptr < flimit); // Must make progress.
}
cls.cpMap = cpMap;
}
private /*non-static*/
class UnresolvedEntry extends Entry {
final Object[] refsOrIndexes;
UnresolvedEntry(byte tag, Object... refsOrIndexes) {
super(tag);
this.refsOrIndexes = refsOrIndexes;
ClassReader.this.haveUnresolvedEntry = true;
}
Entry resolve() {
Class cls = ClassReader.this.cls;
Entry res;
switch (tag) {
case CONSTANT_InvokeDynamic:
BootstrapMethodEntry iboots = cls.bootstrapMethods.get((Integer) refsOrIndexes[0]);
DescriptorEntry idescr = (DescriptorEntry) refsOrIndexes[1];
res = ConstantPool.getInvokeDynamicEntry(iboots, idescr);
break;
default:
throw new AssertionError();
}
return res;
}
private void unresolved() { throw new RuntimeException("unresolved entry has no string"); }
public int compareTo(Object x) { unresolved(); return 0; }
public boolean equals(Object x) { unresolved(); return false; }
protected int computeValueHash() { unresolved(); return 0; }
public String stringValue() { unresolved(); return toString(); }
public String toString() { return "(unresolved "+ConstantPool.tagName(tag)+")"; }
}
boolean haveUnresolvedEntry;
private void fixUnresolvedEntries() {
if (!haveUnresolvedEntry) return;
Entry[] cpMap = cls.getCPMap();
for (int i = 0; i < cpMap.length; i++) {
Entry e = cpMap[i];
if (e instanceof UnresolvedEntry) {
cpMap[i] = e = ((UnresolvedEntry)e).resolve();
assert(!(e instanceof UnresolvedEntry));
}
}
haveUnresolvedEntry = false;
}
void readHeader() throws IOException {
cls.flags = readUnsignedShort();
cls.thisClass = readClassRef();
cls.superClass = readClassRefOrNull();
int ni = readUnsignedShort();
cls.interfaces = new ClassEntry[ni];
for (int i = 0; i < ni; i++) {
cls.interfaces[i] = readClassRef();
}
}
void readMembers(boolean doMethods) throws IOException {
int nm = readUnsignedShort();
for (int i = 0; i < nm; i++) {
readMember(doMethods);
}
}
void readMember(boolean doMethod) throws IOException {
int mflags = readUnsignedShort();
Utf8Entry mname = readUtf8Ref();
SignatureEntry mtype = readSignatureRef();
DescriptorEntry descr = ConstantPool.getDescriptorEntry(mname, mtype);
Class.Member m;
if (!doMethod)
m = cls.new Field(mflags, descr);
else
m = cls.new Method(mflags, descr);
readAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
m);
}
void readAttributes(int ctype, Attribute.Holder h) throws IOException {
int na = readUnsignedShort();
if (na == 0) return; // nothing to do here
if (verbose > 3)
Utils.log.fine("readAttributes "+h+" ["+na+"]");
for (int i = 0; i < na; i++) {
String name = readUtf8Ref().stringValue();
int length = readInt();
// See if there is a special command that applies.
if (attrCommands != null) {
Attribute.Layout lkey = Attribute.keyForLookup(ctype, name);
String cmd = attrCommands.get(lkey);
if (cmd != null) {
switch (cmd) {
case "pass":
String message1 = "passing attribute bitwise in " + h;
throw new Attribute.FormatException(message1, ctype, name, cmd);
case "error":
String message2 = "attribute not allowed in " + h;
throw new Attribute.FormatException(message2, ctype, name, cmd);
case "strip":
skip(length, name + " attribute in " + h);
continue;
}
}
}
// Find canonical instance of the requested attribute.
Attribute a = Attribute.lookup(Package.attrDefs, ctype, name);
if (verbose > 4 && a != null)
Utils.log.fine("pkg_attribute_lookup "+name+" = "+a);
if (a == null) {
a = Attribute.lookup(this.attrDefs, ctype, name);
if (verbose > 4 && a != null)
Utils.log.fine("this "+name+" = "+a);
}
if (a == null) {
a = Attribute.lookup(null, ctype, name);
if (verbose > 4 && a != null)
Utils.log.fine("null_attribute_lookup "+name+" = "+a);
}
if (a == null && length == 0) {
// Any zero-length attr is "known"...
// We can assume an empty attr. has an empty layout.
// Handles markers like Enum, Bridge, Synthetic, Deprecated.
a = Attribute.find(ctype, name, "");
}
boolean isStackMap = (ctype == ATTR_CONTEXT_CODE
&& (name.equals("StackMap") ||
name.equals("StackMapX")));
if (isStackMap) {
// Known attribute but with a corner case format, "pass" it.
Code code = (Code) h;
final int TOO_BIG = 0x10000;
if (code.max_stack >= TOO_BIG ||
code.max_locals >= TOO_BIG ||
code.getLength() >= TOO_BIG ||
name.endsWith("X")) {
// No, we don't really know what to do with this one.
// Do not compress the rare and strange "u4" and "X" cases.
a = null;
}
}
if (a == null) {
if (isStackMap) {
// Known attribute but w/o a format; pass it.
String message = "unsupported StackMap variant in "+h;
throw new Attribute.FormatException(message, ctype, name,
"pass");
} else if ("strip".equals(unknownAttrCommand)) {
// Skip the unknown attribute.
skip(length, "unknown "+name+" attribute in "+h);
continue;
} else {
String message = " is unknown attribute in class " + h;
throw new Attribute.FormatException(message, ctype, name,
unknownAttrCommand);
}
}
long pos0 = inPos; // in case we want to check it
if (a.layout() == Package.attrCodeEmpty) {
// These are hardwired.
Class.Method m = (Class.Method) h;
m.code = new Code(m);
try {
readCode(m.code);
} catch (Instruction.FormatException iie) {
String message = iie.getMessage() + " in " + h;
throw new ClassReader.ClassFormatException(message, iie);
}
assert(length == inPos - pos0);
// Keep empty attribute a...
} else if (a.layout() == Package.attrBootstrapMethodsEmpty) {
assert(h == cls);
readBootstrapMethods(cls);
assert(length == inPos - pos0);
// Delete the attribute; it is logically part of the constant pool.
continue;
} else if (a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired also.
assert(h == cls);
readInnerClasses(cls);
assert(length == inPos - pos0);
// Keep empty attribute a...
} else if (length > 0) {
byte[] bytes = new byte[length];
in.readFully(bytes);
a = a.addContent(bytes);
}
if (a.size() == 0 && !a.layout().isEmpty()) {
throw new ClassFormatException(name +
": attribute length cannot be zero, in " + h);
}
h.addAttribute(a);
if (verbose > 2)
Utils.log.fine("read "+a);
}
}
void readCode(Code code) throws IOException {
code.max_stack = readUnsignedShort();
code.max_locals = readUnsignedShort();
code.bytes = new byte[readInt()];
in.readFully(code.bytes);
Entry[] cpMap = cls.getCPMap();
Instruction.opcodeChecker(code.bytes, cpMap, this.cls.version);
int nh = readUnsignedShort();
code.setHandlerCount(nh);
for (int i = 0; i < nh; i++) {
code.handler_start[i] = readUnsignedShort();
code.handler_end[i] = readUnsignedShort();
code.handler_catch[i] = readUnsignedShort();
code.handler_class[i] = readClassRefOrNull();
}
readAttributes(ATTR_CONTEXT_CODE, code);
}
void readBootstrapMethods(Class cls) throws IOException {
BootstrapMethodEntry[] bsms = new BootstrapMethodEntry[readUnsignedShort()];
for (int i = 0; i < bsms.length; i++) {
MethodHandleEntry bsmRef = (MethodHandleEntry) readRef(CONSTANT_MethodHandle);
Entry[] argRefs = new Entry[readUnsignedShort()];
for (int j = 0; j < argRefs.length; j++) {
argRefs[j] = readRef(CONSTANT_LoadableValue);
}
bsms[i] = ConstantPool.getBootstrapMethodEntry(bsmRef, argRefs);
}
cls.setBootstrapMethods(Arrays.asList(bsms));
}
void readInnerClasses(Class cls) throws IOException {
int nc = readUnsignedShort();
ArrayList<InnerClass> ics = new ArrayList<>(nc);
for (int i = 0; i < nc; i++) {
InnerClass ic =
new InnerClass(readClassRef(),
readClassRefOrNull(),
(Utf8Entry)readRefOrNull(CONSTANT_Utf8),
readUnsignedShort());
ics.add(ic);
}
cls.innerClasses = ics; // set directly; do not use setInnerClasses.
// (Later, ics may be transferred to the pkg.)
}
static class ClassFormatException extends IOException {
@java.io.Serial
private static final long serialVersionUID = -3564121733989501833L;
public ClassFormatException(String message) {
super(message);
}
public ClassFormatException(String message, Throwable cause) {
super(message, cause);
}
}
}

View file

@ -1,317 +0,0 @@
/*
* Copyright (c) 2001, 2012, 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.Entry;
import com.sun.java.util.jar.pack.ConstantPool.Index;
import com.sun.java.util.jar.pack.ConstantPool.NumberEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
import com.sun.java.util.jar.pack.Package.Class;
import com.sun.java.util.jar.pack.Package.InnerClass;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Writer for a class file that is incorporated into a package.
* @author John Rose
*/
class ClassWriter {
int verbose;
Package pkg;
Class cls;
DataOutputStream out;
Index cpIndex;
Index bsmIndex;
ClassWriter(Class cls, OutputStream out) throws IOException {
this.pkg = cls.getPackage();
this.cls = cls;
this.verbose = pkg.verbose;
this.out = new DataOutputStream(new BufferedOutputStream(out));
this.cpIndex = ConstantPool.makeIndex(cls.toString(), cls.getCPMap());
this.cpIndex.flattenSigs = true;
if (cls.hasBootstrapMethods()) {
this.bsmIndex = ConstantPool.makeIndex(cpIndex.debugName+".BootstrapMethods",
cls.getBootstrapMethodMap());
}
if (verbose > 1)
Utils.log.fine("local CP="+(verbose > 2 ? cpIndex.dumpString() : cpIndex.toString()));
}
private void writeShort(int x) throws IOException {
out.writeShort(x);
}
private void writeInt(int x) throws IOException {
out.writeInt(x);
}
/** Write a 2-byte int representing a CP entry, using the local cpIndex. */
private void writeRef(Entry e) throws IOException {
writeRef(e, cpIndex);
}
/** Write a 2-byte int representing a CP entry, using the given cpIndex. */
private void writeRef(Entry e, Index cpIndex) throws IOException {
int i = (e == null) ? 0 : cpIndex.indexOf(e);
writeShort(i);
}
void write() throws IOException {
boolean ok = false;
try {
if (verbose > 1) Utils.log.fine("...writing "+cls);
writeMagicNumbers();
writeConstantPool();
writeHeader();
writeMembers(false); // fields
writeMembers(true); // methods
writeAttributes(ATTR_CONTEXT_CLASS, cls);
/* Closing here will cause all the underlying
streams to close, Causing the jar stream
to close prematurely, instead we just flush.
out.close();
*/
out.flush();
ok = true;
} finally {
if (!ok) {
Utils.log.warning("Error on output of "+cls);
}
}
}
void writeMagicNumbers() throws IOException {
writeInt(cls.magic);
writeShort(cls.version.minor);
writeShort(cls.version.major);
}
void writeConstantPool() throws IOException {
Entry[] cpMap = cls.cpMap;
writeShort(cpMap.length);
for (int i = 0; i < cpMap.length; i++) {
Entry e = cpMap[i];
assert((e == null) == (i == 0 || cpMap[i-1] != null && cpMap[i-1].isDoubleWord()));
if (e == null) continue;
byte tag = e.getTag();
if (verbose > 2) Utils.log.fine(" CP["+i+"] = "+e);
out.write(tag);
switch (tag) {
case CONSTANT_Signature:
throw new AssertionError("CP should have Signatures remapped to Utf8");
case CONSTANT_Utf8:
out.writeUTF(e.stringValue());
break;
case CONSTANT_Integer:
out.writeInt(((NumberEntry)e).numberValue().intValue());
break;
case CONSTANT_Float:
float fval = ((NumberEntry)e).numberValue().floatValue();
out.writeInt(Float.floatToRawIntBits(fval));
break;
case CONSTANT_Long:
out.writeLong(((NumberEntry)e).numberValue().longValue());
break;
case CONSTANT_Double:
double dval = ((NumberEntry)e).numberValue().doubleValue();
out.writeLong(Double.doubleToRawLongBits(dval));
break;
case CONSTANT_Class:
case CONSTANT_String:
case CONSTANT_MethodType:
writeRef(e.getRef(0));
break;
case CONSTANT_MethodHandle:
MethodHandleEntry mhe = (MethodHandleEntry) e;
out.writeByte(mhe.refKind);
writeRef(mhe.getRef(0));
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_NameandType:
writeRef(e.getRef(0));
writeRef(e.getRef(1));
break;
case CONSTANT_InvokeDynamic:
writeRef(e.getRef(0), bsmIndex);
writeRef(e.getRef(1));
break;
case CONSTANT_BootstrapMethod:
throw new AssertionError("CP should have BootstrapMethods moved to side-table");
default:
throw new IOException("Bad constant pool tag "+tag);
}
}
}
void writeHeader() throws IOException {
writeShort(cls.flags);
writeRef(cls.thisClass);
writeRef(cls.superClass);
writeShort(cls.interfaces.length);
for (int i = 0; i < cls.interfaces.length; i++) {
writeRef(cls.interfaces[i]);
}
}
void writeMembers(boolean doMethods) throws IOException {
List<? extends Class.Member> mems;
if (!doMethods)
mems = cls.getFields();
else
mems = cls.getMethods();
writeShort(mems.size());
for (Class.Member m : mems) {
writeMember(m, doMethods);
}
}
void writeMember(Class.Member m, boolean doMethod) throws IOException {
if (verbose > 2) Utils.log.fine("writeMember "+m);
writeShort(m.flags);
writeRef(m.getDescriptor().nameRef);
writeRef(m.getDescriptor().typeRef);
writeAttributes(!doMethod ? ATTR_CONTEXT_FIELD : ATTR_CONTEXT_METHOD,
m);
}
private void reorderBSMandICS(Attribute.Holder h) {
Attribute bsmAttr = h.getAttribute(Package.attrBootstrapMethodsEmpty);
if (bsmAttr == null) return;
Attribute icsAttr = h.getAttribute(Package.attrInnerClassesEmpty);
if (icsAttr == null) return;
int bsmidx = h.attributes.indexOf(bsmAttr);
int icsidx = h.attributes.indexOf(icsAttr);
if (bsmidx > icsidx) {
h.attributes.remove(bsmAttr);
h.attributes.add(icsidx, bsmAttr);
}
return;
}
// handy buffer for collecting attrs
ByteArrayOutputStream buf = new ByteArrayOutputStream();
DataOutputStream bufOut = new DataOutputStream(buf);
void writeAttributes(int ctype, Attribute.Holder h) throws IOException {
if (h.attributes == null) {
writeShort(0); // attribute size
return;
}
// there may be cases if an InnerClass attribute is explicit, then the
// ordering could be wrong, fix the ordering before we write it out.
if (h instanceof Package.Class)
reorderBSMandICS(h);
writeShort(h.attributes.size());
for (Attribute a : h.attributes) {
a.finishRefs(cpIndex);
writeRef(a.getNameRef());
if (a.layout() == Package.attrCodeEmpty ||
a.layout() == Package.attrBootstrapMethodsEmpty ||
a.layout() == Package.attrInnerClassesEmpty) {
// These are hardwired.
DataOutputStream savedOut = out;
assert(out != bufOut);
buf.reset();
out = bufOut;
if ("Code".equals(a.name())) {
Class.Method m = (Class.Method) h;
writeCode(m.code);
} else if ("BootstrapMethods".equals(a.name())) {
assert(h == cls);
writeBootstrapMethods(cls);
} else if ("InnerClasses".equals(a.name())) {
assert(h == cls);
writeInnerClasses(cls);
} else {
throw new AssertionError();
}
out = savedOut;
if (verbose > 2)
Utils.log.fine("Attribute "+a.name()+" ["+buf.size()+"]");
writeInt(buf.size());
buf.writeTo(out);
} else {
if (verbose > 2)
Utils.log.fine("Attribute "+a.name()+" ["+a.size()+"]");
writeInt(a.size());
out.write(a.bytes());
}
}
}
void writeCode(Code code) throws IOException {
code.finishRefs(cpIndex);
writeShort(code.max_stack);
writeShort(code.max_locals);
writeInt(code.bytes.length);
out.write(code.bytes);
int nh = code.getHandlerCount();
writeShort(nh);
for (int i = 0; i < nh; i++) {
writeShort(code.handler_start[i]);
writeShort(code.handler_end[i]);
writeShort(code.handler_catch[i]);
writeRef(code.handler_class[i]);
}
writeAttributes(ATTR_CONTEXT_CODE, code);
}
void writeBootstrapMethods(Class cls) throws IOException {
List<BootstrapMethodEntry> bsms = cls.getBootstrapMethods();
writeShort(bsms.size());
for (BootstrapMethodEntry e : bsms) {
writeRef(e.bsmRef);
writeShort(e.argRefs.length);
for (Entry argRef : e.argRefs) {
writeRef(argRef);
}
}
}
void writeInnerClasses(Class cls) throws IOException {
List<InnerClass> ics = cls.getInnerClasses();
writeShort(ics.size());
for (InnerClass ic : ics) {
writeRef(ic.thisClass);
writeRef(ic.outerClass);
writeRef(ic.name);
writeShort(ic.flags);
}
}
}

View file

@ -1,398 +0,0 @@
/*
* 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.Package.Class;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Represents a chunk of bytecodes.
* @author John Rose
*/
class Code extends Attribute.Holder {
Class.Method m;
public Code(Class.Method m) {
this.m = m;
}
public Class.Method getMethod() {
return m;
}
public Class thisClass() {
return m.thisClass();
}
public Package getPackage() {
return m.thisClass().getPackage();
}
public ConstantPool.Entry[] getCPMap() {
return m.getCPMap();
}
private static final ConstantPool.Entry[] noRefs = ConstantPool.noRefs;
// The following fields are used directly by the ClassReader, etc.
int max_stack;
int max_locals;
ConstantPool.Entry handler_class[] = noRefs;
int handler_start[] = noInts;
int handler_end[] = noInts;
int handler_catch[] = noInts;
byte[] bytes;
Fixups fixups; // reference relocations, if any are required
Object insnMap; // array of instruction boundaries
int getLength() { return bytes.length; }
int getMaxStack() {
return max_stack;
}
void setMaxStack(int ms) {
max_stack = ms;
}
int getMaxNALocals() {
int argsize = m.getArgumentSize();
return max_locals - argsize;
}
void setMaxNALocals(int ml) {
int argsize = m.getArgumentSize();
max_locals = argsize + ml;
}
int getHandlerCount() {
assert(handler_class.length == handler_start.length);
assert(handler_class.length == handler_end.length);
assert(handler_class.length == handler_catch.length);
return handler_class.length;
}
void setHandlerCount(int h) {
if (h > 0) {
handler_class = new ConstantPool.Entry[h];
handler_start = new int[h];
handler_end = new int[h];
handler_catch = new int[h];
// caller must fill these in ASAP
}
}
void setBytes(byte[] bytes) {
this.bytes = bytes;
if (fixups != null)
fixups.setBytes(bytes);
}
void setInstructionMap(int[] insnMap, int mapLen) {
//int[] oldMap = null;
//assert((oldMap = getInstructionMap()) != null);
this.insnMap = allocateInstructionMap(insnMap, mapLen);
//assert(Arrays.equals(oldMap, getInstructionMap()));
}
void setInstructionMap(int[] insnMap) {
setInstructionMap(insnMap, insnMap.length);
}
int[] getInstructionMap() {
return expandInstructionMap(getInsnMap());
}
void addFixups(Collection<Fixups.Fixup> moreFixups) {
if (fixups == null) {
fixups = new Fixups(bytes);
}
assert(fixups.getBytes() == bytes);
fixups.addAll(moreFixups);
}
public void trimToSize() {
if (fixups != null) {
fixups.trimToSize();
if (fixups.size() == 0)
fixups = null;
}
super.trimToSize();
}
protected void visitRefs(int mode, Collection<ConstantPool.Entry> refs) {
int verbose = getPackage().verbose;
if (verbose > 2)
System.out.println("Reference scan "+this);
refs.addAll(Arrays.asList(handler_class));
if (fixups != null) {
fixups.visitRefs(refs);
} else {
// References (to a local cpMap) are embedded in the bytes.
ConstantPool.Entry[] cpMap = getCPMap();
for (Instruction i = instructionAt(0); i != null; i = i.next()) {
if (verbose > 4)
System.out.println(i);
int cpref = i.getCPIndex();
if (cpref >= 0) {
refs.add(cpMap[cpref]);
}
}
}
// Handle attribute list:
super.visitRefs(mode, refs);
}
// Since bytecodes are the single largest contributor to
// package size, it's worth a little bit of trouble
// to reduce the per-bytecode memory footprint.
// In the current scheme, half of the bulk of these arrays
// due to bytes, and half to shorts. (Ints are insignificant.)
// Given an average of 1.8 bytes per instruction, this means
// instruction boundary arrays are about a 75% overhead--tolerable.
// (By using bytes, we get 33% savings over just shorts and ints.
// Using both bytes and shorts gives 66% savings over just ints.)
static final boolean shrinkMaps = true;
private Object allocateInstructionMap(int[] insnMap, int mapLen) {
int PClimit = getLength();
if (shrinkMaps && PClimit <= Byte.MAX_VALUE - Byte.MIN_VALUE) {
byte[] map = new byte[mapLen+1];
for (int i = 0; i < mapLen; i++) {
map[i] = (byte)(insnMap[i] + Byte.MIN_VALUE);
}
map[mapLen] = (byte)(PClimit + Byte.MIN_VALUE);
return map;
} else if (shrinkMaps && PClimit < Short.MAX_VALUE - Short.MIN_VALUE) {
short[] map = new short[mapLen+1];
for (int i = 0; i < mapLen; i++) {
map[i] = (short)(insnMap[i] + Short.MIN_VALUE);
}
map[mapLen] = (short)(PClimit + Short.MIN_VALUE);
return map;
} else {
int[] map = Arrays.copyOf(insnMap, mapLen + 1);
map[mapLen] = PClimit;
return map;
}
}
private int[] expandInstructionMap(Object map0) {
int[] imap;
if (map0 instanceof byte[]) {
byte[] map = (byte[]) map0;
imap = new int[map.length-1];
for (int i = 0; i < imap.length; i++) {
imap[i] = map[i] - Byte.MIN_VALUE;
}
} else if (map0 instanceof short[]) {
short[] map = (short[]) map0;
imap = new int[map.length-1];
for (int i = 0; i < imap.length; i++) {
imap[i] = map[i] - Byte.MIN_VALUE;
}
} else {
int[] map = (int[]) map0;
imap = Arrays.copyOfRange(map, 0, map.length - 1);
}
return imap;
}
Object getInsnMap() {
// Build a map of instruction boundaries.
if (insnMap != null) {
return insnMap;
}
int[] map = new int[getLength()];
int fillp = 0;
for (Instruction i = instructionAt(0); i != null; i = i.next()) {
map[fillp++] = i.getPC();
}
// Make it byte[], short[], or int[] according to the max BCI.
insnMap = allocateInstructionMap(map, fillp);
//assert(assertBCICodingsOK());
return insnMap;
}
/** Encode the given BCI as an instruction boundary number.
* For completeness, irregular (non-boundary) BCIs are
* encoded compactly immediately after the boundary numbers.
* This encoding is the identity mapping outside 0..length,
* and it is 1-1 everywhere. All by itself this technique
* improved zipped rt.jar compression by 2.6%.
*/
public int encodeBCI(int bci) {
if (bci <= 0 || bci > getLength()) return bci;
Object map0 = getInsnMap();
int i, len;
if (shrinkMaps && map0 instanceof byte[]) {
byte[] map = (byte[]) map0;
len = map.length;
i = Arrays.binarySearch(map, (byte)(bci + Byte.MIN_VALUE));
} else if (shrinkMaps && map0 instanceof short[]) {
short[] map = (short[]) map0;
len = map.length;
i = Arrays.binarySearch(map, (short)(bci + Short.MIN_VALUE));
} else {
int[] map = (int[]) map0;
len = map.length;
i = Arrays.binarySearch(map, bci);
}
assert(i != -1);
assert(i != 0);
assert(i != len);
assert(i != -len-1);
return (i >= 0) ? i : len + bci - (-i-1);
}
public int decodeBCI(int bciCode) {
if (bciCode <= 0 || bciCode > getLength()) return bciCode;
Object map0 = getInsnMap();
int i, len;
// len == map.length
// If bciCode < len, result is map[bciCode], the common and fast case.
// Otherwise, let map[i] be the smallest map[*] larger than bci.
// Then, required by the return statement of encodeBCI:
// bciCode == len + bci - i
// Thus:
// bci-i == bciCode-len
// map[i]-adj-i == bciCode-len ; adj in (0..map[i]-map[i-1])
// We can solve this by searching for adjacent entries
// map[i-1], map[i] such that:
// map[i-1]-(i-1) <= bciCode-len < map[i]-i
// This can be approximated by searching map[i] for bciCode and then
// linear searching backward. Given the right i, we then have:
// bci == bciCode-len + i
// This linear search is at its worst case for indexes in the beginning
// of a large method, but it's not clear that this is a problem in
// practice, since BCIs are usually on instruction boundaries.
if (shrinkMaps && map0 instanceof byte[]) {
byte[] map = (byte[]) map0;
len = map.length;
if (bciCode < len)
return map[bciCode] - Byte.MIN_VALUE;
i = Arrays.binarySearch(map, (byte)(bciCode + Byte.MIN_VALUE));
if (i < 0) i = -i-1;
int key = bciCode-len + Byte.MIN_VALUE;
for (;; i--) {
if (map[i-1]-(i-1) <= key) break;
}
} else if (shrinkMaps && map0 instanceof short[]) {
short[] map = (short[]) map0;
len = map.length;
if (bciCode < len)
return map[bciCode] - Short.MIN_VALUE;
i = Arrays.binarySearch(map, (short)(bciCode + Short.MIN_VALUE));
if (i < 0) i = -i-1;
int key = bciCode-len + Short.MIN_VALUE;
for (;; i--) {
if (map[i-1]-(i-1) <= key) break;
}
} else {
int[] map = (int[]) map0;
len = map.length;
if (bciCode < len)
return map[bciCode];
i = Arrays.binarySearch(map, bciCode);
if (i < 0) i = -i-1;
int key = bciCode-len;
for (;; i--) {
if (map[i-1]-(i-1) <= key) break;
}
}
return bciCode-len + i;
}
public void finishRefs(ConstantPool.Index ix) {
if (fixups != null) {
fixups.finishRefs(ix);
fixups = null;
}
// Code attributes are finished in ClassWriter.writeAttributes.
}
Instruction instructionAt(int pc) {
return Instruction.at(bytes, pc);
}
static boolean flagsRequireCode(int flags) {
// A method's flags force it to have a Code attribute,
// if the flags are neither native nor abstract.
return (flags & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0;
}
public String toString() {
return m+".Code";
}
/// Fetching values from my own array.
public int getInt(int pc) { return Instruction.getInt(bytes, pc); }
public int getShort(int pc) { return Instruction.getShort(bytes, pc); }
public int getByte(int pc) { return Instruction.getByte(bytes, pc); }
void setInt(int pc, int x) { Instruction.setInt(bytes, pc, x); }
void setShort(int pc, int x) { Instruction.setShort(bytes, pc, x); }
void setByte(int pc, int x) { Instruction.setByte(bytes, pc, x); }
/* TEST CODE ONLY
private boolean assertBCICodingsOK() {
boolean ok = true;
int len = java.lang.reflect.Array.getLength(insnMap);
int base = 0;
if (insnMap.getClass().getComponentType() == Byte.TYPE)
base = Byte.MIN_VALUE;
if (insnMap.getClass().getComponentType() == Short.TYPE)
base = Short.MIN_VALUE;
for (int i = -1, imax = getLength()+1; i <= imax; i++) {
int bci = i;
int enc = Math.min(-999, bci-1);
int dec = enc;
try {
enc = encodeBCI(bci);
dec = decodeBCI(enc);
} catch (RuntimeException ee) {
ee.printStackTrace();
}
if (dec == bci) {
//System.out.println("BCI="+bci+(enc<len?"":" ")+" enc="+enc);
continue;
}
if (ok) {
for (int q = 0; q <= 1; q++) {
StringBuffer sb = new StringBuffer();
sb.append("bci "+(q==0?"map":"del")+"["+len+"] = {");
for (int j = 0; j < len; j++) {
int mapi = ((Number)java.lang.reflect.Array.get(insnMap, j)).intValue() - base;
mapi -= j*q;
sb.append(" "+mapi);
}
sb.append(" }");
System.out.println("*** "+sb);
}
}
System.out.println("*** BCI="+bci+" enc="+enc+" dec="+dec);
ok = false;
}
return ok;
}
//*/
}

View file

@ -1,906 +0,0 @@
/*
* Copyright (c) 2001, 2011, 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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Define the conversions between sequences of small integers and raw bytes.
* This is a schema of encodings which incorporates varying lengths,
* varying degrees of length variability, and varying amounts of signed-ness.
* @author John Rose
*/
class Coding implements Comparable<Coding>, CodingMethod, Histogram.BitMetric {
/*
Coding schema for single integers, parameterized by (B,H,S):
Let B in [1,5], H in [1,256], S in [0,3].
(S limit is arbitrary. B follows the 32-bit limit. H is byte size.)
A given (B,H,S) code varies in length from 1 to B bytes.
The 256 values a byte may take on are divided into L=(256-H) and H
values, with all the H values larger than the L values.
(That is, the L values are [0,L) and the H are [L,256).)
The last byte is always either the B-th byte, a byte with "L value"
(<L), or both. There is no other byte that satisfies these conditions.
All bytes before the last always have "H values" (>=L).
Therefore, if L==0, the code always has the full length of B bytes.
The coding then becomes a classic B-byte little-endian unsigned integer.
(Also, if L==128, the high bit of each byte acts signals the presence
of a following byte, up to the maximum length.)
In the unsigned case (S==0), the coding is compact and monotonic
in the ordering of byte sequences defined by appending zero bytes
to pad them to a common length B, reversing them, and ordering them
lexicographically. (This agrees with "little-endian" byte order.)
Therefore, the unsigned value of a byte sequence may be defined as:
<pre>
U(b0) == b0
in [0..L)
or [0..256) if B==1 (**)
U(b0,b1) == b0 + b1*H
in [L..L*(1+H))
or [L..L*(1+H) + H^2) if B==2
U(b0,b1,b2) == b0 + b1*H + b2*H^2
in [L*(1+H)..L*(1+H+H^2))
or [L*(1+H)..L*(1+H+H^2) + H^3) if B==3
U(b[i]: i<n) == Sum[i<n]( b[i] * H^i )
up to L*Sum[i<n]( H^i )
or to L*Sum[i<n]( H^i ) + H^n if n==B
</pre>
(**) If B==1, the values H,L play no role in the coding.
As a convention, we require that any (1,H,S) code must always
encode values less than H. Thus, a simple unsigned byte is coded
specifically by the code (1,256,0).
(Properly speaking, the unsigned case should be parameterized as
S==Infinity. If the schema were regular, the case S==0 would really
denote a numbering in which all coded values are negative.)
If S>0, the unsigned value of a byte sequence is regarded as a binary
integer. If any of the S low-order bits are zero, the corresponding
signed value will be non-negative. If all of the S low-order bits
(S>0) are one, the corresponding signed value will be negative.
The non-negative signed values are compact and monotonically increasing
(from 0) in the ordering of the corresponding unsigned values.
The negative signed values are compact and monotonically decreasing
(from -1) in the ordering of the corresponding unsigned values.
In essence, the low-order S bits function as a collective sign bit
for negative signed numbers, and as a low-order base-(2^S-1) digit
for non-negative signed numbers.
Therefore, the signed value corresponding to an unsigned value is:
<pre>
Sgn(x) == x if S==0
Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S), if S>0, (x % 2^S) < 2^S-1
Sgn(x) == -(x / 2^S)-1, if S>0, (x % 2^S) == 2^S-1
</pre>
Finally, the value of a byte sequence, given the coding parameters
(B,H,S), is defined as:
<pre>
V(b[i]: i<n) == Sgn(U(b[i]: i<n))
</pre>
The extremal positive and negative signed value for a given range
of unsigned values may be found by sign-encoding the largest unsigned
value which is not 2^S-1 mod 2^S, and that which is, respectively.
Because B,H,S are variable, this is not a single coding but a schema
of codings. For optimal compression, it is necessary to adaptively
select specific codings to the data being compressed.
For example, if a sequence of values happens never to be negative,
S==0 is the best choice. If the values are equally balanced between
negative and positive, S==1. If negative values are rare, then S>1
is more appropriate.
A (B,H,S) encoding is called a "subrange" if it does not encode
the largest 32-bit value, and if the number R of values it does
encode can be expressed as a positive 32-bit value. (Note that
B=1 implies R<=256, B=2 implies R<=65536, etc.)
A delta version of a given (B,H,S) coding encodes an array of integers
by writing their successive differences in the (B,H,S) coding.
The original integers themselves may be recovered by making a
running accumulation of sum of the differences as they are read.
As a special case, if a (B,H,S) encoding is a subrange, its delta
version will only encode arrays of numbers in the coding's unsigned
range, [0..R-1]. The coding of deltas is still in the normal signed
range, if S!=0. During delta encoding, all subtraction results are
reduced to the signed range, by adding multiples of R. Likewise,
. during encoding, all addition results are reduced to the unsigned range.
This special case for subranges allows the benefits of wraparound
when encoding correlated sequences of very small positive numbers.
*/
// Code-specific limits:
private static int saturate32(long x) {
if (x > Integer.MAX_VALUE) return Integer.MAX_VALUE;
if (x < Integer.MIN_VALUE) return Integer.MIN_VALUE;
return (int)x;
}
private static long codeRangeLong(int B, int H) {
return codeRangeLong(B, H, B);
}
private static long codeRangeLong(int B, int H, int nMax) {
// Code range for a all (B,H) codes of length <=nMax (<=B).
// n < B: L*Sum[i<n]( H^i )
// n == B: L*Sum[i<B]( H^i ) + H^B
assert(nMax >= 0 && nMax <= B);
assert(B >= 1 && B <= 5);
assert(H >= 1 && H <= 256);
if (nMax == 0) return 0; // no codes of zero length
if (B == 1) return H; // special case; see (**) above
int L = 256-H;
long sum = 0;
long H_i = 1;
for (int n = 1; n <= nMax; n++) {
sum += H_i;
H_i *= H;
}
sum *= L;
if (nMax == B)
sum += H_i;
return sum;
}
/** Largest int representable by (B,H,S) in up to nMax bytes. */
public static int codeMax(int B, int H, int S, int nMax) {
//assert(S >= 0 && S <= S_MAX);
long range = codeRangeLong(B, H, nMax);
if (range == 0)
return -1; // degenerate max value for empty set of codes
if (S == 0 || range >= (long)1<<32)
return saturate32(range-1);
long maxPos = range-1;
while (isNegativeCode(maxPos, S)) {
--maxPos;
}
if (maxPos < 0) return -1; // No positive codings at all.
int smax = decodeSign32(maxPos, S);
// check for 32-bit wraparound:
if (smax < 0)
return Integer.MAX_VALUE;
return smax;
}
/** Smallest int representable by (B,H,S) in up to nMax bytes.
Returns Integer.MIN_VALUE if 32-bit wraparound covers
the entire negative range.
*/
public static int codeMin(int B, int H, int S, int nMax) {
//assert(S >= 0 && S <= S_MAX);
long range = codeRangeLong(B, H, nMax);
if (range >= (long)1<<32 && nMax == B) {
// Can code negative values via 32-bit wraparound.
return Integer.MIN_VALUE;
}
if (S == 0) {
return 0;
}
long maxNeg = range-1;
while (!isNegativeCode(maxNeg, S))
--maxNeg;
if (maxNeg < 0) return 0; // No negative codings at all.
return decodeSign32(maxNeg, S);
}
// Some of the arithmetic below is on unsigned 32-bit integers.
// These must be represented in Java as longs in the range [0..2^32-1].
// The conversion to a signed int is just the Java cast (int), but
// the conversion to an unsigned int is the following little method:
private static long toUnsigned32(int sx) {
return ((long)sx << 32) >>> 32;
}
// Sign encoding:
private static boolean isNegativeCode(long ux, int S) {
assert(S > 0);
assert(ux >= -1); // can be out of 32-bit range; who cares
int Smask = (1<<S)-1;
return (((int)ux+1) & Smask) == 0;
}
private static boolean hasNegativeCode(int sx, int S) {
assert(S > 0);
// If S>=2 very low negatives are coded by 32-bit-wrapped positives.
// The lowest negative representable by a negative coding is
// ~(umax32 >> S), and the next lower number is coded by wrapping
// the highest positive:
// CodePos(umax32-1) -> (umax32-1)-((umax32-1)>>S)
// which simplifies to ~(umax32 >> S)-1.
return (0 > sx) && (sx >= ~(-1>>>S));
}
private static int decodeSign32(long ux, int S) {
assert(ux == toUnsigned32((int)ux)) // must be unsigned 32-bit number
: (Long.toHexString(ux));
if (S == 0) {
return (int) ux; // cast to signed int
}
int sx;
if (isNegativeCode(ux, S)) {
// Sgn(x) == -(x / 2^S)-1
sx = ~((int)ux >>> S);
} else {
// Sgn(x) == (x / 2^S)*(2^S-1) + (x % 2^S)
sx = (int)ux - ((int)ux >>> S);
}
// Assert special case of S==1:
assert(!(S == 1) || sx == (((int)ux >>> 1) ^ -((int)ux & 1)));
return sx;
}
private static long encodeSign32(int sx, int S) {
if (S == 0) {
return toUnsigned32(sx); // unsigned 32-bit int
}
int Smask = (1<<S)-1;
long ux;
if (!hasNegativeCode(sx, S)) {
// InvSgn(sx) = (sx / (2^S-1))*2^S + (sx % (2^S-1))
ux = sx + (toUnsigned32(sx) / Smask);
} else {
// InvSgn(sx) = (-sx-1)*2^S + (2^S-1)
ux = (-sx << S) - 1;
}
ux = toUnsigned32((int)ux);
assert(sx == decodeSign32(ux, S))
: (Long.toHexString(ux)+" -> "+
Integer.toHexString(sx)+" != "+
Integer.toHexString(decodeSign32(ux, S)));
return ux;
}
// Top-level coding of single integers:
public static void writeInt(byte[] out, int[] outpos, int sx, int B, int H, int S) {
long ux = encodeSign32(sx, S);
assert(ux == toUnsigned32((int)ux));
assert(ux < codeRangeLong(B, H))
: Long.toHexString(ux);
int L = 256-H;
long sum = ux;
int pos = outpos[0];
for (int i = 0; i < B-1; i++) {
if (sum < L)
break;
sum -= L;
int b_i = (int)( L + (sum % H) );
sum /= H;
out[pos++] = (byte)b_i;
}
out[pos++] = (byte)sum;
// Report number of bytes written by updating outpos[0]:
outpos[0] = pos;
// Check right away for mis-coding.
//assert(sx == readInt(out, new int[1], B, H, S));
}
public static int readInt(byte[] in, int[] inpos, int B, int H, int S) {
// U(b[i]: i<n) == Sum[i<n]( b[i] * H^i )
int L = 256-H;
long sum = 0;
long H_i = 1;
int pos = inpos[0];
for (int i = 0; i < B; i++) {
int b_i = in[pos++] & 0xFF;
sum += b_i*H_i;
H_i *= H;
if (b_i < L) break;
}
//assert(sum >= 0 && sum < codeRangeLong(B, H));
// Report number of bytes read by updating inpos[0]:
inpos[0] = pos;
return decodeSign32(sum, S);
}
// The Stream version doesn't fetch a byte unless it is needed for coding.
public static int readIntFrom(InputStream in, int B, int H, int S) throws IOException {
// U(b[i]: i<n) == Sum[i<n]( b[i] * H^i )
int L = 256-H;
long sum = 0;
long H_i = 1;
for (int i = 0; i < B; i++) {
int b_i = in.read();
if (b_i < 0) throw new RuntimeException("unexpected EOF");
sum += b_i*H_i;
H_i *= H;
if (b_i < L) break;
}
assert(sum >= 0 && sum < codeRangeLong(B, H));
return decodeSign32(sum, S);
}
public static final int B_MAX = 5; /* B: [1,5] */
public static final int H_MAX = 256; /* H: [1,256] */
public static final int S_MAX = 2; /* S: [0,2] */
// END OF STATICS.
private final int B; /*1..5*/ // # bytes (1..5)
private final int H; /*1..256*/ // # codes requiring a higher byte
private final int L; /*0..255*/ // # codes requiring a higher byte
private final int S; /*0..3*/ // # low-order bits representing sign
private final int del; /*0..2*/ // type of delta encoding (0 == none)
private final int min; // smallest representable value
private final int max; // largest representable value
private final int umin; // smallest representable uns. value
private final int umax; // largest representable uns. value
private final int[] byteMin; // smallest repr. value, given # bytes
private final int[] byteMax; // largest repr. value, given # bytes
private Coding(int B, int H, int S) {
this(B, H, S, 0);
}
private Coding(int B, int H, int S, int del) {
this.B = B;
this.H = H;
this.L = 256-H;
this.S = S;
this.del = del;
this.min = codeMin(B, H, S, B);
this.max = codeMax(B, H, S, B);
this.umin = codeMin(B, H, 0, B);
this.umax = codeMax(B, H, 0, B);
this.byteMin = new int[B];
this.byteMax = new int[B];
for (int nMax = 1; nMax <= B; nMax++) {
byteMin[nMax-1] = codeMin(B, H, S, nMax);
byteMax[nMax-1] = codeMax(B, H, S, nMax);
}
}
public boolean equals(Object x) {
if (!(x instanceof Coding)) return false;
Coding that = (Coding) x;
if (this.B != that.B) return false;
if (this.H != that.H) return false;
if (this.S != that.S) return false;
if (this.del != that.del) return false;
return true;
}
public int hashCode() {
return (del<<14)+(S<<11)+(B<<8)+(H<<0);
}
private static Map<Coding, Coding> codeMap;
private static synchronized Coding of(int B, int H, int S, int del) {
if (codeMap == null) codeMap = new HashMap<>();
Coding x0 = new Coding(B, H, S, del);
Coding x1 = codeMap.get(x0);
if (x1 == null) codeMap.put(x0, x1 = x0);
return x1;
}
public static Coding of(int B, int H) {
return of(B, H, 0, 0);
}
public static Coding of(int B, int H, int S) {
return of(B, H, S, 0);
}
public boolean canRepresentValue(int x) {
if (isSubrange())
return canRepresentUnsigned(x);
else
return canRepresentSigned(x);
}
/** Can this coding represent a single value, possibly a delta?
* This ignores the D property. That is, for delta codings,
* this tests whether a delta value of 'x' can be coded.
* For signed delta codings which produce unsigned end values,
* use canRepresentUnsigned.
*/
public boolean canRepresentSigned(int x) {
return (x >= min && x <= max);
}
/** Can this coding, apart from its S property,
* represent a single value? (Negative values
* can only be represented via 32-bit overflow,
* so this returns true for negative values
* if isFullRange is true.)
*/
public boolean canRepresentUnsigned(int x) {
return (x >= umin && x <= umax);
}
// object-oriented code/decode
public int readFrom(byte[] in, int[] inpos) {
return readInt(in, inpos, B, H, S);
}
public void writeTo(byte[] out, int[] outpos, int x) {
writeInt(out, outpos, x, B, H, S);
}
// Stream versions
public int readFrom(InputStream in) throws IOException {
return readIntFrom(in, B, H, S);
}
public void writeTo(OutputStream out, int x) throws IOException {
byte[] buf = new byte[B];
int[] pos = new int[1];
writeInt(buf, pos, x, B, H, S);
out.write(buf, 0, pos[0]);
}
// Stream/array versions
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
// %%% use byte[] buffer
for (int i = start; i < end; i++)
a[i] = readFrom(in);
for (int dstep = 0; dstep < del; dstep++) {
long state = 0;
for (int i = start; i < end; i++) {
state += a[i];
// Reduce array values to the required range.
if (isSubrange()) {
state = reduceToUnsignedRange(state);
}
a[i] = (int) state;
}
}
}
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
if (end <= start) return;
for (int dstep = 0; dstep < del; dstep++) {
int[] deltas;
if (!isSubrange())
deltas = makeDeltas(a, start, end, 0, 0);
else
deltas = makeDeltas(a, start, end, min, max);
a = deltas;
start = 0;
end = deltas.length;
}
// The following code is a buffered version of this loop:
// for (int i = start; i < end; i++)
// writeTo(out, a[i]);
byte[] buf = new byte[1<<8];
final int bufmax = buf.length-B;
int[] pos = { 0 };
for (int i = start; i < end; ) {
while (pos[0] <= bufmax) {
writeTo(buf, pos, a[i++]);
if (i >= end) break;
}
out.write(buf, 0, pos[0]);
pos[0] = 0;
}
}
/** Tell if the range of this coding (number of distinct
* representable values) can be expressed in 32 bits.
*/
boolean isSubrange() {
return max < Integer.MAX_VALUE
&& ((long)max - (long)min + 1) <= Integer.MAX_VALUE;
}
/** Tell if this coding can represent all 32-bit values.
* Note: Some codings, such as unsigned ones, can be neither
* subranges nor full-range codings.
*/
boolean isFullRange() {
return max == Integer.MAX_VALUE && min == Integer.MIN_VALUE;
}
/** Return the number of values this coding (a subrange) can represent. */
int getRange() {
assert(isSubrange());
return (max - min) + 1; // range includes both min & max
}
Coding setB(int B) { return Coding.of(B, H, S, del); }
Coding setH(int H) { return Coding.of(B, H, S, del); }
Coding setS(int S) { return Coding.of(B, H, S, del); }
Coding setL(int L) { return setH(256-L); }
Coding setD(int del) { return Coding.of(B, H, S, del); }
Coding getDeltaCoding() { return setD(del+1); }
/** Return a coding suitable for representing summed, modulo-reduced values. */
Coding getValueCoding() {
if (isDelta())
return Coding.of(B, H, 0, del-1);
else
return this;
}
/** Reduce the given value to be within this coding's unsigned range,
* by adding or subtracting a multiple of (max-min+1).
*/
int reduceToUnsignedRange(long value) {
if (value == (int)value && canRepresentUnsigned((int)value))
// already in unsigned range
return (int)value;
int range = getRange();
assert(range > 0);
value %= range;
if (value < 0) value += range;
assert(canRepresentUnsigned((int)value));
return (int)value;
}
int reduceToSignedRange(int value) {
if (canRepresentSigned(value))
// already in signed range
return value;
return reduceToSignedRange(value, min, max);
}
static int reduceToSignedRange(int value, int min, int max) {
int range = (max-min+1);
assert(range > 0);
int value0 = value;
value -= min;
if (value < 0 && value0 >= 0) {
// 32-bit overflow, but the next '%=' op needs to be unsigned
value -= range;
assert(value >= 0);
}
value %= range;
if (value < 0) value += range;
value += min;
assert(min <= value && value <= max);
return value;
}
/** Does this coding support at least one negative value?
Includes codings that can do so via 32-bit wraparound.
*/
boolean isSigned() {
return min < 0;
}
/** Does this coding code arrays by making successive differences? */
boolean isDelta() {
return del != 0;
}
public int B() { return B; }
public int H() { return H; }
public int L() { return L; }
public int S() { return S; }
public int del() { return del; }
public int min() { return min; }
public int max() { return max; }
public int umin() { return umin; }
public int umax() { return umax; }
public int byteMin(int b) { return byteMin[b-1]; }
public int byteMax(int b) { return byteMax[b-1]; }
public int compareTo(Coding that) {
int dkey = this.del - that.del;
if (dkey == 0)
dkey = this.B - that.B;
if (dkey == 0)
dkey = this.H - that.H;
if (dkey == 0)
dkey = this.S - that.S;
return dkey;
}
/** Heuristic measure of the difference between two codings. */
public int distanceFrom(Coding that) {
int diffdel = this.del - that.del;
if (diffdel < 0) diffdel = -diffdel;
int diffS = this.S - that.S;
if (diffS < 0) diffS = -diffS;
int diffB = this.B - that.B;
if (diffB < 0) diffB = -diffB;
int diffHL;
if (this.H == that.H) {
diffHL = 0;
} else {
// Distance in log space of H (<=128) and L (<128).
int thisHL = this.getHL();
int thatHL = that.getHL();
// Double the accuracy of the log:
thisHL *= thisHL;
thatHL *= thatHL;
if (thisHL > thatHL)
diffHL = ceil_lg2(1+(thisHL-1)/thatHL);
else
diffHL = ceil_lg2(1+(thatHL-1)/thisHL);
}
int norm = 5*(diffdel + diffS + diffB) + diffHL;
assert(norm != 0 || this.compareTo(that) == 0);
return norm;
}
private int getHL() {
// Follow H in log space by the multiplicative inverse of L.
if (H <= 128) return H;
if (L >= 1) return 128*128/L;
return 128*256;
}
/** ceiling(log[2](x)): {1->0, 2->1, 3->2, 4->2, ...} */
static int ceil_lg2(int x) {
assert(x-1 >= 0); // x in range (int.MIN_VALUE -> 32)
x -= 1;
int lg = 0;
while (x != 0) {
lg++;
x >>= 1;
}
return lg;
}
private static final byte[] byteBitWidths = new byte[0x100];
static {
for (int b = 0; b < byteBitWidths.length; b++) {
byteBitWidths[b] = (byte) ceil_lg2(b + 1);
}
for (int i = 10; i >= 0; i = (i << 1) - (i >> 3)) {
assert(bitWidth(i) == ceil_lg2(i + 1));
}
}
/** Number of significant bits in i, not counting sign bits.
* For positive i, it is ceil_lg2(i + 1).
*/
static int bitWidth(int i) {
if (i < 0) i = ~i; // change sign
int w = 0;
int lo = i;
if (lo < byteBitWidths.length)
return byteBitWidths[lo];
int hi;
hi = (lo >>> 16);
if (hi != 0) {
lo = hi;
w += 16;
}
hi = (lo >>> 8);
if (hi != 0) {
lo = hi;
w += 8;
}
w += byteBitWidths[lo];
//assert(w == ceil_lg2(i + 1));
return w;
}
/** Create an array of successive differences.
* If min==max, accept any and all 32-bit overflow.
* Otherwise, avoid 32-bit overflow, and reduce all differences
* to a value in the given range, by adding or subtracting
* multiples of the range cardinality (max-min+1).
* Also, the values are assumed to be in the range [0..(max-min)].
*/
static int[] makeDeltas(int[] values, int start, int end,
int min, int max) {
assert(max >= min);
int count = end-start;
int[] deltas = new int[count];
int state = 0;
if (min == max) {
for (int i = 0; i < count; i++) {
int value = values[start+i];
deltas[i] = value - state;
state = value;
}
} else {
for (int i = 0; i < count; i++) {
int value = values[start+i];
assert(value >= 0 && value+min <= max);
int delta = value - state;
assert(delta == (long)value - (long)state); // no overflow
state = value;
// Reduce delta values to the required range.
delta = reduceToSignedRange(delta, min, max);
deltas[i] = delta;
}
}
return deltas;
}
boolean canRepresent(int minValue, int maxValue) {
assert(minValue <= maxValue);
if (del > 0) {
if (isSubrange()) {
// We will force the values to reduce to the right subrange.
return canRepresentUnsigned(maxValue)
&& canRepresentUnsigned(minValue);
} else {
// Huge range; delta values must assume full 32-bit range.
return isFullRange();
}
}
else
// final values must be representable
return canRepresentSigned(maxValue)
&& canRepresentSigned(minValue);
}
boolean canRepresent(int[] values, int start, int end) {
int len = end-start;
if (len == 0) return true;
if (isFullRange()) return true;
// Calculate max, min:
int lmax = values[start];
int lmin = lmax;
for (int i = 1; i < len; i++) {
int value = values[start+i];
if (lmax < value) lmax = value;
if (lmin > value) lmin = value;
}
return canRepresent(lmin, lmax);
}
public double getBitLength(int value) { // implements BitMetric
return (double) getLength(value) * 8;
}
/** How many bytes are in the coding of this value?
* Returns Integer.MAX_VALUE if the value has no coding.
* The coding must not be a delta coding, since there is no
* definite size for a single value apart from its context.
*/
public int getLength(int value) {
if (isDelta() && isSubrange()) {
if (!canRepresentUnsigned(value))
return Integer.MAX_VALUE;
value = reduceToSignedRange(value);
}
if (value >= 0) {
for (int n = 0; n < B; n++) {
if (value <= byteMax[n]) return n+1;
}
} else {
for (int n = 0; n < B; n++) {
if (value >= byteMin[n]) return n+1;
}
}
return Integer.MAX_VALUE;
}
public int getLength(int[] values, int start, int end) {
int len = end-start;
if (B == 1) return len;
if (L == 0) return len * B;
if (isDelta()) {
int[] deltas;
if (!isSubrange())
deltas = makeDeltas(values, start, end, 0, 0);
else
deltas = makeDeltas(values, start, end, min, max);
//return Coding.of(B, H, S).getLength(deltas, 0, len);
values = deltas;
start = 0;
}
int sum = len; // at least 1 byte per
// add extra bytes for extra-long values
for (int n = 1; n <= B; n++) {
// what is the coding interval [min..max] for n bytes?
int lmax = byteMax[n-1];
int lmin = byteMin[n-1];
int longer = 0; // count of guys longer than n bytes
for (int i = 0; i < len; i++) {
int value = values[start+i];
if (value >= 0) {
if (value > lmax) longer++;
} else {
if (value < lmin) longer++;
}
}
if (longer == 0) break; // no more passes needed
if (n == B) return Integer.MAX_VALUE; // cannot represent!
sum += longer;
}
return sum;
}
public byte[] getMetaCoding(Coding dflt) {
if (dflt == this) return new byte[]{ (byte) _meta_default };
int canonicalIndex = BandStructure.indexOf(this);
if (canonicalIndex > 0)
return new byte[]{ (byte) canonicalIndex };
return new byte[]{
(byte)_meta_arb,
(byte)(del + 2*S + 8*(B-1)),
(byte)(H-1)
};
}
public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
int op = bytes[pos++] & 0xFF;
if (_meta_canon_min <= op && op <= _meta_canon_max) {
Coding c = BandStructure.codingForIndex(op);
assert(c != null);
res[0] = c;
return pos;
}
if (op == _meta_arb) {
int dsb = bytes[pos++] & 0xFF;
int H_1 = bytes[pos++] & 0xFF;
int del = dsb % 2;
int S = (dsb / 2) % 4;
int B = (dsb / 8)+1;
int H = H_1+1;
if (!((1 <= B && B <= B_MAX) &&
(0 <= S && S <= S_MAX) &&
(1 <= H && H <= H_MAX) &&
(0 <= del && del <= 1))
|| (B == 1 && H != 256)
|| (B == 5 && H == 256)) {
throw new RuntimeException("Bad arb. coding: ("+B+","+H+","+S+","+del);
}
res[0] = Coding.of(B, H, S, del);
return pos;
}
return pos-1; // backup
}
public String keyString() {
return "("+B+","+H+","+S+","+del+")";
}
public String toString() {
String str = "Coding"+keyString();
// If -ea, print out more informative strings!
//assert((str = stringForDebug()) != null);
return str;
}
static boolean verboseStringForDebug = false;
String stringForDebug() {
String minS = (min == Integer.MIN_VALUE ? "min" : ""+min);
String maxS = (max == Integer.MAX_VALUE ? "max" : ""+max);
String str = keyString()+" L="+L+" r=["+minS+","+maxS+"]";
if (isSubrange())
str += " subrange";
else if (!isFullRange())
str += " MIDRANGE";
if (verboseStringForDebug) {
str += " {";
int prev_range = 0;
for (int n = 1; n <= B; n++) {
int range_n = saturate32((long)byteMax[n-1] - byteMin[n-1] + 1);
assert(range_n == saturate32(codeRangeLong(B, H, n)));
range_n -= prev_range;
prev_range = range_n;
String rngS = (range_n == Integer.MAX_VALUE ? "max" : ""+range_n);
str += " #"+n+"="+rngS;
}
str += " }";
}
return str;
}
}

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.util.jar.pack;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Interface for encoding and decoding int arrays using bytewise codes.
* @author John Rose
*/
interface CodingMethod {
// Read and write an array of ints from/to a stream.
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException;
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException;
// how to express me in a band header?
public byte[] getMetaCoding(Coding dflt);
}

View file

@ -1,515 +0,0 @@
/*
* Copyright (c) 2001, 2019, 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 java.util.Arrays;
import java.util.List;
/**
* Shared constants
* @author John Rose
*/
class Constants {
private Constants(){}
public static final int JAVA_MAGIC = 0xCAFEBABE;
/*
Java Class Version numbers history
1.0 to 1.3.X 45,3
1.4 to 1.4.X 46,0
1.5 to 1.5.X 49,0
1.6 to 1.6.X 50,0
1.7 to 1.7.X 51,0
1.8 to 1.8.X 52,0
1.9 to 1.9.X 53,0
1.10 to 1.10.X 54,0
1.11 to 1.11.X 55,0
1.12 to 1.12.X 56,0
1.13 to 1.13.X 57,0
1.14 to 1.14.X 58,0
*/
public static final Package.Version JAVA_MIN_CLASS_VERSION =
Package.Version.of(45, 03);
public static final Package.Version JAVA5_MAX_CLASS_VERSION =
Package.Version.of(49, 00);
public static final Package.Version JAVA6_MAX_CLASS_VERSION =
Package.Version.of(50, 00);
public static final Package.Version JAVA7_MAX_CLASS_VERSION =
Package.Version.of(51, 00);
public static final Package.Version JAVA8_MAX_CLASS_VERSION =
Package.Version.of(52, 00);
public static final Package.Version JAVA9_MAX_CLASS_VERSION =
Package.Version.of(53, 00);
public static final Package.Version JAVA10_MAX_CLASS_VERSION =
Package.Version.of(54, 00);
public static final Package.Version JAVA11_MAX_CLASS_VERSION =
Package.Version.of(55, 00);
public static final Package.Version JAVA12_MAX_CLASS_VERSION =
Package.Version.of(56, 00);
public static final Package.Version JAVA13_MAX_CLASS_VERSION =
Package.Version.of(57, 00);
public static final Package.Version JAVA14_MAX_CLASS_VERSION =
Package.Version.of(58, 00);
public static final int JAVA_PACKAGE_MAGIC = 0xCAFED00D;
public static final Package.Version JAVA5_PACKAGE_VERSION =
Package.Version.of(150, 7);
public static final Package.Version JAVA6_PACKAGE_VERSION =
Package.Version.of(160, 1);
public static final Package.Version JAVA7_PACKAGE_VERSION =
Package.Version.of(170, 1);
public static final Package.Version JAVA8_PACKAGE_VERSION =
Package.Version.of(171, 0);
// upper limit, should point to the latest class version
public static final Package.Version JAVA_MAX_CLASS_VERSION =
JAVA13_MAX_CLASS_VERSION;
// upper limit should point to the latest package version, for version info!.
public static final Package.Version MAX_PACKAGE_VERSION =
JAVA7_PACKAGE_VERSION;
public static final int CONSTANT_POOL_INDEX_LIMIT = 0x10000;
public static final int CONSTANT_POOL_NARROW_LIMIT = 0x00100;
public static final String JAVA_SIGNATURE_CHARS = "BSCIJFDZLV([";
public static final byte CONSTANT_Utf8 = 1;
public static final byte CONSTANT_unused2 = 2; // unused, was Unicode
public static final byte CONSTANT_Integer = 3;
public static final byte CONSTANT_Float = 4;
public static final byte CONSTANT_Long = 5;
public static final byte CONSTANT_Double = 6;
public static final byte CONSTANT_Class = 7;
public static final byte CONSTANT_String = 8;
public static final byte CONSTANT_Fieldref = 9;
public static final byte CONSTANT_Methodref = 10;
public static final byte CONSTANT_InterfaceMethodref = 11;
public static final byte CONSTANT_NameandType = 12;
public static final byte CONSTANT_unused13 = 13;
public static final byte CONSTANT_unused14 = 14;
public static final byte CONSTANT_MethodHandle = 15;
public static final byte CONSTANT_MethodType = 16;
public static final byte CONSTANT_unused17 = 17; // unused
public static final byte CONSTANT_InvokeDynamic = 18;
// pseudo-constants:
public static final byte CONSTANT_None = 0;
public static final byte CONSTANT_Signature = CONSTANT_unused13;
public static final byte CONSTANT_BootstrapMethod = CONSTANT_unused17; // used only in InvokeDynamic constants
public static final byte CONSTANT_Limit = 19;
public static final byte CONSTANT_All = 50; // combined global map
public static final byte CONSTANT_LoadableValue = 51; // used for 'KL' and qldc operands
public static final byte CONSTANT_AnyMember = 52; // union of refs to field or (interface) method
public static final byte CONSTANT_FieldSpecific = 53; // used only for 'KQ' ConstantValue attrs
public static final byte CONSTANT_GroupFirst = CONSTANT_All;
public static final byte CONSTANT_GroupLimit = CONSTANT_FieldSpecific+1;
// CONSTANT_MethodHandle reference kinds
public static final byte REF_getField = 1;
public static final byte REF_getStatic = 2;
public static final byte REF_putField = 3;
public static final byte REF_putStatic = 4;
public static final byte REF_invokeVirtual = 5;
public static final byte REF_invokeStatic = 6;
public static final byte REF_invokeSpecial = 7;
public static final byte REF_newInvokeSpecial = 8;
public static final byte REF_invokeInterface = 9;
// pseudo-access bits
public static final int ACC_IC_LONG_FORM = (1<<16); //for ic_flags
// attribute "context types"
public static final int ATTR_CONTEXT_CLASS = 0;
public static final int ATTR_CONTEXT_FIELD = 1;
public static final int ATTR_CONTEXT_METHOD = 2;
public static final int ATTR_CONTEXT_CODE = 3;
public static final int ATTR_CONTEXT_LIMIT = 4;
public static final String[] ATTR_CONTEXT_NAME
= { "class", "field", "method", "code" };
// predefined attr bits
public static final int
X_ATTR_OVERFLOW = 16,
CLASS_ATTR_SourceFile = 17,
METHOD_ATTR_Code = 17,
FIELD_ATTR_ConstantValue = 17,
CLASS_ATTR_EnclosingMethod = 18,
METHOD_ATTR_Exceptions = 18,
X_ATTR_Signature = 19,
X_ATTR_Deprecated = 20,
X_ATTR_RuntimeVisibleAnnotations = 21,
X_ATTR_RuntimeInvisibleAnnotations = 22,
METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23,
CLASS_ATTR_InnerClasses = 23,
METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24,
CLASS_ATTR_ClassFile_version = 24,
METHOD_ATTR_AnnotationDefault = 25,
METHOD_ATTR_MethodParameters = 26, // JDK8
X_ATTR_RuntimeVisibleTypeAnnotations = 27, // JDK8
X_ATTR_RuntimeInvisibleTypeAnnotations = 28, // JDK8
CODE_ATTR_StackMapTable = 0, // new in Java 6
CODE_ATTR_LineNumberTable = 1,
CODE_ATTR_LocalVariableTable = 2,
CODE_ATTR_LocalVariableTypeTable = 3;
// File option bits, from LSB in ascending bit position.
public static final int FO_DEFLATE_HINT = 1<<0;
public static final int FO_IS_CLASS_STUB = 1<<1;
// Archive option bits, from LSB in ascending bit position:
public static final int AO_HAVE_SPECIAL_FORMATS = 1<<0;
public static final int AO_HAVE_CP_NUMBERS = 1<<1;
public static final int AO_HAVE_ALL_CODE_FLAGS = 1<<2;
public static final int AO_HAVE_CP_EXTRAS = 1<<3;
public static final int AO_HAVE_FILE_HEADERS = 1<<4;
public static final int AO_DEFLATE_HINT = 1<<5;
public static final int AO_HAVE_FILE_MODTIME = 1<<6;
public static final int AO_HAVE_FILE_OPTIONS = 1<<7;
public static final int AO_HAVE_FILE_SIZE_HI = 1<<8;
public static final int AO_HAVE_CLASS_FLAGS_HI = 1<<9;
public static final int AO_HAVE_FIELD_FLAGS_HI = 1<<10;
public static final int AO_HAVE_METHOD_FLAGS_HI = 1<<11;
public static final int AO_HAVE_CODE_FLAGS_HI = 1<<12;
public static final int AO_UNUSED_MBZ = (-1)<<13; // option bits reserved for future use
public static final int LG_AO_HAVE_XXX_FLAGS_HI = 9;
// visitRefs modes:
static final int VRM_CLASSIC = 0;
static final int VRM_PACKAGE = 1;
public static final int NO_MODTIME = 0; // null modtime value
// some comstantly empty containers
public static final int[] noInts = {};
public static final byte[] noBytes = {};
public static final Object[] noValues = {};
public static final String[] noStrings = {};
public static final List<Object> emptyList = Arrays.asList(noValues);
// meta-coding
public static final int
_meta_default = 0,
_meta_canon_min = 1,
_meta_canon_max = 115,
_meta_arb = 116,
_meta_run = 117,
_meta_pop = 141,
_meta_limit = 189;
// bytecodes
public static final int
_nop = 0, // 0x00
_aconst_null = 1, // 0x01
_iconst_m1 = 2, // 0x02
_iconst_0 = 3, // 0x03
_iconst_1 = 4, // 0x04
_iconst_2 = 5, // 0x05
_iconst_3 = 6, // 0x06
_iconst_4 = 7, // 0x07
_iconst_5 = 8, // 0x08
_lconst_0 = 9, // 0x09
_lconst_1 = 10, // 0x0a
_fconst_0 = 11, // 0x0b
_fconst_1 = 12, // 0x0c
_fconst_2 = 13, // 0x0d
_dconst_0 = 14, // 0x0e
_dconst_1 = 15, // 0x0f
_bipush = 16, // 0x10
_sipush = 17, // 0x11
_ldc = 18, // 0x12
_ldc_w = 19, // 0x13
_ldc2_w = 20, // 0x14
_iload = 21, // 0x15
_lload = 22, // 0x16
_fload = 23, // 0x17
_dload = 24, // 0x18
_aload = 25, // 0x19
_iload_0 = 26, // 0x1a
_iload_1 = 27, // 0x1b
_iload_2 = 28, // 0x1c
_iload_3 = 29, // 0x1d
_lload_0 = 30, // 0x1e
_lload_1 = 31, // 0x1f
_lload_2 = 32, // 0x20
_lload_3 = 33, // 0x21
_fload_0 = 34, // 0x22
_fload_1 = 35, // 0x23
_fload_2 = 36, // 0x24
_fload_3 = 37, // 0x25
_dload_0 = 38, // 0x26
_dload_1 = 39, // 0x27
_dload_2 = 40, // 0x28
_dload_3 = 41, // 0x29
_aload_0 = 42, // 0x2a
_aload_1 = 43, // 0x2b
_aload_2 = 44, // 0x2c
_aload_3 = 45, // 0x2d
_iaload = 46, // 0x2e
_laload = 47, // 0x2f
_faload = 48, // 0x30
_daload = 49, // 0x31
_aaload = 50, // 0x32
_baload = 51, // 0x33
_caload = 52, // 0x34
_saload = 53, // 0x35
_istore = 54, // 0x36
_lstore = 55, // 0x37
_fstore = 56, // 0x38
_dstore = 57, // 0x39
_astore = 58, // 0x3a
_istore_0 = 59, // 0x3b
_istore_1 = 60, // 0x3c
_istore_2 = 61, // 0x3d
_istore_3 = 62, // 0x3e
_lstore_0 = 63, // 0x3f
_lstore_1 = 64, // 0x40
_lstore_2 = 65, // 0x41
_lstore_3 = 66, // 0x42
_fstore_0 = 67, // 0x43
_fstore_1 = 68, // 0x44
_fstore_2 = 69, // 0x45
_fstore_3 = 70, // 0x46
_dstore_0 = 71, // 0x47
_dstore_1 = 72, // 0x48
_dstore_2 = 73, // 0x49
_dstore_3 = 74, // 0x4a
_astore_0 = 75, // 0x4b
_astore_1 = 76, // 0x4c
_astore_2 = 77, // 0x4d
_astore_3 = 78, // 0x4e
_iastore = 79, // 0x4f
_lastore = 80, // 0x50
_fastore = 81, // 0x51
_dastore = 82, // 0x52
_aastore = 83, // 0x53
_bastore = 84, // 0x54
_castore = 85, // 0x55
_sastore = 86, // 0x56
_pop = 87, // 0x57
_pop2 = 88, // 0x58
_dup = 89, // 0x59
_dup_x1 = 90, // 0x5a
_dup_x2 = 91, // 0x5b
_dup2 = 92, // 0x5c
_dup2_x1 = 93, // 0x5d
_dup2_x2 = 94, // 0x5e
_swap = 95, // 0x5f
_iadd = 96, // 0x60
_ladd = 97, // 0x61
_fadd = 98, // 0x62
_dadd = 99, // 0x63
_isub = 100, // 0x64
_lsub = 101, // 0x65
_fsub = 102, // 0x66
_dsub = 103, // 0x67
_imul = 104, // 0x68
_lmul = 105, // 0x69
_fmul = 106, // 0x6a
_dmul = 107, // 0x6b
_idiv = 108, // 0x6c
_ldiv = 109, // 0x6d
_fdiv = 110, // 0x6e
_ddiv = 111, // 0x6f
_irem = 112, // 0x70
_lrem = 113, // 0x71
_frem = 114, // 0x72
_drem = 115, // 0x73
_ineg = 116, // 0x74
_lneg = 117, // 0x75
_fneg = 118, // 0x76
_dneg = 119, // 0x77
_ishl = 120, // 0x78
_lshl = 121, // 0x79
_ishr = 122, // 0x7a
_lshr = 123, // 0x7b
_iushr = 124, // 0x7c
_lushr = 125, // 0x7d
_iand = 126, // 0x7e
_land = 127, // 0x7f
_ior = 128, // 0x80
_lor = 129, // 0x81
_ixor = 130, // 0x82
_lxor = 131, // 0x83
_iinc = 132, // 0x84
_i2l = 133, // 0x85
_i2f = 134, // 0x86
_i2d = 135, // 0x87
_l2i = 136, // 0x88
_l2f = 137, // 0x89
_l2d = 138, // 0x8a
_f2i = 139, // 0x8b
_f2l = 140, // 0x8c
_f2d = 141, // 0x8d
_d2i = 142, // 0x8e
_d2l = 143, // 0x8f
_d2f = 144, // 0x90
_i2b = 145, // 0x91
_i2c = 146, // 0x92
_i2s = 147, // 0x93
_lcmp = 148, // 0x94
_fcmpl = 149, // 0x95
_fcmpg = 150, // 0x96
_dcmpl = 151, // 0x97
_dcmpg = 152, // 0x98
_ifeq = 153, // 0x99
_ifne = 154, // 0x9a
_iflt = 155, // 0x9b
_ifge = 156, // 0x9c
_ifgt = 157, // 0x9d
_ifle = 158, // 0x9e
_if_icmpeq = 159, // 0x9f
_if_icmpne = 160, // 0xa0
_if_icmplt = 161, // 0xa1
_if_icmpge = 162, // 0xa2
_if_icmpgt = 163, // 0xa3
_if_icmple = 164, // 0xa4
_if_acmpeq = 165, // 0xa5
_if_acmpne = 166, // 0xa6
_goto = 167, // 0xa7
_jsr = 168, // 0xa8
_ret = 169, // 0xa9
_tableswitch = 170, // 0xaa
_lookupswitch = 171, // 0xab
_ireturn = 172, // 0xac
_lreturn = 173, // 0xad
_freturn = 174, // 0xae
_dreturn = 175, // 0xaf
_areturn = 176, // 0xb0
_return = 177, // 0xb1
_getstatic = 178, // 0xb2
_putstatic = 179, // 0xb3
_getfield = 180, // 0xb4
_putfield = 181, // 0xb5
_invokevirtual = 182, // 0xb6
_invokespecial = 183, // 0xb7
_invokestatic = 184, // 0xb8
_invokeinterface = 185, // 0xb9
_invokedynamic = 186, // 0xba
_new = 187, // 0xbb
_newarray = 188, // 0xbc
_anewarray = 189, // 0xbd
_arraylength = 190, // 0xbe
_athrow = 191, // 0xbf
_checkcast = 192, // 0xc0
_instanceof = 193, // 0xc1
_monitorenter = 194, // 0xc2
_monitorexit = 195, // 0xc3
_wide = 196, // 0xc4
_multianewarray = 197, // 0xc5
_ifnull = 198, // 0xc6
_ifnonnull = 199, // 0xc7
_goto_w = 200, // 0xc8
_jsr_w = 201, // 0xc9
_bytecode_limit = 202; // 0xca
// End marker, used to terminate bytecode sequences:
public static final int _end_marker = 255;
// Escapes:
public static final int _byte_escape = 254;
public static final int _ref_escape = 253;
// Self-relative pseudo-opcodes for better compression.
// A "linker op" is a bytecode which links to a class member.
// (But in what follows, "invokeinterface" ops are excluded.)
//
// A "self linker op" is a variant bytecode which works only
// with the current class or its super. Because the number of
// possible targets is small, it admits a more compact encoding.
// Self linker ops are allowed to absorb a previous "aload_0" op.
// There are (7 * 4) self linker ops (super or not, aload_0 or not).
//
// For simplicity, we define the full symmetric set of variants.
// However, some of them are relatively useless.
// Self linker ops are enabled by Pack.selfCallVariants (true).
public static final int _first_linker_op = _getstatic;
public static final int _last_linker_op = _invokestatic;
public static final int _num_linker_ops = (_last_linker_op - _first_linker_op) + 1;
public static final int _self_linker_op = _bytecode_limit;
public static final int _self_linker_aload_flag = 1*_num_linker_ops;
public static final int _self_linker_super_flag = 2*_num_linker_ops;
public static final int _self_linker_limit = _self_linker_op + 4*_num_linker_ops;
// An "invoke init" op is a variant of invokespecial which works
// only with the method name "<init>". There are variants which
// link to the current class, the super class, or the class of the
// immediately previous "newinstance" op. There are 3 of these ops.
// They all take method signature references as operands.
// Invoke init ops are enabled by Pack.initCallVariants (true).
public static final int _invokeinit_op = _self_linker_limit;
public static final int _invokeinit_self_option = 0;
public static final int _invokeinit_super_option = 1;
public static final int _invokeinit_new_option = 2;
public static final int _invokeinit_limit = _invokeinit_op+3;
public static final int _pseudo_instruction_limit = _invokeinit_limit;
// linker variant limit == 202+(7*4)+3 == 233
// Ldc variants support strongly typed references to constants.
// This lets us index constant pool entries completely according to tag,
// which is a great simplification.
// Ldc variants gain us only 0.007% improvement in compression ratio,
// but they simplify the file format greatly.
public static final int _xldc_op = _invokeinit_limit;
public static final int _sldc = _ldc; // previously named _aldc
public static final int _cldc = _xldc_op+0;
public static final int _ildc = _xldc_op+1;
public static final int _fldc = _xldc_op+2;
public static final int _sldc_w = _ldc_w; // previously named _aldc_w
public static final int _cldc_w = _xldc_op+3;
public static final int _ildc_w = _xldc_op+4;
public static final int _fldc_w = _xldc_op+5;
public static final int _lldc2_w = _ldc2_w;
public static final int _dldc2_w = _xldc_op+6;
// anything other than primitive, string, or class must be handled with qldc:
public static final int _qldc = _xldc_op+7;
public static final int _qldc_w = _xldc_op+8;
public static final int _xldc_limit = _xldc_op+9;
// handling of InterfaceMethodRef
public static final int _invoke_int_op = _xldc_limit;
public static final int _invokespecial_int = _invoke_int_op+0;
public static final int _invokestatic_int = _invoke_int_op+1;
public static final int _invoke_int_limit = _invoke_int_op+2;
}

View file

@ -1,750 +0,0 @@
/*
* Copyright (c) 2003, 2019, 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 sun.nio.cs.UTF_8;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.MessageFormat;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/** Command line interface for Pack200.
*/
@SuppressWarnings({"removal"})
class Driver {
private static final ResourceBundle RESOURCE =
ResourceBundle.getBundle("com.sun.java.util.jar.pack.DriverResource");
private static boolean suppressDeprecateMsg = false;
public static void main(String[] ava) throws IOException {
List<String> av = new ArrayList<>(Arrays.asList(ava));
boolean doPack = true;
boolean doUnpack = false;
boolean doRepack = false;
boolean doZip = true;
suppressDeprecateMsg = av.remove("-XDsuppress-tool-removal-message");
String logFile = null;
String verboseProp = Utils.DEBUG_VERBOSE;
{
// Non-standard, undocumented "--unpack" switch enables unpack mode.
String arg0 = av.isEmpty() ? "" : av.get(0);
switch (arg0) {
case "--pack":
av.remove(0);
break;
case "--unpack":
av.remove(0);
doPack = false;
doUnpack = true;
break;
}
}
if (!suppressDeprecateMsg) {
printDeprecateWarning(doPack, System.out);
}
// Collect engine properties here:
Map<String,String> engProps = new HashMap<>();
engProps.put(verboseProp, System.getProperty(verboseProp));
String optionMap;
String[] propTable;
if (doPack) {
optionMap = PACK200_OPTION_MAP;
propTable = PACK200_PROPERTY_TO_OPTION;
} else {
optionMap = UNPACK200_OPTION_MAP;
propTable = UNPACK200_PROPERTY_TO_OPTION;
}
// Collect argument properties here:
Map<String,String> avProps = new HashMap<>();
try {
for (;;) {
String state = parseCommandOptions(av, optionMap, avProps);
// Translate command line options to Pack200 properties:
eachOpt:
for (Iterator<String> opti = avProps.keySet().iterator();
opti.hasNext(); ) {
String opt = opti.next();
String prop = null;
for (int i = 0; i < propTable.length; i += 2) {
if (opt.equals(propTable[1+i])) {
prop = propTable[0+i];
break;
}
}
if (prop != null) {
String val = avProps.get(opt);
opti.remove(); // remove opt from avProps
if (!prop.endsWith(".")) {
// Normal string or boolean.
if (!(opt.equals("--verbose")
|| opt.endsWith("="))) {
// Normal boolean; convert to T/F.
boolean flag = (val != null);
if (opt.startsWith("--no-"))
flag = !flag;
val = flag? "true": "false";
}
engProps.put(prop, val);
} else if (prop.contains(".attribute.")) {
for (String val1 : val.split("\0")) {
String[] val2 = val1.split("=", 2);
engProps.put(prop+val2[0], val2[1]);
}
} else {
// Collection property: pack.pass.file.cli.NNN
int idx = 1;
for (String val1 : val.split("\0")) {
String prop1;
do {
prop1 = prop+"cli."+(idx++);
} while (engProps.containsKey(prop1));
engProps.put(prop1, val1);
}
}
}
}
// See if there is any other action to take.
if ("--config-file=".equals(state)) {
String propFile = av.remove(0);
Properties fileProps = new Properties();
try (InputStream propIn = new FileInputStream(propFile)) {
fileProps.load(propIn);
}
if (engProps.get(verboseProp) != null)
fileProps.list(System.out);
for (Map.Entry<Object,Object> me : fileProps.entrySet()) {
engProps.put((String) me.getKey(), (String) me.getValue());
}
} else if ("--version".equals(state)) {
System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.VERSION),
Driver.class.getName(), "1.31, 07/05/05"));
return;
} else if ("--help".equals(state)) {
printUsage(doPack, true, System.out);
System.exit(0);
return;
} else {
break;
}
}
} catch (IllegalArgumentException ee) {
System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.BAD_ARGUMENT), ee));
printUsage(doPack, false, System.err);
System.exit(2);
return;
}
// Deal with remaining non-engine properties:
for (String opt : avProps.keySet()) {
String val = avProps.get(opt);
switch (opt) {
case "--repack":
doRepack = true;
break;
case "--no-gzip":
doZip = (val == null);
break;
case "--log-file=":
logFile = val;
break;
default:
throw new InternalError(MessageFormat.format(
RESOURCE.getString(DriverResource.BAD_OPTION),
opt, avProps.get(opt)));
}
}
if (logFile != null && !logFile.isEmpty()) {
if (logFile.equals("-")) {
System.setErr(System.out);
} else {
OutputStream log = new FileOutputStream(logFile);
//log = new BufferedOutputStream(out);
System.setErr(new PrintStream(log));
}
}
boolean verbose = (engProps.get(verboseProp) != null);
String packfile = "";
if (!av.isEmpty())
packfile = av.remove(0);
String jarfile = "";
if (!av.isEmpty())
jarfile = av.remove(0);
String newfile = ""; // output JAR file if --repack
String bakfile = ""; // temporary backup of input JAR
String tmpfile = ""; // temporary file to be deleted
if (doRepack) {
// The first argument is the target JAR file.
// (Note: *.pac is nonstandard, but may be necessary
// if a host OS truncates file extensions.)
if (packfile.toLowerCase().endsWith(".pack") ||
packfile.toLowerCase().endsWith(".pac") ||
packfile.toLowerCase().endsWith(".gz")) {
System.err.println(MessageFormat.format(
RESOURCE.getString(DriverResource.BAD_REPACK_OUTPUT),
packfile));
printUsage(doPack, false, System.err);
System.exit(2);
}
newfile = packfile;
// The optional second argument is the source JAR file.
if (jarfile.isEmpty()) {
// If only one file is given, it is the only JAR.
// It serves as both input and output.
jarfile = newfile;
}
tmpfile = createTempFile(newfile, ".pack").getPath();
packfile = tmpfile;
doZip = false; // no need to zip the temporary file
}
if (!av.isEmpty()
// Accept jarfiles ending with .jar or .zip.
// Accept jarfile of "-" (stdout), but only if unpacking.
|| !(jarfile.toLowerCase().endsWith(".jar")
|| jarfile.toLowerCase().endsWith(".zip")
|| (jarfile.equals("-") && !doPack))) {
printUsage(doPack, false, System.err);
System.exit(2);
return;
}
if (doRepack)
doPack = doUnpack = true;
else if (doPack)
doUnpack = false;
Pack200.Packer jpack = Pack200.newPacker();
Pack200.Unpacker junpack = Pack200.newUnpacker();
jpack.properties().putAll(engProps);
junpack.properties().putAll(engProps);
if (doRepack && newfile.equals(jarfile)) {
String zipc = getZipComment(jarfile);
if (verbose && !zipc.isEmpty())
System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.DETECTED_ZIP_COMMENT), zipc));
if (zipc.indexOf(Utils.PACK_ZIP_ARCHIVE_MARKER_COMMENT) >= 0) {
System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.SKIP_FOR_REPACKED), jarfile));
doPack = false;
doUnpack = false;
doRepack = false;
}
}
try {
if (doPack) {
// Mode = Pack.
JarFile in = new JarFile(new File(jarfile));
OutputStream out;
// Packfile must be -, *.gz, *.pack, or *.pac.
if (packfile.equals("-")) {
out = System.out;
// Send warnings, etc., to stderr instead of stdout.
System.setOut(System.err);
} else if (doZip) {
if (!packfile.endsWith(".gz")) {
System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.WRITE_PACK_FILE), packfile));
printUsage(doPack, false, System.err);
System.exit(2);
}
out = new FileOutputStream(packfile);
out = new BufferedOutputStream(out);
out = new GZIPOutputStream(out);
} else {
if (!packfile.toLowerCase().endsWith(".pack") &&
!packfile.toLowerCase().endsWith(".pac")) {
System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.WRITE_PACKGZ_FILE),packfile));
printUsage(doPack, false, System.err);
System.exit(2);
}
out = new FileOutputStream(packfile);
out = new BufferedOutputStream(out);
}
jpack.pack(in, out);
//in.close(); // p200 closes in but not out
out.close();
}
if (doRepack && newfile.equals(jarfile)) {
// If the source and destination are the same,
// we will move the input JAR aside while regenerating it.
// This allows us to restore it if something goes wrong.
File bakf = createTempFile(jarfile, ".bak");
// On Windows target must be deleted see 4017593
bakf.delete();
boolean okBackup = new File(jarfile).renameTo(bakf);
if (!okBackup) {
throw new Error(MessageFormat.format(RESOURCE.getString(DriverResource.SKIP_FOR_MOVE_FAILED),bakfile));
} else {
// Open jarfile recovery bracket.
bakfile = bakf.getPath();
}
}
if (doUnpack) {
// Mode = Unpack.
InputStream in;
if (packfile.equals("-"))
in = System.in;
else
in = new FileInputStream(new File(packfile));
BufferedInputStream inBuf = new BufferedInputStream(in);
in = inBuf;
if (Utils.isGZIPMagic(Utils.readMagic(inBuf))) {
in = new GZIPInputStream(in);
}
String outfile = newfile.isEmpty()? jarfile: newfile;
OutputStream fileOut;
if (outfile.equals("-"))
fileOut = System.out;
else
fileOut = new FileOutputStream(outfile);
fileOut = new BufferedOutputStream(fileOut);
try (JarOutputStream out = new JarOutputStream(fileOut)) {
junpack.unpack(in, out);
// p200 closes in but not out
}
// At this point, we have a good jarfile (or newfile, if -r)
}
if (!bakfile.isEmpty()) {
// On success, abort jarfile recovery bracket.
new File(bakfile).delete();
bakfile = "";
}
} finally {
// Close jarfile recovery bracket.
if (!bakfile.isEmpty()) {
File jarFile = new File(jarfile);
jarFile.delete(); // Win32 requires this, see above
new File(bakfile).renameTo(jarFile);
}
// In all cases, delete temporary *.pack.
if (!tmpfile.isEmpty())
new File(tmpfile).delete();
}
}
private static
File createTempFile(String basefile, String suffix) throws IOException {
File base = new File(basefile);
String prefix = base.getName();
if (prefix.length() < 3) prefix += "tmp";
File where = (base.getParentFile() == null && suffix.equals(".bak"))
? new File(".").getAbsoluteFile()
: base.getParentFile();
Path tmpfile = (where == null)
? Files.createTempFile(prefix, suffix)
: Files.createTempFile(where.toPath(), prefix, suffix);
return tmpfile.toFile();
}
private static
void printDeprecateWarning(boolean doPack, PrintStream out) {
String prog = doPack ? "pack200" : "unpack200";
out.println(MessageFormat.format(RESOURCE.getString(DriverResource.DEPRECATED), prog));
}
private static
void printUsage(boolean doPack, boolean full, PrintStream out) {
String prog = doPack ? "pack200" : "unpack200";
String[] packUsage = (String[])RESOURCE.getObject(DriverResource.PACK_HELP);
String[] unpackUsage = (String[])RESOURCE.getObject(DriverResource.UNPACK_HELP);
String[] usage = doPack? packUsage: unpackUsage;
for (int i = 0; i < usage.length; i++) {
out.println(usage[i]);
if (!full) {
out.println(MessageFormat.format(RESOURCE.getString(DriverResource.MORE_INFO), prog));
break;
}
}
// Print a warning at the end
// The full help page is long, the beginning warning could be out of sight
if (full && !suppressDeprecateMsg) {
printDeprecateWarning(doPack, out);
}
}
private static
String getZipComment(String jarfile) throws IOException {
byte[] tail = new byte[1000];
long filelen = new File(jarfile).length();
if (filelen <= 0) return "";
long skiplen = Math.max(0, filelen - tail.length);
try (InputStream in = new FileInputStream(new File(jarfile))) {
in.skip(skiplen);
in.read(tail);
for (int i = tail.length-4; i >= 0; i--) {
if (tail[i+0] == 'P' && tail[i+1] == 'K' &&
tail[i+2] == 5 && tail[i+3] == 6) {
// Skip sig4, disks4, entries4, clen4, coff4, cmt2
i += 4+4+4+4+4+2;
if (i < tail.length)
return new String(tail, i, tail.length-i, UTF_8.INSTANCE);
return "";
}
}
return "";
}
}
private static final String PACK200_OPTION_MAP =
(""
+"--repack $ \n -r +>- @--repack $ \n"
+"--no-gzip $ \n -g +>- @--no-gzip $ \n"
+"--strip-debug $ \n -G +>- @--strip-debug $ \n"
+"--no-keep-file-order $ \n -O +>- @--no-keep-file-order $ \n"
+"--segment-limit= *> = \n -S +> @--segment-limit= = \n"
+"--effort= *> = \n -E +> @--effort= = \n"
+"--deflate-hint= *> = \n -H +> @--deflate-hint= = \n"
+"--modification-time= *> = \n -m +> @--modification-time= = \n"
+"--pass-file= *> &\0 \n -P +> @--pass-file= &\0 \n"
+"--unknown-attribute= *> = \n -U +> @--unknown-attribute= = \n"
+"--class-attribute= *> &\0 \n -C +> @--class-attribute= &\0 \n"
+"--field-attribute= *> &\0 \n -F +> @--field-attribute= &\0 \n"
+"--method-attribute= *> &\0 \n -M +> @--method-attribute= &\0 \n"
+"--code-attribute= *> &\0 \n -D +> @--code-attribute= &\0 \n"
+"--config-file= *> . \n -f +> @--config-file= . \n"
// Negative options as required by CLIP:
+"--no-strip-debug !--strip-debug \n"
+"--gzip !--no-gzip \n"
+"--keep-file-order !--no-keep-file-order \n"
// Non-Standard Options
+"--verbose $ \n -v +>- @--verbose $ \n"
+"--quiet !--verbose \n -q +>- !--verbose \n"
+"--log-file= *> = \n -l +> @--log-file= = \n"
//+"--java-option= *> = \n -J +> @--java-option= = \n"
+"--version . \n -V +> @--version . \n"
+"--help . \n -? +> @--help . \n -h +> @--help . \n"
// Termination:
+"-- . \n" // end option sequence here
+"- +? >- . \n" // report error if -XXX present; else use stdout
);
// Note: Collection options use "\0" as a delimiter between arguments.
// For Java version of unpacker (used for testing only):
private static final String UNPACK200_OPTION_MAP =
(""
+"--deflate-hint= *> = \n -H +> @--deflate-hint= = \n"
+"--verbose $ \n -v +>- @--verbose $ \n"
+"--quiet !--verbose \n -q +>- !--verbose \n"
+"--remove-pack-file $ \n -r +>- @--remove-pack-file $ \n"
+"--log-file= *> = \n -l +> @--log-file= = \n"
+"--config-file= *> . \n -f +> @--config-file= . \n"
// Termination:
+"-- . \n" // end option sequence here
+"- +? >- . \n" // report error if -XXX present; else use stdin
+"--version . \n -V +> @--version . \n"
+"--help . \n -? +> @--help . \n -h +> @--help . \n"
);
private static final String[] PACK200_PROPERTY_TO_OPTION = {
Pack200.Packer.SEGMENT_LIMIT, "--segment-limit=",
Pack200.Packer.KEEP_FILE_ORDER, "--no-keep-file-order",
Pack200.Packer.EFFORT, "--effort=",
Pack200.Packer.DEFLATE_HINT, "--deflate-hint=",
Pack200.Packer.MODIFICATION_TIME, "--modification-time=",
Pack200.Packer.PASS_FILE_PFX, "--pass-file=",
Pack200.Packer.UNKNOWN_ATTRIBUTE, "--unknown-attribute=",
Pack200.Packer.CLASS_ATTRIBUTE_PFX, "--class-attribute=",
Pack200.Packer.FIELD_ATTRIBUTE_PFX, "--field-attribute=",
Pack200.Packer.METHOD_ATTRIBUTE_PFX, "--method-attribute=",
Pack200.Packer.CODE_ATTRIBUTE_PFX, "--code-attribute=",
//Pack200.Packer.PROGRESS, "--progress=",
Utils.DEBUG_VERBOSE, "--verbose",
Utils.COM_PREFIX+"strip.debug", "--strip-debug",
};
private static final String[] UNPACK200_PROPERTY_TO_OPTION = {
Pack200.Unpacker.DEFLATE_HINT, "--deflate-hint=",
//Pack200.Unpacker.PROGRESS, "--progress=",
Utils.DEBUG_VERBOSE, "--verbose",
Utils.UNPACK_REMOVE_PACKFILE, "--remove-pack-file",
};
/*-*
* Remove a set of command-line options from args,
* storing them in the map in a canonicalized form.
* <p>
* The options string is a newline-separated series of
* option processing specifiers.
*/
private static
String parseCommandOptions(List<String> args,
String options,
Map<String,String> properties) {
//System.out.println(args+" // "+properties);
String resultString = null;
// Convert options string into optLines dictionary.
TreeMap<String,String[]> optmap = new TreeMap<>();
loadOptmap:
for (String optline : options.split("\n")) {
String[] words = optline.split("\\p{Space}+");
if (words.length == 0) continue loadOptmap;
String opt = words[0];
words[0] = ""; // initial word is not a spec
if (opt.isEmpty() && words.length >= 1) {
opt = words[1]; // initial "word" is empty due to leading ' '
words[1] = "";
}
if (opt.length() == 0) continue loadOptmap;
String[] prevWords = optmap.put(opt, words);
if (prevWords != null)
throw new RuntimeException(MessageFormat.format(RESOURCE.getString(DriverResource.DUPLICATE_OPTION), optline.trim()));
}
// State machine for parsing a command line.
ListIterator<String> argp = args.listIterator();
ListIterator<String> pbp = new ArrayList<String>().listIterator();
doArgs:
for (;;) {
// One trip through this loop per argument.
// Multiple trips per option only if several options per argument.
String arg;
if (pbp.hasPrevious()) {
arg = pbp.previous();
pbp.remove();
} else if (argp.hasNext()) {
arg = argp.next();
} else {
// No more arguments at all.
break doArgs;
}
tryOpt:
for (int optlen = arg.length(); ; optlen--) {
// One time through this loop for each matching arg prefix.
String opt;
// Match some prefix of the argument to a key in optmap.
findOpt:
for (;;) {
opt = arg.substring(0, optlen);
if (optmap.containsKey(opt)) break findOpt;
if (optlen == 0) break tryOpt;
// Decide on a smaller prefix to search for.
SortedMap<String,String[]> pfxmap = optmap.headMap(opt);
// pfxmap.lastKey is no shorter than any prefix in optmap.
int len = pfxmap.isEmpty() ? 0 : pfxmap.lastKey().length();
optlen = Math.min(len, optlen - 1);
opt = arg.substring(0, optlen);
// (Note: We could cut opt down to its common prefix with
// pfxmap.lastKey, but that wouldn't save many cycles.)
}
opt = opt.intern();
assert(arg.startsWith(opt));
assert(opt.length() == optlen);
String val = arg.substring(optlen); // arg == opt+val
// Execute the option processing specs for this opt.
// If no actions are taken, then look for a shorter prefix.
boolean didAction = false;
boolean isError = false;
int pbpMark = pbp.nextIndex(); // in case of backtracking
String[] specs = optmap.get(opt);
eachSpec:
for (String spec : specs) {
if (spec.length() == 0) continue eachSpec;
if (spec.startsWith("#")) break eachSpec;
int sidx = 0;
char specop = spec.charAt(sidx++);
// Deal with '+'/'*' prefixes (spec conditions).
boolean ok;
switch (specop) {
case '+':
// + means we want an non-empty val suffix.
ok = !val.isEmpty();
specop = spec.charAt(sidx++);
break;
case '*':
// * means we accept empty or non-empty
ok = true;
specop = spec.charAt(sidx++);
break;
default:
// No condition prefix means we require an exact
// match, as indicated by an empty val suffix.
ok = (val.length() == 0);
break;
}
if (!ok) continue eachSpec;
String specarg = spec.substring(sidx);
switch (specop) {
case '.': // terminate the option sequence
resultString = specarg.isEmpty() ? opt : specarg.intern();
break doArgs;
case '?': // abort the option sequence
resultString = specarg.isEmpty() ? arg : specarg.intern();
isError = true;
break eachSpec;
case '@': // change the effective opt name
opt = specarg.intern();
break;
case '>': // shift remaining arg val to next arg
pbp.add(specarg + val); // push a new argument
val = "";
break;
case '!': // negation option
String negopt = specarg.isEmpty() ? opt : specarg.intern();
properties.remove(negopt);
properties.put(negopt, null); // leave placeholder
didAction = true;
break;
case '$': // normal "boolean" option
String boolval;
if (!specarg.isEmpty()) {
// If there is a given spec token, store it.
boolval = specarg;
} else {
String old = properties.get(opt);
if (old == null || old.length() == 0) {
boolval = "1";
} else {
// Increment any previous value as a numeral.
boolval = ""+(1+Integer.parseInt(old));
}
}
properties.put(opt, boolval);
didAction = true;
break;
case '=': // "string" option
case '&': // "collection" option
// Read an option.
boolean append = (specop == '&');
String strval;
if (pbp.hasPrevious()) {
strval = pbp.previous();
pbp.remove();
} else if (argp.hasNext()) {
strval = argp.next();
} else {
resultString = arg + " ?";
isError = true;
break eachSpec;
}
if (append) {
String old = properties.get(opt);
if (old != null) {
// Append new val to old with embedded delim.
String delim = specarg;
if (delim.length() == 0) delim = " ";
strval = old + specarg + strval;
}
}
properties.put(opt, strval);
didAction = true;
break;
default:
throw new RuntimeException(MessageFormat.format(RESOURCE.getString(DriverResource.BAD_SPEC),opt, spec));
}
}
// Done processing specs.
if (didAction && !isError) {
continue doArgs;
}
// The specs should have done something, but did not.
while (pbp.nextIndex() > pbpMark) {
// Remove anything pushed during these specs.
pbp.previous();
pbp.remove();
}
if (isError) {
throw new IllegalArgumentException(resultString);
}
if (optlen == 0) {
// We cannot try a shorter matching option.
break tryOpt;
}
}
// If we come here, there was no matching option.
// So, push back the argument, and return to caller.
pbp.add(arg);
break doArgs;
}
// Report number of arguments consumed.
args.subList(0, argp.nextIndex()).clear();
// Report any unconsumed partial argument.
while (pbp.hasPrevious()) {
args.add(0, pbp.previous());
}
//System.out.println(args+" // "+properties+" -> "+resultString);
return resultString;
}
}

View file

@ -1,136 +0,0 @@
/*
* Copyright (c) 2005, 2018, 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 java.util.ListResourceBundle;
public class DriverResource extends ListResourceBundle {
public static final String VERSION = "VERSION";
public static final String BAD_ARGUMENT = "BAD_ARGUMENT";
public static final String BAD_OPTION = "BAD_OPTION";
public static final String BAD_REPACK_OUTPUT = "BAD_REPACK_OUTPUT";
public static final String DETECTED_ZIP_COMMENT = "DETECTED_ZIP_COMMENT";
public static final String SKIP_FOR_REPACKED = "SKIP_FOR_REPACKED";
public static final String WRITE_PACK_FILE = "WRITE_PACK_FILE";
public static final String WRITE_PACKGZ_FILE = "WRITE_PACKGZ_FILE";
public static final String SKIP_FOR_MOVE_FAILED = "SKIP_FOR_MOVE_FAILED";
public static final String PACK_HELP = "PACK_HELP";
public static final String UNPACK_HELP = "UNPACK_HELP";
public static final String MORE_INFO = "MORE_INFO";
public static final String DUPLICATE_OPTION = "DUPLICATE_OPTION";
public static final String BAD_SPEC = "BAD_SPEC";
public static final String DEPRECATED = "DEPRECATED";
/*
* The following are the output of 'pack200' and 'unpack200' commands.
* Do not translate command arguments and words with a prefix of '-' or '--'.
*/
private static final Object[][] resource = {
{VERSION, "{0} version {1}"}, // parameter 0:class name;parameter 1: version value
{BAD_ARGUMENT, "Bad argument: {0}"},
{BAD_OPTION, "Bad option: {0}={1}"}, // parameter 0:option name;parameter 1:option value
{BAD_REPACK_OUTPUT, "Bad --repack output: {0}"}, // parameter 0:filename
{DETECTED_ZIP_COMMENT, "Detected ZIP comment: {0}"}, // parameter 0:comment
{SKIP_FOR_REPACKED, "Skipping because already repacked: {0}"}, // parameter 0:filename
{WRITE_PACK_FILE, "To write a *.pack file, specify --no-gzip: {0}"}, // parameter 0:filename
{WRITE_PACKGZ_FILE, "To write a *.pack.gz file, specify --gzip: {0}"}, // parameter 0:filename
{SKIP_FOR_MOVE_FAILED, "Skipping unpack because move failed: {0}"}, // parameter 0:filename
{PACK_HELP, new String[] {
"Usage: pack200 [-opt... | --option=value]... x.pack[.gz] y.jar",
"",
"Packing Options",
" -r, --repack repack or normalize a jar, suitable for ",
" signing with jarsigner",
" -g, --no-gzip output a plain pack file, suitable to be",
" compressed with a file compression utility",
" --gzip (default) post compress the pack output",
" with gzip",
" -G, --strip-debug remove debugging attributes (SourceFile,",
" LineNumberTable, LocalVariableTable",
" and LocalVariableTypeTable) while packing",
" -O, --no-keep-file-order do not transmit file ordering information",
" --keep-file-order (default) preserve input file ordering",
" -S{N}, --segment-limit={N} limit segment sizes (default unlimited)",
" -E{N}, --effort={N} packing effort (default N=5)",
" -H{h}, --deflate-hint={h} transmit deflate hint: true, false,",
" or keep (default)",
" -m{V}, --modification-time={V} transmit modtimes: latest or keep (default)",
" -P{F}, --pass-file={F} transmit the given input element(s) unchanged",
" -U{a}, --unknown-attribute={a} unknown attribute action: error, strip,",
" or pass (default)",
" -C{N}={L}, --class-attribute={N}={L} (user-defined attribute)",
" -F{N}={L}, --field-attribute={N}={L} (user-defined attribute)",
" -M{N}={L}, --method-attribute={N}={L} (user-defined attribute)",
" -D{N}={L}, --code-attribute={N}={L} (user-defined attribute)",
" -f{F}, --config-file={F} read file F for Pack200.Packer properties",
" -v, --verbose increase program verbosity",
" -q, --quiet set verbosity to lowest level",
" -l{F}, --log-file={F} output to the given log file, ",
" or '-' for System.out",
" -?, -h, --help print this help message",
" -V, --version print program version",
" -J{X} pass option X to underlying Java VM",
"",
"Notes:",
" The -P, -C, -F, -M, and -D options accumulate.",
" Example attribute definition: -C SourceFile=RUH .",
" Config. file properties are defined by the Pack200 API.",
" For meaning of -S, -E, -H-, -m, -U values, see Pack200 API.",
" Layout definitions (like RUH) are defined by JSR 200.",
"",
"Repacking mode updates the JAR file with a pack/unpack cycle:",
" pack200 [-r|--repack] [-opt | --option=value]... [repackedy.jar] y.jar\n",
"",
"Exit Status:",
" 0 if successful, >0 if an error occurred"
}
},
{UNPACK_HELP, new String[] {
"Usage: unpack200 [-opt... | --option=value]... x.pack[.gz] y.jar\n",
"",
"Unpacking Options",
" -H{h}, --deflate-hint={h} override transmitted deflate hint:",
" true, false, or keep (default)",
" -r, --remove-pack-file remove input file after unpacking",
" -v, --verbose increase program verbosity",
" -q, --quiet set verbosity to lowest level",
" -l{F}, --log-file={F} output to the given log file, or",
" '-' for System.out",
" -?, -h, --help print this help message",
" -V, --version print program version",
" -J{X} pass option X to underlying Java VM"
}
},
{MORE_INFO, "(For more information, run {0} --help .)"}, // parameter 0:command name
{DUPLICATE_OPTION, "duplicate option: {0}"}, // parameter 0:option
{BAD_SPEC, "bad spec for {0}: {1}"}, // parameter 0:option;parameter 1:specifier
{DEPRECATED, "\nWarning: The {0} tool is deprecated, and is planned for removal in a future JDK release.\n"} // parameter 0:command name
};
protected Object[][] getContents() {
return resource;
}
}

View file

@ -1,136 +0,0 @@
/*
* Copyright (c) 2005, 2018, 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 java.util.ListResourceBundle;
public class DriverResource_ja extends ListResourceBundle {
public static final String VERSION = "VERSION";
public static final String BAD_ARGUMENT = "BAD_ARGUMENT";
public static final String BAD_OPTION = "BAD_OPTION";
public static final String BAD_REPACK_OUTPUT = "BAD_REPACK_OUTPUT";
public static final String DETECTED_ZIP_COMMENT = "DETECTED_ZIP_COMMENT";
public static final String SKIP_FOR_REPACKED = "SKIP_FOR_REPACKED";
public static final String WRITE_PACK_FILE = "WRITE_PACK_FILE";
public static final String WRITE_PACKGZ_FILE = "WRITE_PACKGZ_FILE";
public static final String SKIP_FOR_MOVE_FAILED = "SKIP_FOR_MOVE_FAILED";
public static final String PACK_HELP = "PACK_HELP";
public static final String UNPACK_HELP = "UNPACK_HELP";
public static final String MORE_INFO = "MORE_INFO";
public static final String DUPLICATE_OPTION = "DUPLICATE_OPTION";
public static final String BAD_SPEC = "BAD_SPEC";
public static final String DEPRECATED = "DEPRECATED";
/*
* The following are the output of 'pack200' and 'unpack200' commands.
* Do not translate command arguments and words with a prefix of '-' or '--'.
*/
private static final Object[][] resource = {
{VERSION, "{0}\u30D0\u30FC\u30B8\u30E7\u30F3{1}"}, // parameter 0:class name;parameter 1: version value
{BAD_ARGUMENT, "\u7121\u52B9\u306A\u5F15\u6570: {0}"},
{BAD_OPTION, "\u7121\u52B9\u306A\u30AA\u30D7\u30B7\u30E7\u30F3: {0}={1}"}, // parameter 0:option name;parameter 1:option value
{BAD_REPACK_OUTPUT, "\u7121\u52B9\u306A--repack\u51FA\u529B: {0}"}, // parameter 0:filename
{DETECTED_ZIP_COMMENT, "\u691C\u51FA\u3055\u308C\u305FZIP\u30B3\u30E1\u30F3\u30C8: {0}"}, // parameter 0:comment
{SKIP_FOR_REPACKED, "\u3059\u3067\u306B\u518D\u5727\u7E2E\u3055\u308C\u3066\u3044\u308B\u305F\u3081\u30B9\u30AD\u30C3\u30D7\u3057\u3066\u3044\u307E\u3059: {0}"}, // parameter 0:filename
{WRITE_PACK_FILE, "*.pack\u30D5\u30A1\u30A4\u30EB\u3092\u66F8\u304D\u8FBC\u3080\u306B\u306F\u3001--no-gzip\u3092\u6307\u5B9A\u3057\u307E\u3059: {0}"}, // parameter 0:filename
{WRITE_PACKGZ_FILE, "*.pack.gz\u30D5\u30A1\u30A4\u30EB\u3092\u66F8\u304D\u8FBC\u3080\u306B\u306F\u3001--gzip\u3092\u6307\u5B9A\u3057\u307E\u3059: {0}"}, // parameter 0:filename
{SKIP_FOR_MOVE_FAILED, "\u79FB\u52D5\u304C\u5931\u6557\u3057\u305F\u305F\u3081\u89E3\u51CD\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u3066\u3044\u307E\u3059: {0}"}, // parameter 0:filename
{PACK_HELP, new String[] {
"\u4F7F\u7528\u65B9\u6CD5: pack200 [-opt... | --option=value]... x.pack[.gz] y.jar",
"",
"\u5727\u7E2E\u30AA\u30D7\u30B7\u30E7\u30F3",
" -r\u3001--repack jar\u3092\u518D\u5727\u7E2E\u307E\u305F\u306F\u6B63\u898F\u5316\u3059\u308B\u30AA\u30D7\u30B7\u30E7\u30F3\u3067\u3001",
" jarsigner\u306B\u3088\u308B\u7F72\u540D\u306B\u9069\u3057\u307E\u3059",
" -g\u3001--no-gzip \u30D7\u30EC\u30FC\u30F3\u306Apack\u30D5\u30A1\u30A4\u30EB\u3092\u51FA\u529B\u3059\u308B\u30AA\u30D7\u30B7\u30E7\u30F3\u3067\u3001",
" \u30D5\u30A1\u30A4\u30EB\u5727\u7E2E\u30E6\u30FC\u30C6\u30A3\u30EA\u30C6\u30A3\u306B\u3088\u308B\u5727\u7E2E\u306B\u9069\u3057\u307E\u3059",
" --gzip (\u30C7\u30D5\u30A9\u30EB\u30C8) pack\u51FA\u529B\u3092\u5F8C\u51E6\u7406\u3067\u5727\u7E2E\u3057\u307E\u3059",
" (gzip\u3092\u4F7F\u7528)",
" -G\u3001--strip-debug \u5727\u7E2E\u4E2D\u306B\u30C7\u30D0\u30C3\u30B0\u5C5E\u6027(SourceFile\u3001",
" LineNumberTable\u3001LocalVariableTable",
" \u3001LocalVariableTypeTable)\u3092\u524A\u9664\u3057\u307E\u3059",
" -O\u3001--no-keep-file-order \u30D5\u30A1\u30A4\u30EB\u306E\u9806\u5E8F\u4ED8\u3051\u60C5\u5831\u3092\u8EE2\u9001\u3057\u307E\u305B\u3093",
" --keep-file-order (\u30C7\u30D5\u30A9\u30EB\u30C8)\u5165\u529B\u30D5\u30A1\u30A4\u30EB\u306E\u9806\u5E8F\u4ED8\u3051\u3092\u4FDD\u6301\u3057\u307E\u3059",
" -S{N}\u3001--segment-limit={N} \u30BB\u30B0\u30E1\u30F3\u30C8\u30FB\u30B5\u30A4\u30BA\u3092\u5236\u9650\u3057\u307E\u3059(\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u7121\u5236\u9650)",
" -E{N}\u3001--effort={N} \u5727\u7E2E\u306E\u8A66\u884C(\u30C7\u30D5\u30A9\u30EB\u30C8N=5)",
" -H{h}\u3001--deflate-hint={h} \u30C7\u30D5\u30EC\u30FC\u30C8\u30FB\u30D2\u30F3\u30C8\u3092\u8EE2\u9001\u3057\u307E\u3059: true\u3001false",
" \u307E\u305F\u306Fkeep(\u30C7\u30D5\u30A9\u30EB\u30C8)",
" -m{V}\u3001--modification-time={V} \u5909\u66F4\u6642\u9593\u3092\u8EE2\u9001\u3057\u307E\u3059: latest\u307E\u305F\u306Fkeep(\u30C7\u30D5\u30A9\u30EB\u30C8)",
" -P{F}\u3001--pass-file={F} \u6307\u5B9A\u3055\u308C\u305F\u5165\u529B\u8981\u7D20\u3092\u305D\u306E\u307E\u307E\u8EE2\u9001\u3057\u307E\u3059",
" -U{a}\u3001--unknown-attribute={a} \u4E0D\u660E\u306E\u5C5E\u6027\u30A2\u30AF\u30B7\u30E7\u30F3: error\u3001strip",
" \u307E\u305F\u306Fpass(\u30C7\u30D5\u30A9\u30EB\u30C8)",
" -C{N}={L}\u3001--class-attribute={N}={L} (\u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u5C5E\u6027)",
" -F{N}={L}\u3001--field-attribute={N}={L} (\u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u5C5E\u6027)",
" -M{N}={L}\u3001--method-attribute={N}={L} (\u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u5C5E\u6027)",
" -D{N}={L}\u3001--code-attribute={N}={L} (\u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u5C5E\u6027)",
" -f{F}\u3001--config-file={F} Pack200.Packer\u30D7\u30ED\u30D1\u30C6\u30A3\u306B\u30D5\u30A1\u30A4\u30EBF\u3092\u8AAD\u307F\u8FBC\u307F\u307E\u3059",
" -v\u3001--verbose \u30D7\u30ED\u30B0\u30E9\u30E0\u306E\u5197\u9577\u6027\u3092\u9AD8\u3081\u307E\u3059",
" -q\u3001--quiet \u5197\u9577\u6027\u3092\u6700\u4F4E\u30EC\u30D9\u30EB\u306B\u8A2D\u5B9A\u3057\u307E\u3059",
" -l{F}\u3001--log-file={F} \u6307\u5B9A\u306E\u30ED\u30B0\u30FB\u30D5\u30A1\u30A4\u30EB\u307E\u305F\u306FSystem.out ",
" ('-'\u306E\u5834\u5408)\u306B\u51FA\u529B\u3057\u307E\u3059",
" -?\u3001-h\u3001--help \u3053\u306E\u30D8\u30EB\u30D7\u30FB\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u51FA\u529B\u3057\u307E\u3059",
" -V\u3001--version \u30D7\u30ED\u30B0\u30E9\u30E0\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u51FA\u529B\u3057\u307E\u3059",
" -J{X} \u30AA\u30D7\u30B7\u30E7\u30F3X\u3092\u57FA\u790E\u3068\u306A\u308BJava VM\u306B\u6E21\u3057\u307E\u3059",
"",
"\u6CE8:",
" -P\u3001-C\u3001-F\u3001-M\u304A\u3088\u3073-D\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u7D2F\u7A4D\u3055\u308C\u307E\u3059\u3002",
" \u5C5E\u6027\u5B9A\u7FA9\u306E\u4F8B: -C SourceFile=RUH .",
" Config.\u30D5\u30A1\u30A4\u30EB\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3\u306F\u3001Pack200 API\u306B\u3088\u3063\u3066\u5B9A\u7FA9\u3055\u308C\u307E\u3059\u3002",
" -S\u3001-E\u3001-H\u3001-m\u3001-U\u306E\u5024\u306E\u610F\u5473\u306F\u3001Pack200 API\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
" \u30EC\u30A4\u30A2\u30A6\u30C8\u5B9A\u7FA9(RUH\u306A\u3069)\u306FJSR 200\u306B\u3088\u3063\u3066\u5B9A\u7FA9\u3055\u308C\u307E\u3059\u3002",
"",
"\u518D\u5727\u7E2E\u30E2\u30FC\u30C9\u3067\u306F\u3001JAR\u30D5\u30A1\u30A4\u30EB\u304C\u5727\u7E2E/\u89E3\u51CD\u30B5\u30A4\u30AF\u30EB\u3067\u66F4\u65B0\u3055\u308C\u307E\u3059:",
" pack200 [-r|--repack] [-opt | --option=value]... [repackedy.jar] y.jar\n",
"",
"\u7D42\u4E86\u30B9\u30C6\u30FC\u30BF\u30B9:",
" 0 (\u6210\u529F\u3057\u305F\u5834\u5408)\u3001>0 (\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u305F\u5834\u5408)"
}
},
{UNPACK_HELP, new String[] {
"\u4F7F\u7528\u65B9\u6CD5: unpack200 [-opt... | --option=value]... x.pack[.gz] y.jar\n",
"",
"\u89E3\u51CD\u30AA\u30D7\u30B7\u30E7\u30F3",
" -H{h}\u3001--deflate-hint={h} \u8EE2\u9001\u3055\u308C\u305F\u30C7\u30D5\u30EC\u30FC\u30C8\u30FB\u30D2\u30F3\u30C8\u3092\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9\u3057\u307E\u3059:",
" true\u3001false\u307E\u305F\u306Fkeep(\u30C7\u30D5\u30A9\u30EB\u30C8)",
" -r\u3001--remove-pack-file \u89E3\u51CD\u5F8C\u306B\u5165\u529B\u30D5\u30A1\u30A4\u30EB\u3092\u524A\u9664\u3057\u307E\u3059",
" -v\u3001--verbose \u30D7\u30ED\u30B0\u30E9\u30E0\u306E\u5197\u9577\u6027\u3092\u9AD8\u3081\u307E\u3059",
" -q\u3001--quiet \u5197\u9577\u6027\u3092\u6700\u4F4E\u30EC\u30D9\u30EB\u306B\u8A2D\u5B9A\u3057\u307E\u3059",
" -l{F}\u3001--log-file={F} \u6307\u5B9A\u306E\u30ED\u30B0\u30FB\u30D5\u30A1\u30A4\u30EB\u307E\u305F\u306F",
" System.out ('-'\u306E\u5834\u5408)\u306B\u51FA\u529B\u3057\u307E\u3059",
" -?\u3001-h\u3001--help \u3053\u306E\u30D8\u30EB\u30D7\u30FB\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u51FA\u529B\u3057\u307E\u3059",
" -V\u3001--version \u30D7\u30ED\u30B0\u30E9\u30E0\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u51FA\u529B\u3057\u307E\u3059",
" -J{X} \u30AA\u30D7\u30B7\u30E7\u30F3X\u3092\u57FA\u790E\u3068\u306A\u308BJava VM\u306B\u6E21\u3057\u307E\u3059"
}
},
{MORE_INFO, "(\u8A73\u7D30\u306F\u3001{0} --help\u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002)"}, // parameter 0:command name
{DUPLICATE_OPTION, "\u91CD\u8907\u30AA\u30D7\u30B7\u30E7\u30F3: {0}"}, // parameter 0:option
{BAD_SPEC, "{0}\u306E\u7121\u52B9\u306A\u4ED5\u69D8: {1}"}, // parameter 0:option;parameter 1:specifier
{DEPRECATED, "\n\u8B66\u544A: {0}\u30C4\u30FC\u30EB\u306F\u975E\u63A8\u5968\u3067\u3042\u308A\u3001\u4ECA\u5F8C\u306EJDK\u30EA\u30EA\u30FC\u30B9\u3067\u524A\u9664\u3055\u308C\u308B\u4E88\u5B9A\u3067\u3059\u3002\n"} // parameter 0:command name
};
protected Object[][] getContents() {
return resource;
}
}

View file

@ -1,136 +0,0 @@
/*
* Copyright (c) 2005, 2018, 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 java.util.ListResourceBundle;
public class DriverResource_zh_CN extends ListResourceBundle {
public static final String VERSION = "VERSION";
public static final String BAD_ARGUMENT = "BAD_ARGUMENT";
public static final String BAD_OPTION = "BAD_OPTION";
public static final String BAD_REPACK_OUTPUT = "BAD_REPACK_OUTPUT";
public static final String DETECTED_ZIP_COMMENT = "DETECTED_ZIP_COMMENT";
public static final String SKIP_FOR_REPACKED = "SKIP_FOR_REPACKED";
public static final String WRITE_PACK_FILE = "WRITE_PACK_FILE";
public static final String WRITE_PACKGZ_FILE = "WRITE_PACKGZ_FILE";
public static final String SKIP_FOR_MOVE_FAILED = "SKIP_FOR_MOVE_FAILED";
public static final String PACK_HELP = "PACK_HELP";
public static final String UNPACK_HELP = "UNPACK_HELP";
public static final String MORE_INFO = "MORE_INFO";
public static final String DUPLICATE_OPTION = "DUPLICATE_OPTION";
public static final String BAD_SPEC = "BAD_SPEC";
public static final String DEPRECATED = "DEPRECATED";
/*
* The following are the output of 'pack200' and 'unpack200' commands.
* Do not translate command arguments and words with a prefix of '-' or '--'.
*/
private static final Object[][] resource = {
{VERSION, "{0}\u7248\u672C {1}"}, // parameter 0:class name;parameter 1: version value
{BAD_ARGUMENT, "\u9519\u8BEF\u53C2\u6570: {0}"},
{BAD_OPTION, "\u9519\u8BEF\u9009\u9879: {0}={1}"}, // parameter 0:option name;parameter 1:option value
{BAD_REPACK_OUTPUT, "--repack \u8F93\u51FA\u9519\u8BEF: {0}"}, // parameter 0:filename
{DETECTED_ZIP_COMMENT, "\u68C0\u6D4B\u5230 ZIP \u6CE8\u91CA: {0}"}, // parameter 0:comment
{SKIP_FOR_REPACKED, "\u7531\u4E8E\u5DF2\u91CD\u65B0\u6253\u5305\u800C\u8DF3\u8FC7: {0}"}, // parameter 0:filename
{WRITE_PACK_FILE, "\u8981\u5199\u5165 *.pack \u6587\u4EF6, \u8BF7\u6307\u5B9A --no-gzip: {0}"}, // parameter 0:filename
{WRITE_PACKGZ_FILE, "\u8981\u5199\u5165 *.pack.gz \u6587\u4EF6, \u8BF7\u6307\u5B9A --gzip: {0}"}, // parameter 0:filename
{SKIP_FOR_MOVE_FAILED, "\u7531\u4E8E\u79FB\u52A8\u5931\u8D25\u800C\u8DF3\u8FC7\u89E3\u5305: {0}"}, // parameter 0:filename
{PACK_HELP, new String[] {
"\u7528\u6CD5: pack200 [-opt... | --option=value]... x.pack[.gz] y.jar",
"",
"\u6253\u5305\u9009\u9879",
" -r, --repack \u518D\u6B21\u6253\u5305\u6216\u89C4\u8303\u5316 jar, \u9002\u5408\u4E8E ",
" \u4F7F\u7528 jarsigner \u8FDB\u884C\u7B7E\u540D",
" -g, --no-gzip \u8F93\u51FA\u65E0\u683C\u5F0F\u7684\u5305\u6587\u4EF6, \u9002\u5408\u4E8E",
" \u4F7F\u7528\u6587\u4EF6\u538B\u7F29\u5B9E\u7528\u7A0B\u5E8F\u8FDB\u884C\u538B\u7F29",
" --gzip (\u9ED8\u8BA4\u503C) \u4F7F\u7528 gzip \u5BF9\u5305\u8F93\u51FA\u8FDB\u884C",
" \u538B\u7F29\u540E\u5904\u7406",
" -G, --strip-debug \u6253\u5305\u65F6\u5220\u9664\u8C03\u8BD5\u5C5E\u6027 (SourceFile,",
" LineNumberTable, LocalVariableTable",
" \u548C LocalVariableTypeTable)",
" -O, --no-keep-file-order \u4E0D\u4F20\u8F93\u6587\u4EF6\u6392\u5E8F\u4FE1\u606F",
" --keep-file-order (\u9ED8\u8BA4\u503C) \u4FDD\u7559\u8F93\u5165\u6587\u4EF6\u6392\u5E8F",
" -S{N}, --segment-limit={N} \u9650\u5236\u6BB5\u5927\u5C0F (\u9ED8\u8BA4\u4E3A\u65E0\u9650\u5236)",
" -E{N}, --effort={N} \u6253\u5305\u6548\u679C (\u9ED8\u8BA4\u503C N=5)",
" -H{h}, --deflate-hint={h} \u4F20\u8F93\u538B\u7F29\u63D0\u793A: true, false",
" \u6216 keep (\u9ED8\u8BA4\u503C)",
" -m{V}, --modification-time={V} \u4F20\u8F93 modtimes: latest \u6216 keep (\u9ED8\u8BA4\u503C)",
" -P{F}, --pass-file={F} \u4F20\u8F93\u672A\u66F4\u6539\u7684\u7ED9\u5B9A\u8F93\u5165\u5143\u7D20",
" -U{a}, --unknown-attribute={a} \u672A\u77E5\u5C5E\u6027\u64CD\u4F5C: error, strip",
" \u6216 pass (\u9ED8\u8BA4\u503C)",
" -C{N}={L}, --class-attribute={N}={L} (\u7528\u6237\u5B9A\u4E49\u7684\u5C5E\u6027)",
" -F{N}={L}, --field-attribute={N}={L} (\u7528\u6237\u5B9A\u4E49\u7684\u5C5E\u6027)",
" -M{N}={L}, --method-attribute={N}={L} (\u7528\u6237\u5B9A\u4E49\u7684\u5C5E\u6027)",
" -D{N}={L}, --code-attribute={N}={L} (\u7528\u6237\u5B9A\u4E49\u7684\u5C5E\u6027)",
" -f{F}, --config-file={F} \u8BFB\u53D6\u6587\u4EF6 F \u7684 Pack200.Packer \u5C5E\u6027",
" -v, --verbose \u63D0\u9AD8\u7A0B\u5E8F\u8BE6\u7EC6\u7A0B\u5EA6",
" -q, --quiet \u5C06\u8BE6\u7EC6\u7A0B\u5EA6\u8BBE\u7F6E\u4E3A\u6700\u4F4E\u7EA7\u522B",
" -l{F}, --log-file={F} \u8F93\u51FA\u5230\u7ED9\u5B9A\u65E5\u5FD7\u6587\u4EF6, ",
" \u6216\u5BF9\u4E8E System.out \u6307\u5B9A '-'",
" -?, -h, --help \u8F93\u51FA\u6B64\u5E2E\u52A9\u6D88\u606F",
" -V, --version \u8F93\u51FA\u7A0B\u5E8F\u7248\u672C",
" -J{X} \u5C06\u9009\u9879 X \u4F20\u9012\u7ED9\u57FA\u7840 Java VM",
"",
"\u6CE8:",
" -P, -C, -F, -M \u548C -D \u9009\u9879\u7D2F\u8BA1\u3002",
" \u793A\u4F8B\u5C5E\u6027\u5B9A\u4E49: -C SourceFile=RUH\u3002",
" Config. \u6587\u4EF6\u5C5E\u6027\u7531 Pack200 API \u5B9A\u4E49\u3002",
" \u6709\u5173 -S, -E, -H-, -m, -U \u503C\u7684\u542B\u4E49, \u8BF7\u53C2\u9605 Pack200 API\u3002",
" \u5E03\u5C40\u5B9A\u4E49 (\u4F8B\u5982 RUH) \u7531 JSR 200 \u5B9A\u4E49\u3002",
"",
"\u91CD\u65B0\u6253\u5305\u6A21\u5F0F\u901A\u8FC7\u6253\u5305/\u89E3\u5305\u5468\u671F\u66F4\u65B0 JAR \u6587\u4EF6:",
" pack200 [-r|--repack] [-opt | --option=value]... [repackedy.jar] y.jar\n",
"",
"\u9000\u51FA\u72B6\u6001:",
" \u5982\u679C\u6210\u529F\u5219\u4E3A 0; \u5982\u679C\u51FA\u73B0\u9519\u8BEF, \u5219\u4E3A\u5927\u4E8E 0 \u7684\u503C"
}
},
{UNPACK_HELP, new String[] {
"\u7528\u6CD5: unpack200 [-opt... | --option=value]... x.pack[.gz] y.jar\n",
"",
"\u89E3\u5305\u9009\u9879",
" -H{h}, --deflate-hint={h} \u8986\u76D6\u5DF2\u4F20\u8F93\u7684\u538B\u7F29\u63D0\u793A:",
" true, false \u6216 keep (\u9ED8\u8BA4\u503C)",
" -r, --remove-pack-file \u89E3\u5305\u4E4B\u540E\u5220\u9664\u8F93\u5165\u6587\u4EF6",
" -v, --verbose \u63D0\u9AD8\u7A0B\u5E8F\u8BE6\u7EC6\u7A0B\u5EA6",
" -q, --quiet \u5C06\u8BE6\u7EC6\u7A0B\u5EA6\u8BBE\u7F6E\u4E3A\u6700\u4F4E\u7EA7\u522B",
" -l{F}, --log-file={F} \u8F93\u51FA\u5230\u7ED9\u5B9A\u65E5\u5FD7\u6587\u4EF6, \u6216",
" \u5BF9\u4E8E System.out \u6307\u5B9A '-'",
" -?, -h, --help \u8F93\u51FA\u6B64\u5E2E\u52A9\u6D88\u606F",
" -V, --version \u8F93\u51FA\u7A0B\u5E8F\u7248\u672C",
" -J{X} \u5C06\u9009\u9879 X \u4F20\u9012\u7ED9\u57FA\u7840 Java VM"
}
},
{MORE_INFO, "(\u6709\u5173\u8BE6\u7EC6\u4FE1\u606F, \u8BF7\u8FD0\u884C {0} --help\u3002)"}, // parameter 0:command name
{DUPLICATE_OPTION, "\u91CD\u590D\u7684\u9009\u9879: {0}"}, // parameter 0:option
{BAD_SPEC, "{0}\u7684\u89C4\u8303\u9519\u8BEF: {1}"}, // parameter 0:option;parameter 1:specifier
{DEPRECATED, "\n\u8B66\u544A\uFF1A{0} \u5DE5\u5177\u5DF2\u8FC7\u65F6\uFF0C\u8BA1\u5212\u5728\u672A\u6765\u7684 JDK \u53D1\u884C\u7248\u4E2D\u5220\u9664\u3002\n"} // parameter 0:command name
};
protected Object[][] getContents() {
return resource;
}
}

View file

@ -1,174 +0,0 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.util.jar.pack;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/*
* @author ksrini
*/
/*
* This class provides an ArrayList implementation which has a fixed size,
* thus all the operations which modifies the size have been rendered
* inoperative. This essentially allows us to use generified array
* lists in lieu of arrays.
*/
final class FixedList<E> implements List<E> {
private final ArrayList<E> flist;
protected FixedList(int capacity) {
flist = new ArrayList<>(capacity);
// initialize the list to null
for (int i = 0 ; i < capacity ; i++) {
flist.add(null);
}
}
@Override
public int size() {
return flist.size();
}
@Override
public boolean isEmpty() {
return flist.isEmpty();
}
@Override
public boolean contains(Object o) {
return flist.contains(o);
}
@Override
public Iterator<E> iterator() {
return flist.iterator();
}
@Override
public Object[] toArray() {
return flist.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return flist.toArray(a);
}
@Override
public boolean add(E e) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public boolean remove(Object o) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public boolean containsAll(Collection<?> c) {
return flist.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public boolean addAll(int index, Collection<? extends E> c) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public boolean removeAll(Collection<?> c) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public boolean retainAll(Collection<?> c) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public void clear() throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public E get(int index) {
return flist.get(index);
}
@Override
public E set(int index, E element) {
return flist.set(index, element);
}
@Override
public void add(int index, E element) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public E remove(int index) throws UnsupportedOperationException {
throw new UnsupportedOperationException("operation not permitted");
}
@Override
public int indexOf(Object o) {
return flist.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return flist.lastIndexOf(o);
}
@Override
public ListIterator<E> listIterator() {
return flist.listIterator();
}
@Override
public ListIterator<E> listIterator(int index) {
return flist.listIterator(index);
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
return flist.subList(fromIndex, toIndex);
}
@Override
public String toString() {
return "FixedList{" + "plist=" + flist + '}';
}
}

View file

@ -1,570 +0,0 @@
/*
* Copyright (c) 2003, 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.Entry;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
/**
* Collection of relocatable constant pool references.
* It operates with respect to a particular byte array,
* and stores some of its state in the bytes themselves.
* <p>
* As a Collection, it can be iterated over, but it is not a List,
* since it does not natively support indexed access.
* <p>
*
* @author John Rose
*/
final class Fixups extends AbstractCollection<Fixups.Fixup> {
byte[] bytes; // the subject of the relocations
int head; // desc locating first reloc
int tail; // desc locating last reloc
int size; // number of relocations
Entry[] entries; // [0..size-1] relocations
int[] bigDescs; // descs which cannot be stored in the bytes
// A "desc" (descriptor) is a bit-encoded pair of a location
// and format. Every fixup occurs at a "desc". Until final
// patching, bytes addressed by descs may also be used to
// link this data structure together. If the bytes are missing,
// or if the "desc" is too large to encode in the bytes,
// it is kept in the bigDescs array.
Fixups(byte[] bytes) {
this.bytes = bytes;
entries = new Entry[3];
bigDescs = noBigDescs;
}
Fixups() {
// If there are no bytes, all descs are kept in bigDescs.
this((byte[])null);
}
Fixups(byte[] bytes, Collection<Fixup> fixups) {
this(bytes);
addAll(fixups);
}
Fixups(Collection<Fixup> fixups) {
this((byte[])null);
addAll(fixups);
}
private static final int MINBIGSIZE = 1;
// cleverly share empty bigDescs:
private static final int[] noBigDescs = {MINBIGSIZE};
@Override
public int size() {
return size;
}
public void trimToSize() {
if (size != entries.length) {
Entry[] oldEntries = entries;
entries = new Entry[size];
System.arraycopy(oldEntries, 0, entries, 0, size);
}
int bigSize = bigDescs[BIGSIZE];
if (bigSize == MINBIGSIZE) {
bigDescs = noBigDescs;
} else if (bigSize != bigDescs.length) {
int[] oldBigDescs = bigDescs;
bigDescs = new int[bigSize];
System.arraycopy(oldBigDescs, 0, bigDescs, 0, bigSize);
}
}
public void visitRefs(Collection<Entry> refs) {
for (int i = 0; i < size; i++) {
refs.add(entries[i]);
}
}
@Override
public void clear() {
if (bytes != null) {
// Clean the bytes:
for (Fixup fx : this) {
//System.out.println("clean "+fx);
storeIndex(fx.location(), fx.format(), 0);
}
}
size = 0;
if (bigDescs != noBigDescs)
bigDescs[BIGSIZE] = MINBIGSIZE;
// do not trim to size, however
}
public byte[] getBytes() {
return bytes;
}
public void setBytes(byte[] newBytes) {
if (bytes == newBytes) return;
ArrayList<Fixup> old = null;
assert((old = new ArrayList<>(this)) != null);
if (bytes == null || newBytes == null) {
// One or the other representations is deficient.
// Construct a checkpoint.
ArrayList<Fixup> save = new ArrayList<>(this);
clear();
bytes = newBytes;
addAll(save);
} else {
// assume newBytes is some sort of bitwise copy of the old bytes
bytes = newBytes;
}
assert(old.equals(new ArrayList<>(this)));
}
private static final int LOC_SHIFT = 1;
private static final int FMT_MASK = 0x1;
private static final byte UNUSED_BYTE = 0;
private static final byte OVERFLOW_BYTE = -1;
// fill pointer of bigDescs array is in element [0]
private static final int BIGSIZE = 0;
// Format values:
private static final int U2_FORMAT = 0;
private static final int U1_FORMAT = 1;
// Special values for the static methods.
private static final int SPECIAL_LOC = 0;
private static final int SPECIAL_FMT = U2_FORMAT;
static int fmtLen(int fmt) { return 1+(fmt-U1_FORMAT)/(U2_FORMAT-U1_FORMAT); }
static int descLoc(int desc) { return desc >>> LOC_SHIFT; }
static int descFmt(int desc) { return desc & FMT_MASK; }
static int descEnd(int desc) { return descLoc(desc) + fmtLen(descFmt(desc)); }
static int makeDesc(int loc, int fmt) {
int desc = (loc << LOC_SHIFT) | fmt;
assert(descLoc(desc) == loc);
assert(descFmt(desc) == fmt);
return desc;
}
int fetchDesc(int loc, int fmt) {
byte b1 = bytes[loc];
assert(b1 != OVERFLOW_BYTE);
int value;
if (fmt == U2_FORMAT) {
byte b2 = bytes[loc+1];
value = ((b1 & 0xFF) << 8) + (b2 & 0xFF);
} else {
value = (b1 & 0xFF);
}
// Stored loc field is difference between its own loc and next loc.
return value + (loc << LOC_SHIFT);
}
boolean storeDesc(int loc, int fmt, int desc) {
if (bytes == null)
return false;
int value = desc - (loc << LOC_SHIFT);
byte b1, b2;
switch (fmt) {
case U2_FORMAT:
assert(bytes[loc+0] == UNUSED_BYTE);
assert(bytes[loc+1] == UNUSED_BYTE);
b1 = (byte)(value >> 8);
b2 = (byte)(value >> 0);
if (value == (value & 0xFFFF) && b1 != OVERFLOW_BYTE) {
bytes[loc+0] = b1;
bytes[loc+1] = b2;
assert(fetchDesc(loc, fmt) == desc);
return true;
}
break;
case U1_FORMAT:
assert(bytes[loc] == UNUSED_BYTE);
b1 = (byte)value;
if (value == (value & 0xFF) && b1 != OVERFLOW_BYTE) {
bytes[loc] = b1;
assert(fetchDesc(loc, fmt) == desc);
return true;
}
break;
default: assert(false);
}
// Failure. Caller must allocate a bigDesc.
bytes[loc] = OVERFLOW_BYTE;
assert(fmt==U1_FORMAT || (bytes[loc+1]=(byte)bigDescs[BIGSIZE])!=999);
return false;
}
void storeIndex(int loc, int fmt, int value) {
storeIndex(bytes, loc, fmt, value);
}
static
void storeIndex(byte[] bytes, int loc, int fmt, int value) {
switch (fmt) {
case U2_FORMAT:
assert(value == (value & 0xFFFF)) : (value);
bytes[loc+0] = (byte)(value >> 8);
bytes[loc+1] = (byte)(value >> 0);
break;
case U1_FORMAT:
assert(value == (value & 0xFF)) : (value);
bytes[loc] = (byte)value;
break;
default: assert(false);
}
}
void addU1(int pc, Entry ref) {
add(pc, U1_FORMAT, ref);
}
void addU2(int pc, Entry ref) {
add(pc, U2_FORMAT, ref);
}
/** Simple and necessary tuple to present each fixup. */
public static
class Fixup implements Comparable<Fixup> {
int desc; // location and format of reloc
Entry entry; // which entry to plug into the bytes
Fixup(int desc, Entry entry) {
this.desc = desc;
this.entry = entry;
}
public Fixup(int loc, int fmt, Entry entry) {
this.desc = makeDesc(loc, fmt);
this.entry = entry;
}
public int location() { return descLoc(desc); }
public int format() { return descFmt(desc); }
public Entry entry() { return entry; }
@Override
public int compareTo(Fixup that) {
// Ordering depends only on location.
return this.location() - that.location();
}
@Override
public boolean equals(Object x) {
if (!(x instanceof Fixup)) return false;
Fixup that = (Fixup) x;
return this.desc == that.desc && this.entry == that.entry;
}
@Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + this.desc;
hash = 59 * hash + Objects.hashCode(this.entry);
return hash;
}
@Override
public String toString() {
return "@"+location()+(format()==U1_FORMAT?".1":"")+"="+entry;
}
}
private
class Itr implements Iterator<Fixup> {
int index = 0; // index into entries
int bigIndex = BIGSIZE+1; // index into bigDescs
int next = head; // desc pointing to next fixup
@Override
public boolean hasNext() { return index < size; }
@Override
public void remove() { throw new UnsupportedOperationException(); }
@Override
public Fixup next() {
int thisIndex = index;
return new Fixup(nextDesc(), entries[thisIndex]);
}
int nextDesc() {
index++;
int thisDesc = next;
if (index < size) {
// Fetch next desc eagerly, in case this fixup gets finalized.
int loc = descLoc(thisDesc);
int fmt = descFmt(thisDesc);
if (bytes != null && bytes[loc] != OVERFLOW_BYTE) {
next = fetchDesc(loc, fmt);
} else {
// The unused extra byte is "asserted" to be equal to BI.
// This helps keep the overflow descs in sync.
assert(fmt==U1_FORMAT || bytes == null || bytes[loc+1]==(byte)bigIndex);
next = bigDescs[bigIndex++];
}
}
return thisDesc;
}
}
@Override
public Iterator<Fixup> iterator() {
return new Itr();
}
public void add(int location, int format, Entry entry) {
addDesc(makeDesc(location, format), entry);
}
@Override
public boolean add(Fixup f) {
addDesc(f.desc, f.entry);
return true;
}
@Override
public boolean addAll(Collection<? extends Fixup> c) {
if (c instanceof Fixups) {
// Use knowledge of Itr structure to avoid building little structs.
Fixups that = (Fixups) c;
if (that.size == 0) return false;
if (this.size == 0 && entries.length < that.size)
growEntries(that.size); // presize exactly
Entry[] thatEntries = that.entries;
for (Itr i = that.new Itr(); i.hasNext(); ) {
int ni = i.index;
addDesc(i.nextDesc(), thatEntries[ni]);
}
return true;
} else {
return super.addAll(c);
}
}
// Here is how things get added:
private void addDesc(int thisDesc, Entry entry) {
if (entries.length == size)
growEntries(size * 2);
entries[size] = entry;
if (size == 0) {
head = tail = thisDesc;
} else {
int prevDesc = tail;
// Store new desc in previous tail.
int prevLoc = descLoc(prevDesc);
int prevFmt = descFmt(prevDesc);
int prevLen = fmtLen(prevFmt);
int thisLoc = descLoc(thisDesc);
// The collection must go in ascending order, and not overlap.
if (thisLoc < prevLoc + prevLen)
badOverlap(thisLoc);
tail = thisDesc;
if (!storeDesc(prevLoc, prevFmt, thisDesc)) {
// overflow
int bigSize = bigDescs[BIGSIZE];
if (bigDescs.length == bigSize)
growBigDescs();
//System.out.println("bigDescs["+bigSize+"] = "+thisDesc);
bigDescs[bigSize++] = thisDesc;
bigDescs[BIGSIZE] = bigSize;
}
}
size += 1;
}
private void badOverlap(int thisLoc) {
throw new IllegalArgumentException("locs must be ascending and must not overlap: "+thisLoc+" >> "+this);
}
private void growEntries(int newSize) {
Entry[] oldEntries = entries;
entries = new Entry[Math.max(3, newSize)];
System.arraycopy(oldEntries, 0, entries, 0, oldEntries.length);
}
private void growBigDescs() {
int[] oldBigDescs = bigDescs;
bigDescs = new int[oldBigDescs.length * 2];
System.arraycopy(oldBigDescs, 0, bigDescs, 0, oldBigDescs.length);
}
/// Static methods that optimize the use of this class.
static Object addRefWithBytes(Object f, byte[] bytes, Entry e) {
return add(f, bytes, 0, U2_FORMAT, e);
}
static Object addRefWithLoc(Object f, int loc, Entry entry) {
return add(f, null, loc, U2_FORMAT, entry);
}
private static
Object add(Object prevFixups,
byte[] bytes, int loc, int fmt,
Entry e) {
Fixups f;
if (prevFixups == null) {
if (loc == SPECIAL_LOC && fmt == SPECIAL_FMT) {
// Special convention: If the attribute has a
// U2 relocation at position zero, store the Entry
// rather than building a Fixups structure.
return e;
}
f = new Fixups(bytes);
} else if (!(prevFixups instanceof Fixups)) {
// Recognize the special convention:
Entry firstEntry = (Entry) prevFixups;
f = new Fixups(bytes);
f.add(SPECIAL_LOC, SPECIAL_FMT, firstEntry);
} else {
f = (Fixups) prevFixups;
assert(f.bytes == bytes);
}
f.add(loc, fmt, e);
return f;
}
public static
void setBytes(Object fixups, byte[] bytes) {
if (fixups instanceof Fixups) {
Fixups f = (Fixups) fixups;
f.setBytes(bytes);
}
}
public static
Object trimToSize(Object fixups) {
if (fixups instanceof Fixups) {
Fixups f = (Fixups) fixups;
f.trimToSize();
if (f.size() == 0)
fixups = null;
}
return fixups;
}
// Iterate over all the references in this set of fixups.
public static
void visitRefs(Object fixups, Collection<Entry> refs) {
if (fixups == null) {
} else if (!(fixups instanceof Fixups)) {
// Special convention; see above.
refs.add((Entry) fixups);
} else {
Fixups f = (Fixups) fixups;
f.visitRefs(refs);
}
}
// Clear out this set of fixups by replacing each reference
// by a hardcoded coding of its reference, drawn from ix.
public static
void finishRefs(Object fixups, byte[] bytes, ConstantPool.Index ix) {
if (fixups == null)
return;
if (!(fixups instanceof Fixups)) {
// Special convention; see above.
int index = ix.indexOf((Entry) fixups);
storeIndex(bytes, SPECIAL_LOC, SPECIAL_FMT, index);
return;
}
Fixups f = (Fixups) fixups;
assert(f.bytes == bytes);
f.finishRefs(ix);
}
void finishRefs(ConstantPool.Index ix) {
if (isEmpty())
return;
for (Fixup fx : this) {
int index = ix.indexOf(fx.entry);
//System.out.println("finish "+fx+" = "+index);
// Note that the iterator has already fetched the
// bytes we are about to overwrite.
storeIndex(fx.location(), fx.format(), index);
}
// Further iterations should do nothing:
bytes = null; // do not clean them
clear();
}
/*
/// Testing.
public static void main(String[] av) {
byte[] bytes = new byte[1 << 20];
ConstantPool cp = new ConstantPool();
Fixups f = new Fixups(bytes);
boolean isU1 = false;
int span = 3;
int nextLoc = 0;
int[] locs = new int[100];
final int[] indexes = new int[100];
int iptr = 1;
for (int loc = 0; loc < bytes.length; loc++) {
if (loc == nextLoc && loc+1 < bytes.length) {
int fmt = (isU1 ? U1_FORMAT : U2_FORMAT);
Entry e = ConstantPool.getUtf8Entry("L"+loc);
f.add(loc, fmt, e);
isU1 ^= true;
if (iptr < 10) {
// Make it close in.
nextLoc += fmtLen(fmt) + (iptr < 5 ? 0 : 1);
} else {
nextLoc += span;
span = (int)(span * 1.77);
}
// Here are the bytes that would have gone here:
locs[iptr] = loc;
if (fmt == U1_FORMAT) {
indexes[iptr++] = (loc & 0xFF);
} else {
indexes[iptr++] = ((loc & 0xFF) << 8) | ((loc+1) & 0xFF);
++loc; // skip a byte
}
continue;
}
bytes[loc] = (byte)loc;
}
System.out.println("size="+f.size()
+" overflow="+(f.bigDescs[BIGSIZE]-1));
System.out.println("Fixups: "+f);
// Test collection contents.
assert(iptr == 1+f.size());
List l = new ArrayList(f);
Collections.sort(l); // should not change the order
if (!l.equals(new ArrayList(f))) System.out.println("** disordered");
f.setBytes(null);
if (!l.equals(new ArrayList(f))) System.out.println("** bad set 1");
f.setBytes(bytes);
if (!l.equals(new ArrayList(f))) System.out.println("** bad set 2");
Fixups f3 = new Fixups(f);
if (!l.equals(new ArrayList(f3))) System.out.println("** bad set 3");
Iterator fi = f.iterator();
for (int i = 1; i < iptr; i++) {
Fixup fx = (Fixup) fi.next();
if (fx.location() != locs[i]) {
System.out.println("** "+fx+" != "+locs[i]);
}
if (fx.format() == U1_FORMAT)
System.out.println(fx+" -> "+bytes[locs[i]]);
else
System.out.println(fx+" -> "+bytes[locs[i]]+" "+bytes[locs[i]+1]);
}
assert(!fi.hasNext());
indexes[0] = 1; // like iptr
Index ix = new Index("ix") {
public int indexOf(Entry e) {
return indexes[indexes[0]++];
}
};
f.finishRefs(ix);
for (int loc = 0; loc < bytes.length; loc++) {
if (bytes[loc] != (byte)loc) {
System.out.println("** ["+loc+"] = "+bytes[loc]+" != "+(byte)loc);
}
}
}
//*/
}

View file

@ -1,818 +0,0 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.util.jar.pack;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Arrays;
/**
* Histogram derived from an integer array of events (int[]).
* @author John Rose
*/
final class Histogram {
// Compact histogram representation: 4 bytes per distinct value,
// plus 5 words per distinct count.
protected final int[][] matrix; // multi-row matrix {{counti,valueij...}}
protected final int totalWeight; // sum of all counts
// These are created eagerly also, since that saves work.
// They cost another 8 bytes per distinct value.
protected final int[] values; // unique values, sorted by value
protected final int[] counts; // counts, same order as values
private static final long LOW32 = (long)-1 >>> 32;
/** Build a histogram given a sequence of values.
* To save work, the input should be sorted, but need not be.
*/
public
Histogram(int[] valueSequence) {
long[] hist2col = computeHistogram2Col(maybeSort(valueSequence));
int[][] table = makeTable(hist2col);
values = table[0];
counts = table[1];
this.matrix = makeMatrix(hist2col);
this.totalWeight = valueSequence.length;
assert(assertWellFormed(valueSequence));
}
public
Histogram(int[] valueSequence, int start, int end) {
this(sortedSlice(valueSequence, start, end));
}
/** Build a histogram given a compact matrix of counts and values. */
public
Histogram(int[][] matrix) {
// sort the rows
matrix = normalizeMatrix(matrix); // clone and sort
this.matrix = matrix;
int length = 0;
int weight = 0;
for (int i = 0; i < matrix.length; i++) {
int rowLength = matrix[i].length-1;
length += rowLength;
weight += matrix[i][0] * rowLength;
}
this.totalWeight = weight;
long[] hist2col = new long[length];
int fillp = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 1; j < matrix[i].length; j++) {
// sort key is value, so put it in the high 32!
hist2col[fillp++] = ((long) matrix[i][j] << 32)
| (LOW32 & matrix[i][0]);
}
}
assert(fillp == hist2col.length);
Arrays.sort(hist2col);
int[][] table = makeTable(hist2col);
values = table[1]; //backwards
counts = table[0]; //backwards
assert(assertWellFormed(null));
}
/** Histogram of int values, reported compactly as a ragged matrix,
* indexed by descending frequency rank.
* <p>
* Format of matrix:
* Each row in the matrix begins with an occurrence count,
* and continues with all int values that occur at that frequency.
* <pre>
* int[][] matrix = {
* { count1, value11, value12, value13, ... },
* { count2, value21, value22, ... },
* ...
* }
* </pre>
* The first column of the matrix { count1, count2, ... }
* is sorted in descending order, and contains no duplicates.
* Each row of the matrix (apart from its first element)
* is sorted in ascending order, and contains no duplicates.
* That is, each sequence { valuei1, valuei2, ... } is sorted.
*/
public
int[][] getMatrix() { return matrix; }
public
int getRowCount() { return matrix.length; }
public
int getRowFrequency(int rn) { return matrix[rn][0]; }
public
int getRowLength(int rn) { return matrix[rn].length-1; }
public
int getRowValue(int rn, int vn) { return matrix[rn][vn+1]; }
public
int getRowWeight(int rn) {
return getRowFrequency(rn) * getRowLength(rn);
}
public
int getTotalWeight() {
return totalWeight;
}
public
int getTotalLength() {
return values.length;
}
/** Returns an array of all values, sorted. */
public
int[] getAllValues() {
return values;
}
/** Returns an array parallel with {@link #getValues},
* with a frequency for each value.
*/
public
int[] getAllFrequencies() {
return counts;
}
private static double log2 = Math.log(2);
public
int getFrequency(int value) {
int pos = Arrays.binarySearch(values, value);
if (pos < 0) return 0;
assert(values[pos] == value);
return counts[pos];
}
public
double getBitLength(int value) {
double prob = (double) getFrequency(value) / getTotalWeight();
return - Math.log(prob) / log2;
}
public
double getRowBitLength(int rn) {
double prob = (double) getRowFrequency(rn) / getTotalWeight();
return - Math.log(prob) / log2;
}
public
interface BitMetric {
public double getBitLength(int value);
}
private final BitMetric bitMetric = new BitMetric() {
public double getBitLength(int value) {
return Histogram.this.getBitLength(value);
}
};
public BitMetric getBitMetric() {
return bitMetric;
}
/** bit-length is negative entropy: -H(matrix). */
public
double getBitLength() {
double sum = 0;
for (int i = 0; i < matrix.length; i++) {
sum += getRowBitLength(i) * getRowWeight(i);
}
assert(0.1 > Math.abs(sum - getBitLength(bitMetric)));
return sum;
}
/** bit-length in to another coding (cross-entropy) */
public
double getBitLength(BitMetric len) {
double sum = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 1; j < matrix[i].length; j++) {
sum += matrix[i][0] * len.getBitLength(matrix[i][j]);
}
}
return sum;
}
private static
double round(double x, double scale) {
return Math.round(x * scale) / scale;
}
/** Sort rows and columns.
* Merge adjacent rows with the same key element [0].
* Make a fresh copy of all of it.
*/
public int[][] normalizeMatrix(int[][] matrix) {
long[] rowMap = new long[matrix.length];
for (int i = 0; i < matrix.length; i++) {
if (matrix[i].length <= 1) continue;
int count = matrix[i][0];
if (count <= 0) continue;
rowMap[i] = (long) count << 32 | i;
}
Arrays.sort(rowMap);
int[][] newMatrix = new int[matrix.length][];
int prevCount = -1;
int fillp1 = 0;
int fillp2 = 0;
for (int i = 0; ; i++) {
int[] row;
if (i < matrix.length) {
long rowMapEntry = rowMap[rowMap.length-i-1];
if (rowMapEntry == 0) continue;
row = matrix[(int)rowMapEntry];
assert(rowMapEntry>>>32 == row[0]);
} else {
row = new int[]{ -1 }; // close it off
}
if (row[0] != prevCount && fillp2 > fillp1) {
// Close off previous run.
int length = 0;
for (int p = fillp1; p < fillp2; p++) {
int[] row0 = newMatrix[p]; // previously visited row
assert(row0[0] == prevCount);
length += row0.length-1;
}
int[] row1 = new int[1+length]; // cloned & consolidated row
row1[0] = prevCount;
int rfillp = 1;
for (int p = fillp1; p < fillp2; p++) {
int[] row0 = newMatrix[p]; // previously visited row
assert(row0[0] == prevCount);
System.arraycopy(row0, 1, row1, rfillp, row0.length-1);
rfillp += row0.length-1;
}
if (!isSorted(row1, 1, true)) {
Arrays.sort(row1, 1, row1.length);
int jfillp = 2;
// Detect and squeeze out duplicates.
for (int j = 2; j < row1.length; j++) {
if (row1[j] != row1[j-1])
row1[jfillp++] = row1[j];
}
if (jfillp < row1.length) {
// Reallocate because of lost duplicates.
int[] newRow1 = new int[jfillp];
System.arraycopy(row1, 0, newRow1, 0, jfillp);
row1 = newRow1;
}
}
newMatrix[fillp1++] = row1;
fillp2 = fillp1;
}
if (i == matrix.length)
break;
prevCount = row[0];
newMatrix[fillp2++] = row;
}
assert(fillp1 == fillp2); // no unfinished business
// Now drop missing rows.
matrix = newMatrix;
if (fillp1 < matrix.length) {
newMatrix = new int[fillp1][];
System.arraycopy(matrix, 0, newMatrix, 0, fillp1);
matrix = newMatrix;
}
return matrix;
}
public
String[] getRowTitles(String name) {
int totalUnique = getTotalLength();
int ltotalWeight = getTotalWeight();
String[] histTitles = new String[matrix.length];
int cumWeight = 0;
int cumUnique = 0;
for (int i = 0; i < matrix.length; i++) {
int count = getRowFrequency(i);
int unique = getRowLength(i);
int weight = getRowWeight(i);
cumWeight += weight;
cumUnique += unique;
long wpct = ((long)cumWeight * 100 + ltotalWeight/2) / ltotalWeight;
long upct = ((long)cumUnique * 100 + totalUnique/2) / totalUnique;
double len = getRowBitLength(i);
assert(0.1 > Math.abs(len - getBitLength(matrix[i][1])));
histTitles[i] = name+"["+i+"]"
+" len="+round(len,10)
+" ("+count+"*["+unique+"])"
+" ("+cumWeight+":"+wpct+"%)"
+" ["+cumUnique+":"+upct+"%]";
}
return histTitles;
}
/** Print a report of this histogram.
*/
public
void print(PrintStream out) {
print("hist", out);
}
/** Print a report of this histogram.
*/
public
void print(String name, PrintStream out) {
print(name, getRowTitles(name), out);
}
/** Print a report of this histogram.
*/
public
void print(String name, String[] histTitles, PrintStream out) {
int totalUnique = getTotalLength();
int ltotalWeight = getTotalWeight();
double tlen = getBitLength();
double avgLen = tlen / ltotalWeight;
double avg = (double) ltotalWeight / totalUnique;
String title = (name
+" len="+round(tlen,10)
+" avgLen="+round(avgLen,10)
+" weight("+ltotalWeight+")"
+" unique["+totalUnique+"]"
+" avgWeight("+round(avg,100)+")");
if (histTitles == null) {
out.println(title);
} else {
out.println(title+" {");
StringBuffer buf = new StringBuffer();
for (int i = 0; i < matrix.length; i++) {
buf.setLength(0);
buf.append(" ").append(histTitles[i]).append(" {");
for (int j = 1; j < matrix[i].length; j++) {
buf.append(" ").append(matrix[i][j]);
}
buf.append(" }");
out.println(buf);
}
out.println("}");
}
}
/*
public static
int[][] makeHistogramMatrix(int[] values) {
// Make sure they are sorted.
values = maybeSort(values);
long[] hist2col = computeHistogram2Col(values);
int[][] matrix = makeMatrix(hist2col);
return matrix;
}
*/
private static
int[][] makeMatrix(long[] hist2col) {
// Sort by increasing count, then by increasing value.
Arrays.sort(hist2col);
int[] counts = new int[hist2col.length];
for (int i = 0; i < counts.length; i++) {
counts[i] = (int)( hist2col[i] >>> 32 );
}
long[] countHist = computeHistogram2Col(counts);
int[][] matrix = new int[countHist.length][];
int histp = 0; // cursor into hist2col (increasing count, value)
int countp = 0; // cursor into countHist (increasing count)
// Do a join between hist2col (resorted) and countHist.
for (int i = matrix.length; --i >= 0; ) {
long countAndRep = countHist[countp++];
int count = (int) (countAndRep); // what is the value count?
int repeat = (int) (countAndRep >>> 32); // # times repeated?
int[] row = new int[1+repeat];
row[0] = count;
for (int j = 0; j < repeat; j++) {
long countAndValue = hist2col[histp++];
assert(countAndValue >>> 32 == count);
row[1+j] = (int) countAndValue;
}
matrix[i] = row;
}
assert(histp == hist2col.length);
return matrix;
}
private static
int[][] makeTable(long[] hist2col) {
int[][] table = new int[2][hist2col.length];
// Break apart the entries in hist2col.
// table[0] gets values, table[1] gets entries.
for (int i = 0; i < hist2col.length; i++) {
table[0][i] = (int)( hist2col[i] );
table[1][i] = (int)( hist2col[i] >>> 32 );
}
return table;
}
/** Simple two-column histogram. Contains repeated counts.
* Assumes input is sorted. Does not sort output columns.
* <p>
* Format of result:
* <pre>
* long[] hist = {
* (count1 << 32) | (value1),
* (count2 << 32) | (value2),
* ...
* }
* </pre>
* In addition, the sequence {valuei...} is guaranteed to be sorted.
* Note that resorting this using Arrays.sort() will reorder the
* entries by increasing count.
*/
private static
long[] computeHistogram2Col(int[] sortedValues) {
switch (sortedValues.length) {
case 0:
return new long[]{ };
case 1:
return new long[]{ ((long)1 << 32) | (LOW32 & sortedValues[0]) };
}
long[] hist = null;
for (boolean sizeOnly = true; ; sizeOnly = false) {
int prevIndex = -1;
int prevValue = sortedValues[0] ^ -1; // force a difference
int prevCount = 0;
for (int i = 0; i <= sortedValues.length; i++) {
int thisValue;
if (i < sortedValues.length)
thisValue = sortedValues[i];
else
thisValue = prevValue ^ -1; // force a difference at end
if (thisValue == prevValue) {
prevCount += 1;
} else {
// Found a new value.
if (!sizeOnly && prevCount != 0) {
// Save away previous value.
hist[prevIndex] = ((long)prevCount << 32)
| (LOW32 & prevValue);
}
prevValue = thisValue;
prevCount = 1;
prevIndex += 1;
}
}
if (sizeOnly) {
// Finished the sizing pass. Allocate the histogram.
hist = new long[prevIndex];
} else {
break; // done
}
}
return hist;
}
/** Regroup the histogram, so that it becomes an approximate histogram
* whose rows are of the given lengths.
* If matrix rows must be split, the latter parts (larger values)
* are placed earlier in the new matrix.
* If matrix rows are joined, they are resorted into ascending order.
* In the new histogram, the counts are averaged over row entries.
*/
private static
int[][] regroupHistogram(int[][] matrix, int[] groups) {
long oldEntries = 0;
for (int i = 0; i < matrix.length; i++) {
oldEntries += matrix[i].length-1;
}
long newEntries = 0;
for (int ni = 0; ni < groups.length; ni++) {
newEntries += groups[ni];
}
if (newEntries > oldEntries) {
int newlen = groups.length;
long ok = oldEntries;
for (int ni = 0; ni < groups.length; ni++) {
if (ok < groups[ni]) {
int[] newGroups = new int[ni+1];
System.arraycopy(groups, 0, newGroups, 0, ni+1);
groups = newGroups;
groups[ni] = (int) ok;
ok = 0;
break;
}
ok -= groups[ni];
}
} else {
long excess = oldEntries - newEntries;
int[] newGroups = new int[groups.length+1];
System.arraycopy(groups, 0, newGroups, 0, groups.length);
newGroups[groups.length] = (int) excess;
groups = newGroups;
}
int[][] newMatrix = new int[groups.length][];
// Fill pointers.
int i = 0; // into matrix
int jMin = 1;
int jMax = matrix[i].length;
for (int ni = 0; ni < groups.length; ni++) {
int groupLength = groups[ni];
int[] group = new int[1+groupLength];
long groupWeight = 0; // count of all in new group
newMatrix[ni] = group;
int njFill = 1;
while (njFill < group.length) {
int len = group.length - njFill;
while (jMin == jMax) {
jMin = 1;
jMax = matrix[++i].length;
}
if (len > jMax - jMin) len = jMax - jMin;
groupWeight += (long) matrix[i][0] * len;
System.arraycopy(matrix[i], jMax - len, group, njFill, len);
jMax -= len;
njFill += len;
}
Arrays.sort(group, 1, group.length);
// compute average count of new group:
group[0] = (int) ((groupWeight + groupLength/2) / groupLength);
}
assert(jMin == jMax);
assert(i == matrix.length-1);
return newMatrix;
}
public static
Histogram makeByteHistogram(InputStream bytes) throws IOException {
byte[] buf = new byte[1<<12];
int[] tally = new int[1<<8];
for (int nr; (nr = bytes.read(buf)) > 0; ) {
for (int i = 0; i < nr; i++) {
tally[buf[i] & 0xFF] += 1;
}
}
// Build a matrix.
int[][] matrix = new int[1<<8][2];
for (int i = 0; i < tally.length; i++) {
matrix[i][0] = tally[i];
matrix[i][1] = i;
}
return new Histogram(matrix);
}
/** Slice and sort the given input array. */
private static
int[] sortedSlice(int[] valueSequence, int start, int end) {
if (start == 0 && end == valueSequence.length &&
isSorted(valueSequence, 0, false)) {
return valueSequence;
} else {
int[] slice = new int[end-start];
System.arraycopy(valueSequence, start, slice, 0, slice.length);
Arrays.sort(slice);
return slice;
}
}
/** Tell if an array is sorted. */
private static
boolean isSorted(int[] values, int from, boolean strict) {
for (int i = from+1; i < values.length; i++) {
if (strict ? !(values[i-1] < values[i])
: !(values[i-1] <= values[i])) {
return false; // found witness to disorder
}
}
return true; // no witness => sorted
}
/** Clone and sort the array, if not already sorted. */
private static
int[] maybeSort(int[] values) {
if (!isSorted(values, 0, false)) {
values = values.clone();
Arrays.sort(values);
}
return values;
}
/// Debug stuff follows.
private boolean assertWellFormed(int[] valueSequence) {
/*
// Sanity check.
int weight = 0;
int vlength = 0;
for (int i = 0; i < matrix.length; i++) {
int vlengthi = (matrix[i].length-1);
int count = matrix[i][0];
assert(vlengthi > 0); // no empty rows
assert(count > 0); // no impossible rows
vlength += vlengthi;
weight += count * vlengthi;
}
assert(isSorted(values, 0, true));
// make sure the counts all add up
assert(totalWeight == weight);
assert(vlength == values.length);
assert(vlength == counts.length);
int weight2 = 0;
for (int i = 0; i < counts.length; i++) {
weight2 += counts[i];
}
assert(weight2 == weight);
int[] revcol1 = new int[matrix.length]; //1st matrix colunm
for (int i = 0; i < matrix.length; i++) {
// spot checking: try a random query on each matrix row
assert(matrix[i].length > 1);
revcol1[matrix.length-i-1] = matrix[i][0];
assert(isSorted(matrix[i], 1, true));
int rand = (matrix[i].length+1) / 2;
int val = matrix[i][rand];
int count = matrix[i][0];
int pos = Arrays.binarySearch(values, val);
assert(values[pos] == val);
assert(counts[pos] == matrix[i][0]);
if (valueSequence != null) {
int count2 = 0;
for (int j = 0; j < valueSequence.length; j++) {
if (valueSequence[j] == val) count2++;
}
assert(count2 == count);
}
}
assert(isSorted(revcol1, 0, true));
//*/
return true;
}
/*
public static
int[] readValuesFrom(InputStream instr) {
return readValuesFrom(new InputStreamReader(instr));
}
public static
int[] readValuesFrom(Reader inrdr) {
inrdr = new BufferedReader(inrdr);
final StreamTokenizer in = new StreamTokenizer(inrdr);
final int TT_NOTHING = -99;
in.commentChar('#');
return readValuesFrom(new Iterator() {
int token = TT_NOTHING;
private int getToken() {
if (token == TT_NOTHING) {
try {
token = in.nextToken();
assert(token != TT_NOTHING);
} catch (IOException ee) {
throw new RuntimeException(ee);
}
}
return token;
}
public boolean hasNext() {
return getToken() != StreamTokenizer.TT_EOF;
}
public Object next() {
int ntok = getToken();
token = TT_NOTHING;
switch (ntok) {
case StreamTokenizer.TT_EOF:
throw new NoSuchElementException();
case StreamTokenizer.TT_NUMBER:
return new Integer((int) in.nval);
default:
assert(false);
return null;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
});
}
public static
int[] readValuesFrom(Iterator iter) {
return readValuesFrom(iter, 0);
}
public static
int[] readValuesFrom(Iterator iter, int initSize) {
int[] na = new int[Math.max(10, initSize)];
int np = 0;
while (iter.hasNext()) {
Integer val = (Integer) iter.next();
if (np == na.length) {
int[] na2 = new int[np*2];
System.arraycopy(na, 0, na2, 0, np);
na = na2;
}
na[np++] = val.intValue();
}
if (np != na.length) {
int[] na2 = new int[np];
System.arraycopy(na, 0, na2, 0, np);
na = na2;
}
return na;
}
public static
Histogram makeByteHistogram(byte[] bytes) {
try {
return makeByteHistogram(new ByteArrayInputStream(bytes));
} catch (IOException ee) {
throw new RuntimeException(ee);
}
}
public static
void main(String[] av) throws IOException {
if (av.length > 0 && av[0].equals("-r")) {
int[] values = new int[Integer.parseInt(av[1])];
int limit = values.length;
if (av.length >= 3) {
limit = (int)( limit * Double.parseDouble(av[2]) );
}
Random rnd = new Random();
for (int i = 0; i < values.length; i++) {
values[i] = rnd.nextInt(limit);;
}
Histogram rh = new Histogram(values);
rh.print("random", System.out);
return;
}
if (av.length > 0 && av[0].equals("-s")) {
int[] values = readValuesFrom(System.in);
Random rnd = new Random();
for (int i = values.length; --i > 0; ) {
int j = rnd.nextInt(i+1);
if (j < i) {
int tem = values[i];
values[i] = values[j];
values[j] = tem;
}
}
for (int i = 0; i < values.length; i++)
System.out.println(values[i]);
return;
}
if (av.length > 0 && av[0].equals("-e")) {
// edge cases
new Histogram(new int[][] {
{1, 11, 111},
{0, 123, 456},
{1, 111, 1111},
{0, 456, 123},
{3},
{},
{3},
{2, 22},
{4}
}).print(System.out);
return;
}
if (av.length > 0 && av[0].equals("-b")) {
// edge cases
Histogram bh = makeByteHistogram(System.in);
bh.print("bytes", System.out);
return;
}
boolean regroup = false;
if (av.length > 0 && av[0].equals("-g")) {
regroup = true;
}
int[] values = readValuesFrom(System.in);
Histogram h = new Histogram(values);
if (!regroup)
h.print(System.out);
if (regroup) {
int[] groups = new int[12];
for (int i = 0; i < groups.length; i++) {
groups[i] = 1<<i;
}
int[][] gm = regroupHistogram(h.getMatrix(), groups);
Histogram g = new Histogram(gm);
System.out.println("h.getBitLength(g) = "+
h.getBitLength(g.getBitMetric()));
System.out.println("g.getBitLength(h) = "+
g.getBitLength(h.getBitMetric()));
g.print("regrouped", System.out);
}
}
//*/
}

View file

@ -1,689 +0,0 @@
/*
* Copyright (c) 2001, 2019, 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 java.io.IOException;
import java.util.Arrays;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* A parsed bytecode instruction.
* Provides accessors to various relevant bits.
* @author John Rose
*/
class Instruction {
protected byte[] bytes; // bytecodes
protected int pc; // location of this instruction
protected int bc; // opcode of this instruction
protected int w; // 0 if normal, 1 if a _wide prefix at pc
protected int length; // bytes in this instruction
protected boolean special;
protected Instruction(byte[] bytes, int pc, int bc, int w, int length) {
reset(bytes, pc, bc, w, length);
}
private void reset(byte[] bytes, int pc, int bc, int w, int length) {
this.bytes = bytes;
this.pc = pc;
this.bc = bc;
this.w = w;
this.length = length;
}
public int getBC() {
return bc;
}
public boolean isWide() {
return w != 0;
}
public byte[] getBytes() {
return bytes;
}
public int getPC() {
return pc;
}
public int getLength() {
return length;
}
public int getNextPC() {
return pc + length;
}
public Instruction next() {
int npc = pc + length;
if (npc == bytes.length)
return null;
else
return Instruction.at(bytes, npc, this);
}
public boolean isNonstandard() {
return isNonstandard(bc);
}
public void setNonstandardLength(int length) {
assert(isNonstandard());
this.length = length;
}
/** A fake instruction at this pc whose next() will be at nextpc. */
public Instruction forceNextPC(int nextpc) {
int llength = nextpc - pc;
return new Instruction(bytes, pc, -1, -1, llength);
}
public static Instruction at(byte[] bytes, int pc) {
return Instruction.at(bytes, pc, null);
}
public static Instruction at(byte[] bytes, int pc, Instruction reuse) {
int bc = getByte(bytes, pc);
int prefix = -1;
int w = 0;
int length = BC_LENGTH[w][bc];
if (length == 0) {
// Hard cases:
switch (bc) {
case _wide:
bc = getByte(bytes, pc+1);
w = 1;
length = BC_LENGTH[w][bc];
if (length == 0) {
// unknown instruction; treat as one byte
length = 1;
}
break;
case _tableswitch:
return new TableSwitch(bytes, pc);
case _lookupswitch:
return new LookupSwitch(bytes, pc);
default:
// unknown instruction; treat as one byte
length = 1;
break;
}
}
assert(length > 0);
assert(pc+length <= bytes.length);
// Speed hack: Instruction.next reuses self if possible.
if (reuse != null && !reuse.special) {
reuse.reset(bytes, pc, bc, w, length);
return reuse;
}
return new Instruction(bytes, pc, bc, w, length);
}
// Return the constant pool reference type, or 0 if none.
public byte getCPTag() {
return BC_TAG[w][bc];
}
// Return the constant pool index, or -1 if none.
public int getCPIndex() {
int indexLoc = BC_INDEX[w][bc];
if (indexLoc == 0) return -1;
assert(w == 0);
if (length == 2)
return getByte(bytes, pc+indexLoc); // _ldc opcode only
else
return getShort(bytes, pc+indexLoc);
}
public void setCPIndex(int cpi) {
int indexLoc = BC_INDEX[w][bc];
assert(indexLoc != 0);
if (length == 2)
setByte(bytes, pc+indexLoc, cpi); // _ldc opcode only
else
setShort(bytes, pc+indexLoc, cpi);
assert(getCPIndex() == cpi);
}
public ConstantPool.Entry getCPRef(ConstantPool.Entry[] cpMap) {
int index = getCPIndex();
return (index < 0) ? null : cpMap[index];
}
// Return the slot of the affected local, or -1 if none.
public int getLocalSlot() {
int slotLoc = BC_SLOT[w][bc];
if (slotLoc == 0) return -1;
if (w == 0)
return getByte(bytes, pc+slotLoc);
else
return getShort(bytes, pc+slotLoc);
}
// Return the target of the branch, or -1 if none.
public int getBranchLabel() {
int branchLoc = BC_BRANCH[w][bc];
if (branchLoc == 0) return -1;
assert(w == 0);
assert(length == 3 || length == 5);
int offset;
if (length == 3)
offset = (short)getShort(bytes, pc+branchLoc);
else
offset = getInt(bytes, pc+branchLoc);
assert(offset+pc >= 0);
assert(offset+pc <= bytes.length);
return offset+pc;
}
public void setBranchLabel(int targetPC) {
int branchLoc = BC_BRANCH[w][bc];
assert(branchLoc != 0);
if (length == 3)
setShort(bytes, pc+branchLoc, targetPC-pc);
else
setInt(bytes, pc+branchLoc, targetPC-pc);
assert(targetPC == getBranchLabel());
}
// Return the trailing constant in the instruction (as a signed value).
// Return 0 if there is none.
public int getConstant() {
int conLoc = BC_CON[w][bc];
if (conLoc == 0) return 0;
switch (length - conLoc) {
case 1: return (byte) getByte(bytes, pc+conLoc);
case 2: return (short) getShort(bytes, pc+conLoc);
}
assert(false);
return 0;
}
public void setConstant(int con) {
int conLoc = BC_CON[w][bc];
assert(conLoc != 0);
switch (length - conLoc) {
case 1: setByte(bytes, pc+conLoc, con); break;
case 2: setShort(bytes, pc+conLoc, con); break;
}
assert(con == getConstant());
}
public abstract static class Switch extends Instruction {
// Each case is a (value, label) pair, indexed 0 <= n < caseCount
public abstract int getCaseCount();
public abstract int getCaseValue(int n);
public abstract int getCaseLabel(int n);
public abstract void setCaseCount(int caseCount);
public abstract void setCaseValue(int n, int value);
public abstract void setCaseLabel(int n, int targetPC);
protected abstract int getLength(int caseCount);
public int getDefaultLabel() { return intAt(0)+pc; }
public void setDefaultLabel(int targetPC) { setIntAt(0, targetPC-pc); }
protected int apc; // aligned pc (table base)
protected int intAt(int n) { return getInt(bytes, apc + n*4); }
protected void setIntAt(int n, int x) { setInt(bytes, apc + n*4, x); }
protected Switch(byte[] bytes, int pc, int bc) {
super(bytes, pc, bc, /*w*/0, /*length*/0);
this.apc = alignPC(pc+1);
this.special = true;
length = getLength(getCaseCount());
}
public int getAlignedPC() { return apc; }
public String toString() {
String s = super.toString();
s += " Default:"+labstr(getDefaultLabel());
int caseCount = getCaseCount();
for (int i = 0; i < caseCount; i++) {
s += "\n\tCase "+getCaseValue(i)+":"+labstr(getCaseLabel(i));
}
return s;
}
public static int alignPC(int apc) {
while (apc % 4 != 0) ++apc;
return apc;
}
}
public static class TableSwitch extends Switch {
// apc: (df, lo, hi, (hi-lo+1)*(label))
public int getLowCase() { return intAt(1); }
public int getHighCase() { return intAt(2); }
public int getCaseCount() { return intAt(2)-intAt(1)+1; }
public int getCaseValue(int n) { return getLowCase()+n; }
public int getCaseLabel(int n) { return intAt(3+n)+pc; }
public void setLowCase(int val) { setIntAt(1, val); }
public void setHighCase(int val) { setIntAt(2, val); }
public void setCaseLabel(int n, int tpc) { setIntAt(3+n, tpc-pc); }
public void setCaseCount(int caseCount) {
setHighCase(getLowCase() + caseCount - 1);
length = getLength(caseCount);
}
public void setCaseValue(int n, int val) {
if (n != 0) throw new UnsupportedOperationException();
int caseCount = getCaseCount();
setLowCase(val);
setCaseCount(caseCount); // keep invariant
}
TableSwitch(byte[] bytes, int pc) {
super(bytes, pc, _tableswitch);
}
protected int getLength(int caseCount) {
return (apc-pc) + (3 + caseCount) * 4;
}
}
public static class LookupSwitch extends Switch {
// apc: (df, nc, nc*(case, label))
public int getCaseCount() { return intAt(1); }
public int getCaseValue(int n) { return intAt(2+n*2+0); }
public int getCaseLabel(int n) { return intAt(2+n*2+1)+pc; }
public void setCaseCount(int caseCount) {
setIntAt(1, caseCount);
length = getLength(caseCount);
}
public void setCaseValue(int n, int val) { setIntAt(2+n*2+0, val); }
public void setCaseLabel(int n, int tpc) { setIntAt(2+n*2+1, tpc-pc); }
LookupSwitch(byte[] bytes, int pc) {
super(bytes, pc, _lookupswitch);
}
protected int getLength(int caseCount) {
return (apc-pc) + (2 + caseCount*2) * 4;
}
}
/** Two instructions are equal if they have the same bytes. */
public boolean equals(Object o) {
return (o != null) && (o.getClass() == Instruction.class)
&& equals((Instruction) o);
}
public int hashCode() {
int hash = 3;
hash = 11 * hash + Arrays.hashCode(this.bytes);
hash = 11 * hash + this.pc;
hash = 11 * hash + this.bc;
hash = 11 * hash + this.w;
hash = 11 * hash + this.length;
return hash;
}
public boolean equals(Instruction that) {
if (this.pc != that.pc) return false;
if (this.bc != that.bc) return false;
if (this.w != that.w) return false;
if (this.length != that.length) return false;
for (int i = 1; i < length; i++) {
if (this.bytes[this.pc+i] != that.bytes[that.pc+i])
return false;
}
return true;
}
static String labstr(int pc) {
if (pc >= 0 && pc < 100000)
return ((100000+pc)+"").substring(1);
return pc+"";
}
public String toString() {
return toString(null);
}
public String toString(ConstantPool.Entry[] cpMap) {
String s = labstr(pc) + ": ";
if (bc >= _bytecode_limit) {
s += Integer.toHexString(bc);
return s;
}
if (w == 1) s += "wide ";
String bcname = (bc < BC_NAME.length)? BC_NAME[bc]: null;
if (bcname == null) {
return s+"opcode#"+bc;
}
s += bcname;
int tag = getCPTag();
if (tag != 0) s += " "+ConstantPool.tagName(tag)+":";
int idx = getCPIndex();
if (idx >= 0) s += (cpMap == null) ? ""+idx : "="+cpMap[idx].stringValue();
int slt = getLocalSlot();
if (slt >= 0) s += " Local:"+slt;
int lab = getBranchLabel();
if (lab >= 0) s += " To:"+labstr(lab);
int con = getConstant();
if (con != 0) s += " Con:"+con;
return s;
}
//public static byte constantPoolTagFor(int bc) { return BC_TAG[0][bc]; }
/// Fetching values from byte arrays:
public int getIntAt(int off) {
return getInt(bytes, pc+off);
}
public int getShortAt(int off) {
return getShort(bytes, pc+off);
}
public int getByteAt(int off) {
return getByte(bytes, pc+off);
}
public static int getInt(byte[] bytes, int pc) {
return (getShort(bytes, pc+0) << 16) + (getShort(bytes, pc+2) << 0);
}
public static int getShort(byte[] bytes, int pc) {
return (getByte(bytes, pc+0) << 8) + (getByte(bytes, pc+1) << 0);
}
public static int getByte(byte[] bytes, int pc) {
return bytes[pc] & 0xFF;
}
public static void setInt(byte[] bytes, int pc, int x) {
setShort(bytes, pc+0, x >> 16);
setShort(bytes, pc+2, x >> 0);
}
public static void setShort(byte[] bytes, int pc, int x) {
setByte(bytes, pc+0, x >> 8);
setByte(bytes, pc+1, x >> 0);
}
public static void setByte(byte[] bytes, int pc, int x) {
bytes[pc] = (byte)x;
}
// some bytecode classifiers
public static boolean isNonstandard(int bc) {
return BC_LENGTH[0][bc] < 0;
}
public static int opLength(int bc) {
int l = BC_LENGTH[0][bc];
assert(l > 0);
return l;
}
public static int opWideLength(int bc) {
int l = BC_LENGTH[1][bc];
assert(l > 0);
return l;
}
public static boolean isLocalSlotOp(int bc) {
return (bc < BC_SLOT[0].length && BC_SLOT[0][bc] > 0);
}
public static boolean isBranchOp(int bc) {
return (bc < BC_BRANCH[0].length && BC_BRANCH[0][bc] > 0);
}
public static boolean isCPRefOp(int bc) {
if (bc < BC_INDEX[0].length && BC_INDEX[0][bc] > 0) return true;
if (bc >= _xldc_op && bc < _xldc_limit) return true;
if (bc == _invokespecial_int || bc == _invokestatic_int) return true;
return false;
}
public static byte getCPRefOpTag(int bc) {
if (bc < BC_INDEX[0].length && BC_INDEX[0][bc] > 0) return BC_TAG[0][bc];
if (bc >= _xldc_op && bc < _xldc_limit) return CONSTANT_LoadableValue;
if (bc == _invokestatic_int || bc == _invokespecial_int) return CONSTANT_InterfaceMethodref;
return CONSTANT_None;
}
public static boolean isFieldOp(int bc) {
return (bc >= _getstatic && bc <= _putfield);
}
public static boolean isInvokeInitOp(int bc) {
return (bc >= _invokeinit_op && bc < _invokeinit_limit);
}
public static boolean isSelfLinkerOp(int bc) {
return (bc >= _self_linker_op && bc < _self_linker_limit);
}
/// Format definitions.
private static final byte[][] BC_LENGTH = new byte[2][0x100];
private static final byte[][] BC_INDEX = new byte[2][0x100];
private static final byte[][] BC_TAG = new byte[2][0x100];
private static final byte[][] BC_BRANCH = new byte[2][0x100];
private static final byte[][] BC_SLOT = new byte[2][0x100];
private static final byte[][] BC_CON = new byte[2][0x100];
private static final String[] BC_NAME = new String[0x100]; // debug only
private static final String[][] BC_FORMAT = new String[2][_bytecode_limit]; // debug only
static {
for (int i = 0; i < _bytecode_limit; i++) {
BC_LENGTH[0][i] = -1;
BC_LENGTH[1][i] = -1;
}
def("b", _nop, _dconst_1);
def("bx", _bipush);
def("bxx", _sipush);
def("bk", _ldc); // do not pack
def("bkk", _ldc_w, _ldc2_w); // do not pack
def("blwbll", _iload, _aload);
def("b", _iload_0, _saload);
def("blwbll", _istore, _astore);
def("b", _istore_0, _lxor);
def("blxwbllxx", _iinc);
def("b", _i2l, _dcmpg);
def("boo", _ifeq, _jsr); // pack oo
def("blwbll", _ret);
def("", _tableswitch, _lookupswitch); // pack all ints, omit padding
def("b", _ireturn, _return);
def("bkf", _getstatic, _putfield); // pack kf (base=Field)
def("bkm", _invokevirtual, _invokestatic); // pack kn (base=Method)
def("bkixx", _invokeinterface); // pack ki (base=IMethod), omit xx
def("bkyxx", _invokedynamic); // pack ky (base=Any), omit xx
def("bkc", _new); // pack kc
def("bx", _newarray);
def("bkc", _anewarray); // pack kc
def("b", _arraylength, _athrow);
def("bkc", _checkcast, _instanceof); // pack kc
def("b", _monitorenter, _monitorexit);
def("", _wide);
def("bkcx", _multianewarray); // pack kc
def("boo", _ifnull, _ifnonnull); // pack oo
def("boooo", _goto_w, _jsr_w); // pack oooo
for (int i = 0; i < _bytecode_limit; i++) {
//System.out.println(i+": l="+BC_LENGTH[0][i]+" i="+BC_INDEX[0][i]);
//assert(BC_LENGTH[0][i] != -1);
if (BC_LENGTH[0][i] == -1) {
continue; // unknown opcode
}
// Have a complete mapping, to support spurious _wide prefixes.
if (BC_LENGTH[1][i] == -1)
BC_LENGTH[1][i] = (byte)(1+BC_LENGTH[0][i]);
}
String names =
"nop aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3 iconst_4 "+
"iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2 dconst_0 dconst_1 "+
"bipush sipush ldc ldc_w ldc2_w iload lload fload dload aload iload_0 "+
"iload_1 iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1 "+
"fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1 aload_2 "+
"aload_3 iaload laload faload daload aaload baload caload saload istore "+
"lstore fstore dstore astore istore_0 istore_1 istore_2 istore_3 lstore_0 "+
"lstore_1 lstore_2 lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 "+
"dstore_1 dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore "+
"lastore fastore dastore aastore bastore castore sastore pop pop2 dup "+
"dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd isub lsub "+
"fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem lrem frem drem "+
"ineg lneg fneg dneg ishl lshl ishr lshr iushr lushr iand land ior lor "+
"ixor lxor iinc i2l i2f i2d l2i l2f l2d f2i f2l f2d d2i d2l d2f i2b i2c "+
"i2s lcmp fcmpl fcmpg dcmpl dcmpg ifeq ifne iflt ifge ifgt ifle if_icmpeq "+
"if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple if_acmpeq if_acmpne "+
"goto jsr ret tableswitch lookupswitch ireturn lreturn freturn dreturn "+
"areturn return getstatic putstatic getfield putfield invokevirtual "+
"invokespecial invokestatic invokeinterface invokedynamic new newarray "+
"anewarray arraylength athrow checkcast instanceof monitorenter "+
"monitorexit wide multianewarray ifnull ifnonnull goto_w jsr_w ";
for (int bc = 0; names.length() > 0; bc++) {
int sp = names.indexOf(' ');
BC_NAME[bc] = names.substring(0, sp);
names = names.substring(sp+1);
}
}
public static String byteName(int bc) {
String iname;
if (bc < BC_NAME.length && BC_NAME[bc] != null) {
iname = BC_NAME[bc];
} else if (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;
assert(origBC >= _first_linker_op && origBC <= _last_linker_op);
iname = BC_NAME[origBC];
iname += (isSuper ? "_super" : "_this");
if (isAload) iname = "aload_0&" + iname;
iname = "*"+iname;
} else if (isInvokeInitOp(bc)) {
int idx = (bc - _invokeinit_op);
switch (idx) {
case _invokeinit_self_option:
iname = "*invokespecial_init_this"; break;
case _invokeinit_super_option:
iname = "*invokespecial_init_super"; break;
default:
assert(idx == _invokeinit_new_option);
iname = "*invokespecial_init_new"; break;
}
} else {
switch (bc) {
case _ildc: iname = "*ildc"; break;
case _fldc: iname = "*fldc"; break;
case _ildc_w: iname = "*ildc_w"; break;
case _fldc_w: iname = "*fldc_w"; break;
case _dldc2_w: iname = "*dldc2_w"; break;
case _cldc: iname = "*cldc"; break;
case _cldc_w: iname = "*cldc_w"; break;
case _qldc: iname = "*qldc"; break;
case _qldc_w: iname = "*qldc_w"; break;
case _byte_escape: iname = "*byte_escape"; break;
case _ref_escape: iname = "*ref_escape"; break;
case _end_marker: iname = "*end"; break;
default: iname = "*bc#"+bc; break;
}
}
return iname;
}
private static int BW = 4; // width of classification field
private static void def(String fmt, int bc) {
def(fmt, bc, bc);
}
private static void def(String fmt, int from_bc, int to_bc) {
String[] fmts = { fmt, null };
if (fmt.indexOf('w') > 0) {
fmts[1] = fmt.substring(fmt.indexOf('w'));
fmts[0] = fmt.substring(0, fmt.indexOf('w'));
}
for (int w = 0; w <= 1; w++) {
fmt = fmts[w];
if (fmt == null) continue;
int length = fmt.length();
int index = Math.max(0, fmt.indexOf('k'));
int tag = CONSTANT_None;
int branch = Math.max(0, fmt.indexOf('o'));
int slot = Math.max(0, fmt.indexOf('l'));
int con = Math.max(0, fmt.indexOf('x'));
if (index > 0 && index+1 < length) {
switch (fmt.charAt(index+1)) {
case 'c': tag = CONSTANT_Class; break;
case 'k': tag = CONSTANT_LoadableValue; break;
case 'f': tag = CONSTANT_Fieldref; break;
case 'm': tag = CONSTANT_Methodref; break;
case 'i': tag = CONSTANT_InterfaceMethodref; break;
case 'y': tag = CONSTANT_InvokeDynamic; break;
}
assert(tag != CONSTANT_None);
} else if (index > 0 && length == 2) {
assert(from_bc == _ldc);
tag = CONSTANT_LoadableValue; // _ldc opcode only
}
for (int bc = from_bc; bc <= to_bc; bc++) {
BC_FORMAT[w][bc] = fmt;
assert(BC_LENGTH[w][bc] == -1);
BC_LENGTH[w][bc] = (byte) length;
BC_INDEX[w][bc] = (byte) index;
BC_TAG[w][bc] = (byte) tag;
assert(!(index == 0 && tag != CONSTANT_None));
BC_BRANCH[w][bc] = (byte) branch;
BC_SLOT[w][bc] = (byte) slot;
assert(branch == 0 || slot == 0); // not both branch & local
assert(branch == 0 || index == 0); // not both branch & cp
assert(slot == 0 || index == 0); // not both local & cp
BC_CON[w][bc] = (byte) con;
}
}
}
public static void opcodeChecker(byte[] code, ConstantPool.Entry[] cpMap,
Package.Version clsVersion) throws FormatException {
Instruction i = at(code, 0);
while (i != null) {
int opcode = i.getBC();
if (opcode < _nop || opcode > _jsr_w) {
String message = "illegal opcode: " + opcode + " " + i;
throw new FormatException(message);
}
ConstantPool.Entry e = i.getCPRef(cpMap);
if (e != null) {
byte tag = i.getCPTag();
boolean match = e.tagMatches(tag);
if (!match &&
(i.bc == _invokespecial || i.bc == _invokestatic) &&
e.tagMatches(CONSTANT_InterfaceMethodref) &&
clsVersion.greaterThan(Constants.JAVA7_MAX_CLASS_VERSION)) {
match = true;
}
if (!match) {
String message = "illegal reference, expected type="
+ ConstantPool.tagName(tag) + ": "
+ i.toString(cpMap);
throw new FormatException(message);
}
}
i = i.next();
}
}
static class FormatException extends IOException {
@java.io.Serial
private static final long serialVersionUID = 3175572275651367015L;
FormatException(String message) {
super(message);
}
}
}

View file

@ -1,332 +0,0 @@
/*
* Copyright (c) 2003, 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 java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@SuppressWarnings({"removal"})
class NativeUnpack {
// Pointer to the native unpacker obj
private long unpackerPtr;
// Input stream.
private BufferedInputStream in;
private static synchronized native void initIDs();
// Starts processing at the indicated position in the buffer.
// If the buffer is null, the readInputFn callback is used to get bytes.
// Returns (s<<32|f), the number of following segments and files.
private synchronized native long start(ByteBuffer buf, long offset);
// Returns true if there's another, and fills in the parts.
private synchronized native boolean getNextFile(Object[] parts);
private synchronized native ByteBuffer getUnusedInput();
// Resets the engine and frees all resources.
// Returns total number of bytes consumed by the engine.
private synchronized native long finish();
// Setting state in the unpacker.
protected synchronized native boolean setOption(String opt, String value);
protected synchronized native String getOption(String opt);
private int _verbose;
// State for progress bar:
private long _byteCount; // bytes read in current segment
private int _segCount; // number of segs scanned
private int _fileCount; // number of files written
private long _estByteLimit; // estimate of eventual total
private int _estSegLimit; // ditto
private int _estFileLimit; // ditto
private int _prevPercent = -1; // for monotonicity
private final CRC32 _crc32 = new CRC32();
private byte[] _buf = new byte[1<<14];
private UnpackerImpl _p200;
private PropMap _props;
static {
// If loading from stand alone build uncomment this.
// System.loadLibrary("unpack");
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public Void run() {
System.loadLibrary("unpack");
return null;
}
});
initIDs();
}
NativeUnpack(UnpackerImpl p200) {
super();
_p200 = p200;
_props = p200.props;
p200._nunp = this;
}
// for JNI callbacks
private static Object currentInstance() {
UnpackerImpl p200 = (UnpackerImpl) Utils.getTLGlobals();
return (p200 == null)? null: p200._nunp;
}
private synchronized long getUnpackerPtr() {
return unpackerPtr;
}
// Callback from the unpacker engine to get more data.
private long readInputFn(ByteBuffer pbuf, long minlen) throws IOException {
if (in == null) return 0; // nothing is readable
long maxlen = pbuf.capacity() - pbuf.position();
assert(minlen <= maxlen); // don't talk nonsense
long numread = 0;
int steps = 0;
while (numread < minlen) {
steps++;
// read available input, up to buf.length or maxlen
int readlen = _buf.length;
if (readlen > (maxlen - numread))
readlen = (int)(maxlen - numread);
int nr = in.read(_buf, 0, readlen);
if (nr <= 0) break;
numread += nr;
assert(numread <= maxlen);
// %%% get rid of this extra copy by using nio?
pbuf.put(_buf, 0, nr);
}
if (_verbose > 1)
Utils.log.fine("readInputFn("+minlen+","+maxlen+") => "+numread+" steps="+steps);
if (maxlen > 100) {
_estByteLimit = _byteCount + maxlen;
} else {
_estByteLimit = (_byteCount + numread) * 20;
}
_byteCount += numread;
updateProgress();
return numread;
}
private void updateProgress() {
// Progress is a combination of segment reading and file writing.
final double READ_WT = 0.33;
final double WRITE_WT = 0.67;
double readProgress = _segCount;
if (_estByteLimit > 0 && _byteCount > 0)
readProgress += (double)_byteCount / _estByteLimit;
double writeProgress = _fileCount;
double scaledProgress
= READ_WT * readProgress / Math.max(_estSegLimit,1)
+ WRITE_WT * writeProgress / Math.max(_estFileLimit,1);
int percent = (int) Math.round(100*scaledProgress);
if (percent > 100) percent = 100;
if (percent > _prevPercent) {
_prevPercent = percent;
_props.setInteger(Pack200.Unpacker.PROGRESS, percent);
if (_verbose > 0)
Utils.log.info("progress = "+percent);
}
}
private void copyInOption(String opt) {
String val = _props.getProperty(opt);
if (_verbose > 0)
Utils.log.info("set "+opt+"="+val);
if (val != null) {
boolean set = setOption(opt, val);
if (!set)
Utils.log.warning("Invalid option "+opt+"="+val);
}
}
void run(InputStream inRaw, JarOutputStream jstream,
ByteBuffer presetInput) throws IOException {
BufferedInputStream in0 = new BufferedInputStream(inRaw);
this.in = in0; // for readInputFn to see
_verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
// Fix for BugId: 4902477, -unpack.modification.time = 1059010598000
// TODO eliminate and fix in unpack.cpp
final int modtime = Pack200.Packer.KEEP.equals(_props.getProperty(Utils.UNPACK_MODIFICATION_TIME, "0")) ?
Constants.NO_MODTIME : _props.getTime(Utils.UNPACK_MODIFICATION_TIME);
copyInOption(Utils.DEBUG_VERBOSE);
copyInOption(Pack200.Unpacker.DEFLATE_HINT);
if (modtime == Constants.NO_MODTIME) // Don't pass KEEP && NOW
copyInOption(Utils.UNPACK_MODIFICATION_TIME);
updateProgress(); // reset progress bar
for (;;) {
// Read the packed bits.
long counts = start(presetInput, 0);
_byteCount = _estByteLimit = 0; // reset partial scan counts
++_segCount; // just finished scanning a whole segment...
int nextSeg = (int)( counts >>> 32 );
int nextFile = (int)( counts >>> 0 );
// Estimate eventual total number of segments and files.
_estSegLimit = _segCount + nextSeg;
double filesAfterThisSeg = _fileCount + nextFile;
_estFileLimit = (int)( (filesAfterThisSeg *
_estSegLimit) / _segCount );
// Write the files.
int[] intParts = { 0,0, 0, 0 };
// intParts = {size.hi/lo, mod, defl}
Object[] parts = { intParts, null, null, null };
// parts = { {intParts}, name, data0/1 }
while (getNextFile(parts)) {
//BandStructure.printArrayTo(System.out, intParts, 0, parts.length);
String name = (String) parts[1];
long size = ( (long)intParts[0] << 32)
+ (((long)intParts[1] << 32) >>> 32);
long mtime = (modtime != Constants.NO_MODTIME ) ?
modtime : intParts[2] ;
boolean deflateHint = (intParts[3] != 0);
ByteBuffer data0 = (ByteBuffer) parts[2];
ByteBuffer data1 = (ByteBuffer) parts[3];
writeEntry(jstream, name, mtime, size, deflateHint,
data0, data1);
++_fileCount;
updateProgress();
}
presetInput = getUnusedInput();
long consumed = finish();
if (_verbose > 0)
Utils.log.info("bytes consumed = "+consumed);
if (presetInput == null &&
!Utils.isPackMagic(Utils.readMagic(in0))) {
break;
}
if (_verbose > 0 ) {
if (presetInput != null)
Utils.log.info("unused input = "+presetInput);
}
}
}
void run(InputStream in, JarOutputStream jstream) throws IOException {
run(in, jstream, null);
}
void run(File inFile, JarOutputStream jstream) throws IOException {
// %%% maybe memory-map the file, and pass it straight into unpacker
ByteBuffer mappedFile = null;
try (FileInputStream fis = new FileInputStream(inFile)) {
run(fis, jstream, mappedFile);
}
// Note: caller is responsible to finish with jstream.
}
private void writeEntry(JarOutputStream j, String name,
long mtime, long lsize, boolean deflateHint,
ByteBuffer data0, ByteBuffer data1) throws IOException {
int size = (int)lsize;
if (size != lsize)
throw new IOException("file too large: "+lsize);
CRC32 crc32 = _crc32;
if (_verbose > 1)
Utils.log.fine("Writing entry: "+name+" size="+size
+(deflateHint?" deflated":""));
if (_buf.length < size) {
int newSize = size;
while (newSize < _buf.length) {
newSize <<= 1;
if (newSize <= 0) {
newSize = size;
break;
}
}
_buf = new byte[newSize];
}
assert(_buf.length >= size);
int fillp = 0;
if (data0 != null) {
int size0 = data0.capacity();
data0.get(_buf, fillp, size0);
fillp += size0;
}
if (data1 != null) {
int size1 = data1.capacity();
data1.get(_buf, fillp, size1);
fillp += size1;
}
while (fillp < size) {
// Fill in rest of data from the stream itself.
int nr = in.read(_buf, fillp, size - fillp);
if (nr <= 0) throw new IOException("EOF at end of archive");
fillp += nr;
}
ZipEntry z = new ZipEntry(name);
z.setTime(mtime * 1000);
if (size == 0) {
z.setMethod(ZipOutputStream.STORED);
z.setSize(0);
z.setCrc(0);
z.setCompressedSize(0);
} else if (!deflateHint) {
z.setMethod(ZipOutputStream.STORED);
z.setSize(size);
z.setCompressedSize(size);
crc32.reset();
crc32.update(_buf, 0, size);
z.setCrc(crc32.getValue());
} else {
z.setMethod(Deflater.DEFLATED);
z.setSize(size);
}
j.putNextEntry(z);
if (size > 0)
j.write(_buf, 0, size);
j.closeEntry();
if (_verbose > 0) Utils.log.info("Writing " + Utils.zeString(z));
}
}

View file

@ -1,611 +0,0 @@
/*
* Copyright (c) 2003, 2015, 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.Attribute.Layout;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Pack200;
/*
* Implementation of the Pack provider.
* </pre></blockquote>
* @author John Rose
* @author Kumar Srinivasan
*/
@SuppressWarnings({"removal"})
public class PackerImpl extends TLGlobals implements Pack200.Packer {
/**
* Constructs a Packer object and sets the initial state of
* the packer engines.
*/
public PackerImpl() {}
/**
* Get the set of options for the pack and unpack engines.
* @return A sorted association of option key strings to option values.
*/
public SortedMap<String, String> properties() {
return props;
}
//Driver routines
/**
* Takes a JarFile and converts into a pack-stream.
* <p>
* Closes its input but not its output. (Pack200 archives are appendable.)
* @param in a JarFile
* @param out an OutputStream
* @exception IOException if an error is encountered.
*/
public synchronized void pack(JarFile in, OutputStream out) throws IOException {
assert(Utils.currentInstance.get() == null);
try {
Utils.currentInstance.set(this);
if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
Utils.copyJarFile(in, out);
} else {
(new DoPack()).run(in, out);
}
} finally {
Utils.currentInstance.set(null);
in.close();
}
}
/**
* Takes a JarInputStream and converts into a pack-stream.
* <p>
* Closes its input but not its output. (Pack200 archives are appendable.)
* <p>
* The modification time and deflation hint attributes are not available,
* for the jar-manifest file and the directory containing the file.
*
* @see #MODIFICATION_TIME
* @see #DEFLATION_HINT
* @param in a JarInputStream
* @param out an OutputStream
* @exception IOException if an error is encountered.
*/
public synchronized void pack(JarInputStream in, OutputStream out) throws IOException {
assert(Utils.currentInstance.get() == null);
try {
Utils.currentInstance.set(this);
if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
Utils.copyJarFile(in, out);
} else {
(new DoPack()).run(in, out);
}
} finally {
Utils.currentInstance.set(null);
in.close();
}
}
// All the worker bees.....
// The packer worker.
private class DoPack {
final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);
{
props.setInteger(Pack200.Packer.PROGRESS, 0);
if (verbose > 0) Utils.log.info(props.toString());
}
// Here's where the bits are collected before getting packed, we also
// initialize the version numbers now.
final Package pkg = new Package(Package.Version.makeVersion(props, "min.class"),
Package.Version.makeVersion(props, "max.class"),
Package.Version.makeVersion(props, "package"));
final String unknownAttrCommand;
{
String uaMode = props.getProperty(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS);
if (!(Pack200.Packer.STRIP.equals(uaMode) ||
Pack200.Packer.PASS.equals(uaMode) ||
Pack200.Packer.ERROR.equals(uaMode))) {
throw new RuntimeException("Bad option: " + Pack200.Packer.UNKNOWN_ATTRIBUTE + " = " + uaMode);
}
unknownAttrCommand = uaMode.intern();
}
final String classFormatCommand;
{
String fmtMode = props.getProperty(Utils.CLASS_FORMAT_ERROR, Pack200.Packer.PASS);
if (!(Pack200.Packer.PASS.equals(fmtMode) ||
Pack200.Packer.ERROR.equals(fmtMode))) {
throw new RuntimeException("Bad option: " + Utils.CLASS_FORMAT_ERROR + " = " + fmtMode);
}
classFormatCommand = fmtMode.intern();
}
final Map<Attribute.Layout, Attribute> attrDefs;
final Map<Attribute.Layout, String> attrCommands;
{
Map<Attribute.Layout, Attribute> lattrDefs = new HashMap<>();
Map<Attribute.Layout, String> lattrCommands = new HashMap<>();
String[] keys = {
Pack200.Packer.CLASS_ATTRIBUTE_PFX,
Pack200.Packer.FIELD_ATTRIBUTE_PFX,
Pack200.Packer.METHOD_ATTRIBUTE_PFX,
Pack200.Packer.CODE_ATTRIBUTE_PFX
};
int[] ctypes = {
Constants.ATTR_CONTEXT_CLASS,
Constants.ATTR_CONTEXT_FIELD,
Constants.ATTR_CONTEXT_METHOD,
Constants.ATTR_CONTEXT_CODE
};
for (int i = 0; i < ctypes.length; i++) {
String pfx = keys[i];
Map<String, String> map = props.prefixMap(pfx);
for (String key : map.keySet()) {
assert(key.startsWith(pfx));
String name = key.substring(pfx.length());
String layout = props.getProperty(key);
Layout lkey = Attribute.keyForLookup(ctypes[i], name);
if (Pack200.Packer.STRIP.equals(layout) ||
Pack200.Packer.PASS.equals(layout) ||
Pack200.Packer.ERROR.equals(layout)) {
lattrCommands.put(lkey, layout.intern());
} else {
Attribute.define(lattrDefs, ctypes[i], name, layout);
if (verbose > 1) {
Utils.log.fine("Added layout for "+Constants.ATTR_CONTEXT_NAME[i]+" attribute "+name+" = "+layout);
}
assert(lattrDefs.containsKey(lkey));
}
}
}
this.attrDefs = (lattrDefs.isEmpty()) ? null : lattrDefs;
this.attrCommands = (lattrCommands.isEmpty()) ? null : lattrCommands;
}
final boolean keepFileOrder
= props.getBoolean(Pack200.Packer.KEEP_FILE_ORDER);
final boolean keepClassOrder
= props.getBoolean(Utils.PACK_KEEP_CLASS_ORDER);
final boolean keepModtime
= Pack200.Packer.KEEP.equals(props.getProperty(Pack200.Packer.MODIFICATION_TIME));
final boolean latestModtime
= Pack200.Packer.LATEST.equals(props.getProperty(Pack200.Packer.MODIFICATION_TIME));
final boolean keepDeflateHint
= Pack200.Packer.KEEP.equals(props.getProperty(Pack200.Packer.DEFLATE_HINT));
{
if (!keepModtime && !latestModtime) {
int modtime = props.getTime(Pack200.Packer.MODIFICATION_TIME);
if (modtime != Constants.NO_MODTIME) {
pkg.default_modtime = modtime;
}
}
if (!keepDeflateHint) {
boolean deflate_hint = props.getBoolean(Pack200.Packer.DEFLATE_HINT);
if (deflate_hint) {
pkg.default_options |= Constants.AO_DEFLATE_HINT;
}
}
}
long totalOutputSize = 0;
int segmentCount = 0;
long segmentTotalSize = 0;
long segmentSize = 0; // running counter
final long segmentLimit;
{
long limit;
if (props.getProperty(Pack200.Packer.SEGMENT_LIMIT, "").isEmpty())
limit = -1;
else
limit = props.getLong(Pack200.Packer.SEGMENT_LIMIT);
limit = Math.min(Integer.MAX_VALUE, limit);
limit = Math.max(-1, limit);
if (limit == -1)
limit = Long.MAX_VALUE;
segmentLimit = limit;
}
final List<String> passFiles; // parsed pack.pass.file options
{
// Which class files will be passed through?
passFiles = props.getProperties(Pack200.Packer.PASS_FILE_PFX);
for (ListIterator<String> i = passFiles.listIterator(); i.hasNext(); ) {
String file = i.next();
if (file == null) { i.remove(); continue; }
file = Utils.getJarEntryName(file); // normalize '\\' to '/'
if (file.endsWith("/"))
file = file.substring(0, file.length()-1);
i.set(file);
}
if (verbose > 0) Utils.log.info("passFiles = " + passFiles);
}
{
// Hook for testing: Forces use of special archive modes.
int opt = props.getInteger(Utils.COM_PREFIX+"archive.options");
if (opt != 0)
pkg.default_options |= opt;
}
// (Done collecting options from props.)
// Get a new package, based on the old one.
private void makeNextPackage() {
pkg.reset();
}
final class InFile {
final String name;
final JarFile jf;
final JarEntry je;
final File f;
int modtime = Constants.NO_MODTIME;
int options;
InFile(String name) {
this.name = Utils.getJarEntryName(name);
this.f = new File(name);
this.jf = null;
this.je = null;
int timeSecs = getModtime(f.lastModified());
if (keepModtime && timeSecs != Constants.NO_MODTIME) {
this.modtime = timeSecs;
} else if (latestModtime && timeSecs > pkg.default_modtime) {
pkg.default_modtime = timeSecs;
}
}
InFile(JarFile jf, JarEntry je) {
this.name = Utils.getJarEntryName(je.getName());
this.f = null;
this.jf = jf;
this.je = je;
int timeSecs = (int) je.getTimeLocal()
.atOffset(ZoneOffset.UTC)
.toEpochSecond();
if (keepModtime && timeSecs != Constants.NO_MODTIME) {
this.modtime = timeSecs;
} else if (latestModtime && timeSecs > pkg.default_modtime) {
pkg.default_modtime = timeSecs;
}
if (keepDeflateHint && je.getMethod() == JarEntry.DEFLATED) {
options |= Constants.FO_DEFLATE_HINT;
}
}
InFile(JarEntry je) {
this(null, je);
}
boolean isClassFile() {
if (!name.endsWith(".class") || name.endsWith("module-info.class")) {
return false;
}
for (String prefix = name;;) {
if (passFiles.contains(prefix)) {
return false;
}
int chop = prefix.lastIndexOf('/');
if (chop < 0) {
break;
}
prefix = prefix.substring(0, chop);
}
return true;
}
boolean isMetaInfFile() {
return name.startsWith("/" + Utils.METAINF)
|| name.startsWith(Utils.METAINF);
}
boolean mustProcess() {
return !isMetaInfFile() && isClassFile();
}
long getInputLength() {
long len = (je != null)? je.getSize(): f.length();
assert(len >= 0) : this+".len="+len;
// Bump size by pathname length and modtime/def-hint bytes.
return Math.max(0, len) + name.length() + 5;
}
int getModtime(long timeMillis) {
// Convert milliseconds to seconds.
long seconds = (timeMillis+500) / 1000;
if ((int)seconds == seconds) {
return (int)seconds;
} else {
Utils.log.warning("overflow in modtime for "+f);
return Constants.NO_MODTIME;
}
}
void copyTo(Package.File file) {
if (modtime != Constants.NO_MODTIME)
file.modtime = modtime;
file.options |= options;
}
InputStream getInputStream() throws IOException {
if (jf != null)
return jf.getInputStream(je);
else
return new FileInputStream(f);
}
public String toString() {
return name;
}
}
private int nread = 0; // used only if (verbose > 0)
private void noteRead(InFile f) {
nread++;
if (verbose > 2)
Utils.log.fine("...read "+f.name);
if (verbose > 0 && (nread % 1000) == 0)
Utils.log.info("Have read "+nread+" files...");
}
void run(JarInputStream in, OutputStream out) throws IOException {
// First thing we do is get the manifest, as JIS does
// not provide the Manifest as an entry.
if (in.getManifest() != null) {
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
in.getManifest().write(tmp);
InputStream tmpIn = new ByteArrayInputStream(tmp.toByteArray());
pkg.addFile(readFile(JarFile.MANIFEST_NAME, tmpIn));
}
for (JarEntry je; (je = in.getNextJarEntry()) != null; ) {
InFile inFile = new InFile(je);
String name = inFile.name;
Package.File bits = readFile(name, in);
Package.File file = null;
// (5078608) : discount the resource files in META-INF
// from segment computation.
long inflen = (inFile.isMetaInfFile())
? 0L
: inFile.getInputLength();
if ((segmentSize += inflen) > segmentLimit) {
segmentSize -= inflen;
int nextCount = -1; // don't know; it's a stream
flushPartial(out, nextCount);
}
if (verbose > 1) {
Utils.log.fine("Reading " + name);
}
assert(je.isDirectory() == name.endsWith("/"));
if (inFile.mustProcess()) {
file = readClass(name, bits.getInputStream());
}
if (file == null) {
file = bits;
pkg.addFile(file);
}
inFile.copyTo(file);
noteRead(inFile);
}
flushAll(out);
}
void run(JarFile in, OutputStream out) throws IOException {
List<InFile> inFiles = scanJar(in);
if (verbose > 0)
Utils.log.info("Reading " + inFiles.size() + " files...");
int numDone = 0;
for (InFile inFile : inFiles) {
String name = inFile.name;
// (5078608) : discount the resource files completely from segmenting
long inflen = (inFile.isMetaInfFile())
? 0L
: inFile.getInputLength() ;
if ((segmentSize += inflen) > segmentLimit) {
segmentSize -= inflen;
// Estimate number of remaining segments:
float filesDone = numDone+1;
float segsDone = segmentCount+1;
float filesToDo = inFiles.size() - filesDone;
float segsToDo = filesToDo * (segsDone/filesDone);
if (verbose > 1)
Utils.log.fine("Estimated segments to do: "+segsToDo);
flushPartial(out, (int) Math.ceil(segsToDo));
}
InputStream strm = inFile.getInputStream();
if (verbose > 1)
Utils.log.fine("Reading " + name);
Package.File file = null;
if (inFile.mustProcess()) {
file = readClass(name, strm);
if (file == null) {
strm.close();
strm = inFile.getInputStream();
}
}
if (file == null) {
file = readFile(name, strm);
pkg.addFile(file);
}
inFile.copyTo(file);
strm.close(); // tidy up
noteRead(inFile);
numDone += 1;
}
flushAll(out);
}
Package.File readClass(String fname, InputStream in) throws IOException {
Package.Class cls = pkg.new Class(fname);
in = new BufferedInputStream(in);
ClassReader reader = new ClassReader(cls, in);
reader.setAttrDefs(attrDefs);
reader.setAttrCommands(attrCommands);
reader.unknownAttrCommand = unknownAttrCommand;
try {
reader.read();
} catch (IOException ioe) {
String message = "Passing class file uncompressed due to";
if (ioe instanceof Attribute.FormatException) {
Attribute.FormatException ee = (Attribute.FormatException) ioe;
// He passed up the category to us in layout.
if (ee.layout.equals(Pack200.Packer.PASS)) {
Utils.log.info(ee.toString());
Utils.log.warning(message + " unrecognized attribute: " +
fname);
return null;
}
} else if (ioe instanceof ClassReader.ClassFormatException) {
ClassReader.ClassFormatException ce = (ClassReader.ClassFormatException) ioe;
if (classFormatCommand.equals(Pack200.Packer.PASS)) {
Utils.log.info(ce.toString());
Utils.log.warning(message + " unknown class format: " +
fname);
return null;
}
}
// Otherwise, it must be an error.
throw ioe;
}
pkg.addClass(cls);
return cls.file;
}
// Read raw data.
Package.File readFile(String fname, InputStream in) throws IOException {
Package.File file = pkg.new File(fname);
file.readFrom(in);
if (file.isDirectory() && file.getFileLength() != 0)
throw new IllegalArgumentException("Non-empty directory: "+file.getFileName());
return file;
}
void flushPartial(OutputStream out, int nextCount) throws IOException {
if (pkg.files.isEmpty() && pkg.classes.isEmpty()) {
return; // do not flush an empty segment
}
flushPackage(out, Math.max(1, nextCount));
props.setInteger(Pack200.Packer.PROGRESS, 25);
// In case there will be another segment:
makeNextPackage();
segmentCount += 1;
segmentTotalSize += segmentSize;
segmentSize = 0;
}
void flushAll(OutputStream out) throws IOException {
props.setInteger(Pack200.Packer.PROGRESS, 50);
flushPackage(out, 0);
out.flush();
props.setInteger(Pack200.Packer.PROGRESS, 100);
segmentCount += 1;
segmentTotalSize += segmentSize;
segmentSize = 0;
if (verbose > 0 && segmentCount > 1) {
Utils.log.info("Transmitted "
+segmentTotalSize+" input bytes in "
+segmentCount+" segments totaling "
+totalOutputSize+" bytes");
}
}
/** Write all information in the current package segment
* to the output stream.
*/
void flushPackage(OutputStream out, int nextCount) throws IOException {
int nfiles = pkg.files.size();
if (!keepFileOrder) {
// Keeping the order of classes costs about 1%
// Keeping the order of all files costs something more.
if (verbose > 1) Utils.log.fine("Reordering files.");
boolean stripDirectories = true;
pkg.reorderFiles(keepClassOrder, stripDirectories);
} else {
// Package builder must have created a stub for each class.
assert(pkg.files.containsAll(pkg.getClassStubs()));
// Order of stubs in file list must agree with classes.
List<Package.File> res = pkg.files;
assert((res = new ArrayList<>(pkg.files))
.retainAll(pkg.getClassStubs()) || true);
assert(res.equals(pkg.getClassStubs()));
}
pkg.trimStubs();
// Do some stripping, maybe.
if (props.getBoolean(Utils.COM_PREFIX+"strip.debug")) pkg.stripAttributeKind("Debug");
if (props.getBoolean(Utils.COM_PREFIX+"strip.compile")) pkg.stripAttributeKind("Compile");
if (props.getBoolean(Utils.COM_PREFIX+"strip.constants")) pkg.stripAttributeKind("Constant");
if (props.getBoolean(Utils.COM_PREFIX+"strip.exceptions")) pkg.stripAttributeKind("Exceptions");
if (props.getBoolean(Utils.COM_PREFIX+"strip.innerclasses")) pkg.stripAttributeKind("InnerClasses");
PackageWriter pw = new PackageWriter(pkg, out);
pw.archiveNextCount = nextCount;
pw.write();
out.flush();
if (verbose > 0) {
long outSize = pw.archiveSize0+pw.archiveSize1;
totalOutputSize += outSize;
long inSize = segmentSize;
Utils.log.info("Transmitted "
+nfiles+" files of "
+inSize+" input bytes in a segment of "
+outSize+" bytes");
}
}
List<InFile> scanJar(JarFile jf) throws IOException {
// Collect jar entries, preserving order.
List<InFile> inFiles = new ArrayList<>();
try {
for (JarEntry je : Collections.list(jf.entries())) {
InFile inFile = new InFile(jf, je);
assert(je.isDirectory() == inFile.name.endsWith("/"));
inFiles.add(inFile);
}
} catch (IllegalStateException ise) {
throw new IOException(ise.getLocalizedMessage(), ise);
}
return inFiles;
}
}
}

View file

@ -1,500 +0,0 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.java.util.jar.pack;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static com.sun.java.util.jar.pack.Constants.*;
/**
* Population-based coding.
* See the section "Encodings of Uncorrelated Values" in the Pack200 spec.
* @author John Rose
*/
// This tactic alone reduces the final zipped rt.jar by about a percent.
class PopulationCoding implements CodingMethod {
Histogram vHist; // histogram of all values
int[] fValues; // list of favored values
int fVlen; // inclusive max index
long[] symtab; // int map of favored value -> token [1..#fValues]
CodingMethod favoredCoding;
CodingMethod tokenCoding;
CodingMethod unfavoredCoding;
int L = -1; //preferred L value for tokenCoding
public void setFavoredValues(int[] fValues, int fVlen) {
// Note: {f} is allFavoredValues[1..fvlen], not [0..fvlen-1].
// This is because zero is an exceptional favored value index.
assert(fValues[0] == 0); // must be empty
assert(this.fValues == null); // do not do this twice
this.fValues = fValues;
this.fVlen = fVlen;
if (L >= 0) {
setL(L); // reassert
}
}
public void setFavoredValues(int[] fValues) {
int lfVlen = fValues.length-1;
setFavoredValues(fValues, lfVlen);
}
public void setHistogram(Histogram vHist) {
this.vHist = vHist;
}
public void setL(int L) {
this.L = L;
if (L >= 0 && fValues != null && tokenCoding == null) {
tokenCoding = fitTokenCoding(fVlen, L);
assert(tokenCoding != null);
}
}
public static Coding fitTokenCoding(int fVlen, int L) {
// Find the smallest B s.t. (B,H,0) covers fVlen.
if (fVlen < 256)
// H/L do not matter when B==1
return BandStructure.BYTE1;
Coding longest = BandStructure.UNSIGNED5.setL(L);
if (!longest.canRepresentUnsigned(fVlen))
return null; // failure; L is too sharp and fVlen too large
Coding tc = longest;
for (Coding shorter = longest; ; ) {
shorter = shorter.setB(shorter.B()-1);
if (shorter.umax() < fVlen)
break;
tc = shorter; // shorten it by reducing B
}
return tc;
}
public void setFavoredCoding(CodingMethod favoredCoding) {
this.favoredCoding = favoredCoding;
}
public void setTokenCoding(CodingMethod tokenCoding) {
this.tokenCoding = tokenCoding;
this.L = -1;
if (tokenCoding instanceof Coding && fValues != null) {
Coding tc = (Coding) tokenCoding;
if (tc == fitTokenCoding(fVlen, tc.L()))
this.L = tc.L();
// Otherwise, it's a non-default coding.
}
}
public void setUnfavoredCoding(CodingMethod unfavoredCoding) {
this.unfavoredCoding = unfavoredCoding;
}
public int favoredValueMaxLength() {
if (L == 0)
return Integer.MAX_VALUE;
else
return BandStructure.UNSIGNED5.setL(L).umax();
}
public void resortFavoredValues() {
Coding tc = (Coding) tokenCoding;
// Make a local copy before reordering.
fValues = BandStructure.realloc(fValues, 1+fVlen);
// Resort favoredValues within each byte-size cadre.
int fillp = 1; // skip initial zero
for (int n = 1; n <= tc.B(); n++) {
int nmax = tc.byteMax(n);
if (nmax > fVlen)
nmax = fVlen;
if (nmax < tc.byteMin(n))
break;
int low = fillp;
int high = nmax+1;
if (high == low) continue;
assert(high > low)
: high+"!>"+low;
assert(tc.getLength(low) == n)
: n+" != len("+(low)+") == "+
tc.getLength(low);
assert(tc.getLength(high-1) == n)
: n+" != len("+(high-1)+") == "+
tc.getLength(high-1);
int midTarget = low + (high-low)/2;
int mid = low;
// Divide the values into cadres, and sort within each.
int prevCount = -1;
int prevLimit = low;
for (int i = low; i < high; i++) {
int val = fValues[i];
int count = vHist.getFrequency(val);
if (prevCount != count) {
if (n == 1) {
// For the single-byte encoding, keep strict order
// among frequency groups.
Arrays.sort(fValues, prevLimit, i);
} else if (Math.abs(mid - midTarget) >
Math.abs(i - midTarget)) {
// Find a single inflection point
// close to the middle of the byte-size cadre.
mid = i;
}
prevCount = count;
prevLimit = i;
}
}
if (n == 1) {
Arrays.sort(fValues, prevLimit, high);
} else {
// Sort up to the midpoint, if any.
Arrays.sort(fValues, low, mid);
Arrays.sort(fValues, mid, high);
}
assert(tc.getLength(low) == tc.getLength(mid));
assert(tc.getLength(low) == tc.getLength(high-1));
fillp = nmax+1;
}
assert(fillp == fValues.length);
// Reset symtab.
symtab = null;
}
public int getToken(int value) {
if (symtab == null)
symtab = makeSymtab();
int pos = Arrays.binarySearch(symtab, (long)value << 32);
if (pos < 0) pos = -pos-1;
if (pos < symtab.length && value == (int)(symtab[pos] >>> 32))
return (int)symtab[pos];
else
return 0;
}
public int[][] encodeValues(int[] values, int start, int end) {
// Compute token sequence.
int[] tokens = new int[end-start];
int nuv = 0;
for (int i = 0; i < tokens.length; i++) {
int val = values[start+i];
int tok = getToken(val);
if (tok != 0)
tokens[i] = tok;
else
nuv += 1;
}
// Compute unfavored value sequence.
int[] unfavoredValues = new int[nuv];
nuv = 0; // reset
for (int i = 0; i < tokens.length; i++) {
if (tokens[i] != 0) continue; // already covered
int val = values[start+i];
unfavoredValues[nuv++] = val;
}
assert(nuv == unfavoredValues.length);
return new int[][]{ tokens, unfavoredValues };
}
private long[] makeSymtab() {
long[] lsymtab = new long[fVlen];
for (int token = 1; token <= fVlen; token++) {
lsymtab[token-1] = ((long)fValues[token] << 32) | token;
}
// Index by value:
Arrays.sort(lsymtab);
return lsymtab;
}
private Coding getTailCoding(CodingMethod c) {
while (c instanceof AdaptiveCoding)
c = ((AdaptiveCoding)c).tailCoding;
return (Coding) c;
}
// CodingMethod methods.
public void writeArrayTo(OutputStream out, int[] a, int start, int end) throws IOException {
int[][] vals = encodeValues(a, start, end);
writeSequencesTo(out, vals[0], vals[1]);
}
void writeSequencesTo(OutputStream out, int[] tokens, int[] uValues) throws IOException {
favoredCoding.writeArrayTo(out, fValues, 1, 1+fVlen);
getTailCoding(favoredCoding).writeTo(out, computeSentinelValue());
tokenCoding.writeArrayTo(out, tokens, 0, tokens.length);
if (uValues.length > 0)
unfavoredCoding.writeArrayTo(out, uValues, 0, uValues.length);
}
int computeSentinelValue() {
Coding fc = getTailCoding(favoredCoding);
if (fc.isDelta()) {
// repeat the last favored value, using delta=0
return 0;
} else {
// else repeat the shorter of the min or last value
int min = fValues[1];
int last = min;
// (remember that fVlen is an inclusive limit in fValues)
for (int i = 2; i <= fVlen; i++) {
last = fValues[i];
min = moreCentral(min, last);
}
int endVal;
if (fc.getLength(min) <= fc.getLength(last))
return min;
else
return last;
}
}
public void readArrayFrom(InputStream in, int[] a, int start, int end) throws IOException {
// Parameters are fCode, L, uCode.
setFavoredValues(readFavoredValuesFrom(in, end-start));
// Read the tokens. Read them into the final array, for the moment.
tokenCoding.readArrayFrom(in, a, start, end);
// Decode the favored tokens.
int headp = 0, tailp = -1;
int uVlen = 0;
for (int i = start; i < end; i++) {
int tok = a[i];
if (tok == 0) {
// Make a linked list, and decode in a second pass.
if (tailp < 0) {
headp = i;
} else {
a[tailp] = i;
}
tailp = i;
uVlen += 1;
} else {
a[i] = fValues[tok];
}
}
// Walk the linked list of "zero" locations, decoding unfavored vals.
int[] uValues = new int[uVlen];
if (uVlen > 0)
unfavoredCoding.readArrayFrom(in, uValues, 0, uVlen);
for (int i = 0; i < uVlen; i++) {
int nextp = a[headp];
a[headp] = uValues[i];
headp = nextp;
}
}
int[] readFavoredValuesFrom(InputStream in, int maxForDebug) throws IOException {
int[] lfValues = new int[1000]; // realloc as needed
// The set uniqueValuesForDebug records all favored values.
// As each new value is added, we assert that the value
// was not already in the set.
Set<Integer> uniqueValuesForDebug = null;
assert((uniqueValuesForDebug = new HashSet<>()) != null);
int fillp = 1;
maxForDebug += fillp;
int min = Integer.MIN_VALUE; // farthest from the center
//int min2 = Integer.MIN_VALUE; // emulate buggy 150.7 spec.
int last = 0;
CodingMethod fcm = favoredCoding;
while (fcm instanceof AdaptiveCoding) {
AdaptiveCoding ac = (AdaptiveCoding) fcm;
int len = ac.headLength;
while (fillp + len > lfValues.length) {
lfValues = BandStructure.realloc(lfValues);
}
int newFillp = fillp + len;
ac.headCoding.readArrayFrom(in, lfValues, fillp, newFillp);
while (fillp < newFillp) {
int val = lfValues[fillp++];
assert(uniqueValuesForDebug.add(val));
assert(fillp <= maxForDebug);
last = val;
min = moreCentral(min, val);
//min2 = moreCentral2(min2, val, min);
}
fcm = ac.tailCoding;
}
Coding fc = (Coding) fcm;
if (fc.isDelta()) {
for (long state = 0;;) {
// Read a new value:
state += fc.readFrom(in);
int val;
if (fc.isSubrange())
val = fc.reduceToUnsignedRange(state);
else
val = (int)state;
state = val;
if (fillp > 1 && (val == last || val == min)) //|| val == min2
break;
if (fillp == lfValues.length)
lfValues = BandStructure.realloc(lfValues);
lfValues[fillp++] = val;
assert(uniqueValuesForDebug.add(val));
assert(fillp <= maxForDebug);
last = val;
min = moreCentral(min, val);
//min2 = moreCentral(min2, val);
}
} else {
for (;;) {
int val = fc.readFrom(in);
if (fillp > 1 && (val == last || val == min)) //|| val == min2
break;
if (fillp == lfValues.length)
lfValues = BandStructure.realloc(lfValues);
lfValues[fillp++] = val;
assert(uniqueValuesForDebug.add(val));
assert(fillp <= maxForDebug);
last = val;
min = moreCentral(min, val);
//min2 = moreCentral2(min2, val, min);
}
}
return BandStructure.realloc(lfValues, fillp);
}
private static int moreCentral(int x, int y) {
int kx = (x >> 31) ^ (x << 1);
int ky = (y >> 31) ^ (y << 1);
// bias kx/ky to get an unsigned comparison:
kx -= Integer.MIN_VALUE;
ky -= Integer.MIN_VALUE;
int xy = (kx < ky? x: y);
// assert that this ALU-ish version is the same:
assert(xy == moreCentralSlow(x, y));
return xy;
}
// private static int moreCentral2(int x, int y, int min) {
// // Strict implementation of buggy 150.7 specification.
// // The bug is that the spec. says absolute-value ties are broken
// // in favor of positive numbers, but the suggested implementation
// // (also mentioned in the spec.) breaks ties in favor of negatives.
// if (x + y == 0) return (x > y? x : y);
// return min;
// }
private static int moreCentralSlow(int x, int y) {
int ax = x;
if (ax < 0) ax = -ax;
if (ax < 0) return y; //x is MIN_VALUE
int ay = y;
if (ay < 0) ay = -ay;
if (ay < 0) return x; //y is MIN_VALUE
if (ax < ay) return x;
if (ax > ay) return y;
// At this point the absolute values agree, and the negative wins.
return x < y ? x : y;
}
static final int[] LValuesCoded
= { -1, 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252 };
public byte[] getMetaCoding(Coding dflt) {
int K = fVlen;
int LCoded = 0;
if (tokenCoding instanceof Coding) {
Coding tc = (Coding) tokenCoding;
if (tc.B() == 1) {
LCoded = 1;
} else if (L >= 0) {
assert(L == tc.L());
for (int i = 1; i < LValuesCoded.length; i++) {
if (LValuesCoded[i] == L) { LCoded = i; break; }
}
}
}
CodingMethod tokenDflt = null;
if (LCoded != 0 && tokenCoding == fitTokenCoding(fVlen, L)) {
// A simple L value is enough to recover the tokenCoding.
tokenDflt = tokenCoding;
}
int FDef = (favoredCoding == dflt)?1:0;
int UDef = (unfavoredCoding == dflt || unfavoredCoding == null)?1:0;
int TDef = (tokenCoding == tokenDflt)?1:0;
int TDefL = (TDef == 1) ? LCoded : 0;
assert(TDef == ((TDefL>0)?1:0));
ByteArrayOutputStream bytes = new ByteArrayOutputStream(10);
bytes.write(_meta_pop + FDef + 2*UDef + 4*TDefL);
try {
if (FDef == 0) bytes.write(favoredCoding.getMetaCoding(dflt));
if (TDef == 0) bytes.write(tokenCoding.getMetaCoding(dflt));
if (UDef == 0) bytes.write(unfavoredCoding.getMetaCoding(dflt));
} catch (IOException ee) {
throw new RuntimeException(ee);
}
return bytes.toByteArray();
}
public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod res[]) {
int op = bytes[pos++] & 0xFF;
if (op < _meta_pop || op >= _meta_limit) return pos-1; // backup
op -= _meta_pop;
int FDef = op % 2;
int UDef = (op / 2) % 2;
int TDefL = (op / 4);
int TDef = (TDefL > 0)?1:0;
int L = LValuesCoded[TDefL];
CodingMethod[] FCode = {dflt}, TCode = {null}, UCode = {dflt};
if (FDef == 0)
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, FCode);
if (TDef == 0)
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, TCode);
if (UDef == 0)
pos = BandStructure.parseMetaCoding(bytes, pos, dflt, UCode);
PopulationCoding pop = new PopulationCoding();
pop.L = L; // might be -1
pop.favoredCoding = FCode[0];
pop.tokenCoding = TCode[0]; // might be null!
pop.unfavoredCoding = UCode[0];
res[0] = pop;
return pos;
}
private String keyString(CodingMethod m) {
if (m instanceof Coding)
return ((Coding)m).keyString();
if (m == null)
return "none";
return m.toString();
}
public String toString() {
PropMap p200 = Utils.currentPropMap();
boolean verbose
= (p200 != null &&
p200.getBoolean(Utils.COM_PREFIX+"verbose.pop"));
StringBuilder res = new StringBuilder(100);
res.append("pop(").append("fVlen=").append(fVlen);
if (verbose && fValues != null) {
res.append(" fV=[");
for (int i = 1; i <= fVlen; i++) {
res.append(i==1?"":",").append(fValues[i]);
}
res.append(";").append(computeSentinelValue());
res.append("]");
}
res.append(" fc=").append(keyString(favoredCoding));
res.append(" tc=").append(keyString(tokenCoding));
res.append(" uc=").append(keyString(unfavoredCoding));
res.append(")");
return res.toString();
}
}

View file

@ -1,332 +0,0 @@
/*
* Copyright (c) 2003, 2015, 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 java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.jar.Pack200;
/**
* Control block for publishing Pack200 options to the other classes.
*/
@SuppressWarnings({"removal"})
final class PropMap implements SortedMap<String, String> {
private final TreeMap<String, String> theMap = new TreeMap<>();;
// Override:
public String put(String key, String value) {
String oldValue = theMap.put(key, value);
return oldValue;
}
// All this other stuff is private to the current package.
// Outide clients of Pack200 do not need to use it; they can
// get by with generic SortedMap functionality.
private static Map<String, String> defaultProps;
static {
Properties props = new Properties();
// Allow implementation selected via -Dpack.disable.native=true
String propValue = getPropertyValue(Utils.DEBUG_DISABLE_NATIVE, "false");
props.put(Utils.DEBUG_DISABLE_NATIVE,
String.valueOf(Boolean.parseBoolean(propValue)));
// Set the DEBUG_VERBOSE from system
int verbose = 0;
try {
verbose = Integer.decode(getPropertyValue(Utils.DEBUG_VERBOSE, "0"));
} catch (NumberFormatException e) {
}
props.put(Utils.DEBUG_VERBOSE, String.valueOf(verbose));
// The segment size is unlimited
props.put(Pack200.Packer.SEGMENT_LIMIT, "-1");
// Preserve file ordering by default.
props.put(Pack200.Packer.KEEP_FILE_ORDER, Pack200.Packer.TRUE);
// Preserve all modification times by default.
props.put(Pack200.Packer.MODIFICATION_TIME, Pack200.Packer.KEEP);
// Preserve deflation hints by default.
props.put(Pack200.Packer.DEFLATE_HINT, Pack200.Packer.KEEP);
// Pass through files with unrecognized attributes by default.
props.put(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS);
// Pass through files with unrecognized format by default, also
// allow system property to be set
props.put(Utils.CLASS_FORMAT_ERROR,
getPropertyValue(Utils.CLASS_FORMAT_ERROR, Pack200.Packer.PASS));
// Default effort is 5, midway between 1 and 9.
props.put(Pack200.Packer.EFFORT, "5");
// Define certain attribute layouts by default.
// Do this after the previous props are put in place,
// to allow override if necessary.
String propFile = "intrinsic.properties";
PrivilegedAction<InputStream> pa =
() -> PackerImpl.class.getResourceAsStream(propFile);
try (InputStream propStr = AccessController.doPrivileged(pa)) {
if (propStr == null) {
throw new RuntimeException(propFile + " cannot be loaded");
}
props.load(propStr);
} catch (IOException ee) {
throw new RuntimeException(ee);
}
for (Map.Entry<Object, Object> e : props.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
if (key.startsWith("attribute.")) {
e.setValue(Attribute.normalizeLayoutString(val));
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
HashMap<String, String> temp = new HashMap(props); // shrink to fit
defaultProps = temp;
}
private static String getPropertyValue(String key, String defaultValue) {
PrivilegedAction<String> pa = () -> System.getProperty(key);
String s = AccessController.doPrivileged(pa);
return s != null ? s : defaultValue;
}
PropMap() {
theMap.putAll(defaultProps);
}
// Return a view of this map which includes only properties
// that begin with the given prefix. This is easy because
// the map is sorted, and has a subMap accessor.
SortedMap<String, String> prefixMap(String prefix) {
int len = prefix.length();
if (len == 0)
return this;
char nextch = (char)(prefix.charAt(len-1) + 1);
String limit = prefix.substring(0, len-1)+nextch;
//System.out.println(prefix+" => "+subMap(prefix, limit));
return subMap(prefix, limit);
}
String getProperty(String s) {
return get(s);
}
String getProperty(String s, String defaultVal) {
String val = getProperty(s);
if (val == null)
return defaultVal;
return val;
}
String setProperty(String s, String val) {
return put(s, val);
}
// Get sequence of props for "prefix", and "prefix.*".
List<String> getProperties(String prefix) {
Collection<String> values = prefixMap(prefix).values();
List<String> res = new ArrayList<>(values.size());
res.addAll(values);
while (res.remove(null));
return res;
}
private boolean toBoolean(String val) {
return Boolean.valueOf(val).booleanValue();
}
boolean getBoolean(String s) {
return toBoolean(getProperty(s));
}
boolean setBoolean(String s, boolean val) {
return toBoolean(setProperty(s, String.valueOf(val)));
}
int toInteger(String val) {
return toInteger(val, 0);
}
int toInteger(String val, int def) {
if (val == null) return def;
if (Pack200.Packer.TRUE.equals(val)) return 1;
if (Pack200.Packer.FALSE.equals(val)) return 0;
return Integer.parseInt(val);
}
int getInteger(String s, int def) {
return toInteger(getProperty(s), def);
}
int getInteger(String s) {
return toInteger(getProperty(s));
}
int setInteger(String s, int val) {
return toInteger(setProperty(s, String.valueOf(val)));
}
long toLong(String val) {
try {
return val == null ? 0 : Long.parseLong(val);
} catch (java.lang.NumberFormatException nfe) {
throw new IllegalArgumentException("Invalid value");
}
}
long getLong(String s) {
return toLong(getProperty(s));
}
long setLong(String s, long val) {
return toLong(setProperty(s, String.valueOf(val)));
}
int getTime(String s) {
String sval = getProperty(s, "0");
if (Utils.NOW.equals(sval)) {
return (int)((System.currentTimeMillis()+500)/1000);
}
long lval = toLong(sval);
final long recentSecondCount = 1000000000;
if (lval < recentSecondCount*10 && !"0".equals(sval))
Utils.log.warning("Supplied modtime appears to be seconds rather than milliseconds: "+sval);
return (int)((lval+500)/1000);
}
void list(PrintStream out) {
PrintWriter outw = new PrintWriter(out);
list(outw);
outw.flush();
}
void list(PrintWriter out) {
out.println("#"+Utils.PACK_ZIP_ARCHIVE_MARKER_COMMENT+"[");
Set<Map.Entry<String, String>> defaults = defaultProps.entrySet();
for (Map.Entry<String, String> e : theMap.entrySet()) {
if (defaults.contains(e)) continue;
out.println(" " + e.getKey() + " = " + e.getValue());
}
out.println("#]");
}
@Override
public int size() {
return theMap.size();
}
@Override
public boolean isEmpty() {
return theMap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return theMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return theMap.containsValue(value);
}
@Override
public String get(Object key) {
return theMap.get(key);
}
@Override
public String remove(Object key) {
return theMap.remove(key);
}
@Override
public void putAll(Map<? extends String, ? extends String> m) {
theMap.putAll(m);
}
@Override
public void clear() {
theMap.clear();
}
@Override
public Set<String> keySet() {
return theMap.keySet();
}
@Override
public Collection<String> values() {
return theMap.values();
}
@Override
public Set<Map.Entry<String, String>> entrySet() {
return theMap.entrySet();
}
@Override
public Comparator<? super String> comparator() {
return theMap.comparator();
}
@Override
public SortedMap<String, String> subMap(String fromKey, String toKey) {
return theMap.subMap(fromKey, toKey);
}
@Override
public SortedMap<String, String> headMap(String toKey) {
return theMap.headMap(toKey);
}
@Override
public SortedMap<String, String> tailMap(String fromKey) {
return theMap.tailMap(fromKey);
}
@Override
public String firstKey() {
return theMap.firstKey();
}
@Override
public String lastKey() {
return theMap.lastKey();
}
}

View file

@ -1,125 +0,0 @@
/*
* Copyright (c) 2010, 2012, 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.ClassEntry;
import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry;
import com.sun.java.util.jar.pack.ConstantPool.MemberEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodHandleEntry;
import com.sun.java.util.jar.pack.ConstantPool.MethodTypeEntry;
import com.sun.java.util.jar.pack.ConstantPool.InvokeDynamicEntry;
import com.sun.java.util.jar.pack.ConstantPool.BootstrapMethodEntry;
import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry;
import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
/*
* @author ksrini
*/
/*
* This class provides a container to hold the global variables, for packer
* and unpacker instances. This is typically stashed away in a ThreadLocal,
* and the storage is destroyed upon completion. Therefore any local
* references to these members must be eliminated appropriately to prevent a
* memory leak.
*/
class TLGlobals {
// Global environment
final PropMap props;
// Needed by ConstantPool.java
private final Map<String, Utf8Entry> utf8Entries;
private final Map<String, ClassEntry> classEntries;
private final Map<Object, LiteralEntry> literalEntries;
private final Map<String, SignatureEntry> signatureEntries;
private final Map<String, DescriptorEntry> descriptorEntries;
private final Map<String, MemberEntry> memberEntries;
private final Map<String, MethodHandleEntry> methodHandleEntries;
private final Map<String, MethodTypeEntry> methodTypeEntries;
private final Map<String, InvokeDynamicEntry> invokeDynamicEntries;
private final Map<String, BootstrapMethodEntry> bootstrapMethodEntries;
TLGlobals() {
utf8Entries = new HashMap<>();
classEntries = new HashMap<>();
literalEntries = new HashMap<>();
signatureEntries = new HashMap<>();
descriptorEntries = new HashMap<>();
memberEntries = new HashMap<>();
methodHandleEntries = new HashMap<>();
methodTypeEntries = new HashMap<>();
invokeDynamicEntries = new HashMap<>();
bootstrapMethodEntries = new HashMap<>();
props = new PropMap();
}
SortedMap<String, String> getPropMap() {
return props;
}
Map<String, Utf8Entry> getUtf8Entries() {
return utf8Entries;
}
Map<String, ClassEntry> getClassEntries() {
return classEntries;
}
Map<Object, LiteralEntry> getLiteralEntries() {
return literalEntries;
}
Map<String, DescriptorEntry> getDescriptorEntries() {
return descriptorEntries;
}
Map<String, SignatureEntry> getSignatureEntries() {
return signatureEntries;
}
Map<String, MemberEntry> getMemberEntries() {
return memberEntries;
}
Map<String, MethodHandleEntry> getMethodHandleEntries() {
return methodHandleEntries;
}
Map<String, MethodTypeEntry> getMethodTypeEntries() {
return methodTypeEntries;
}
Map<String, InvokeDynamicEntry> getInvokeDynamicEntries() {
return invokeDynamicEntries;
}
Map<String, BootstrapMethodEntry> getBootstrapMethodEntries() {
return bootstrapMethodEntries;
}
}

View file

@ -1,263 +0,0 @@
/*
* Copyright (c) 2003, 2015, 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 java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
/*
* Implementation of the Pack provider.
* </pre></blockquote>
* @author John Rose
* @author Kumar Srinivasan
*/
@SuppressWarnings({"removal"})
public class UnpackerImpl extends TLGlobals implements Pack200.Unpacker {
public UnpackerImpl() {}
/**
* Get the set of options for the pack and unpack engines.
* @return A sorted association of option key strings to option values.
*/
public SortedMap<String, String> properties() {
return props;
}
// Back-pointer to NativeUnpacker, when active.
Object _nunp;
public String toString() {
return Utils.getVersionString();
}
//Driver routines
// The unpack worker...
/**
* Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally
* the entire buffer must be read, it may be more efficient to read the packed-stream
* to a file and pass the File object, in the alternate method described below.
* <p>
* Closes its input but not its output. (The output can accumulate more elements.)
* @param in an InputStream.
* @param out a JarOutputStream.
* @exception IOException if an error is encountered.
*/
public synchronized void unpack(InputStream in, JarOutputStream out) throws IOException {
if (in == null) {
throw new NullPointerException("null input");
}
if (out == null) {
throw new NullPointerException("null output");
}
assert(Utils.currentInstance.get() == null);
try {
Utils.currentInstance.set(this);
final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);
BufferedInputStream in0 = new BufferedInputStream(in);
if (Utils.isJarMagic(Utils.readMagic(in0))) {
if (verbose > 0)
Utils.log.info("Copying unpacked JAR file...");
Utils.copyJarFile(new JarInputStream(in0), out);
} else if (props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) {
(new DoUnpack()).run(in0, out);
in0.close();
Utils.markJarFile(out);
} else {
try {
(new NativeUnpack(this)).run(in0, out);
} catch (UnsatisfiedLinkError | NoClassDefFoundError ex) {
// failover to java implementation
(new DoUnpack()).run(in0, out);
}
in0.close();
Utils.markJarFile(out);
}
} finally {
_nunp = null;
Utils.currentInstance.set(null);
}
}
/**
* Takes an input File containing the pack file, and generates a JarOutputStream.
* <p>
* Does not close its output. (The output can accumulate more elements.)
* @param in a File.
* @param out a JarOutputStream.
* @exception IOException if an error is encountered.
*/
public synchronized void unpack(File in, JarOutputStream out) throws IOException {
if (in == null) {
throw new NullPointerException("null input");
}
if (out == null) {
throw new NullPointerException("null output");
}
// Use the stream-based implementation.
// %%% Reconsider if native unpacker learns to memory-map the file.
try (FileInputStream instr = new FileInputStream(in)) {
unpack(instr, out);
}
if (props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) {
in.delete();
}
}
private class DoUnpack {
final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);
{
props.setInteger(Pack200.Unpacker.PROGRESS, 0);
}
// Here's where the bits are read from disk:
final Package pkg = new Package();
final boolean keepModtime
= Pack200.Packer.KEEP.equals(
props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP));
final boolean keepDeflateHint
= Pack200.Packer.KEEP.equals(
props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP));
final int modtime;
final boolean deflateHint;
{
if (!keepModtime) {
modtime = props.getTime(Utils.UNPACK_MODIFICATION_TIME);
} else {
modtime = pkg.default_modtime;
}
deflateHint = (keepDeflateHint) ? false :
props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT);
}
// Checksum apparatus.
final CRC32 crc = new CRC32();
final ByteArrayOutputStream bufOut = new ByteArrayOutputStream();
final OutputStream crcOut = new CheckedOutputStream(bufOut, crc);
public void run(BufferedInputStream in, JarOutputStream out) throws IOException {
if (verbose > 0) {
props.list(System.out);
}
for (int seg = 1; ; seg++) {
unpackSegment(in, out);
// Try to get another segment.
if (!Utils.isPackMagic(Utils.readMagic(in))) break;
if (verbose > 0)
Utils.log.info("Finished segment #"+seg);
}
}
private void unpackSegment(InputStream in, JarOutputStream out) throws IOException {
props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0");
// Process the output directory or jar output.
new PackageReader(pkg, in).read();
if (props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug");
if (props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile");
props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50");
pkg.ensureAllClassFiles();
// Now write out the files.
Set<Package.Class> classesToWrite = new HashSet<>(pkg.getClasses());
for (Package.File file : pkg.getFiles()) {
String name = file.nameString;
JarEntry je = new JarEntry(Utils.getJarEntryName(name));
boolean deflate;
deflate = (keepDeflateHint)
? (((file.options & Constants.FO_DEFLATE_HINT) != 0) ||
((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0))
: deflateHint;
boolean needCRC = !deflate; // STORE mode requires CRC
if (needCRC) crc.reset();
bufOut.reset();
if (file.isClassStub()) {
Package.Class cls = file.getStubClass();
assert(cls != null);
new ClassWriter(cls, needCRC ? crcOut : bufOut).write();
classesToWrite.remove(cls); // for an error check
} else {
// collect data & maybe CRC
file.writeTo(needCRC ? crcOut : bufOut);
}
je.setMethod(deflate ? JarEntry.DEFLATED : JarEntry.STORED);
if (needCRC) {
if (verbose > 0)
Utils.log.info("stored size="+bufOut.size()+" and crc="+crc.getValue());
je.setMethod(JarEntry.STORED);
je.setSize(bufOut.size());
je.setCrc(crc.getValue());
}
if (keepModtime) {
LocalDateTime ldt = LocalDateTime
.ofEpochSecond(file.modtime, 0, ZoneOffset.UTC);
je.setTimeLocal(ldt);
} else {
je.setTime((long)modtime * 1000);
}
out.putNextEntry(je);
bufOut.writeTo(out);
out.closeEntry();
if (verbose > 0)
Utils.log.info("Writing "+Utils.zeString((ZipEntry)je));
}
assert(classesToWrite.isEmpty());
props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100");
pkg.reset(); // reset for the next segment, if any
}
}
}

View file

@ -1,320 +0,0 @@
/*
* Copyright (c) 2003, 2015, 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 java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Date;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import sun.util.logging.PlatformLogger;
class Utils {
static final String COM_PREFIX = "com.sun.java.util.jar.pack.";
static final String METAINF = "META-INF";
/*
* Outputs various diagnostic support information.
* If >0, print summary comments (e.g., constant pool info).
* If >1, print unit comments (e.g., processing of classes).
* If >2, print many comments (e.g., processing of members).
* If >3, print tons of comments (e.g., processing of references).
* (installer only)
*/
static final String DEBUG_VERBOSE = COM_PREFIX+"verbose";
/*
* Disables use of native code, prefers the Java-coded implementation.
* (installer only)
*/
static final String DEBUG_DISABLE_NATIVE = COM_PREFIX+"disable.native";
/*
* Property indicating that the unpacker should
* ignore the transmitted PACK_MODIFICATION_TIME,
* replacing it by the given value. The value can
* be a numeric string, representing the number of
* mSecs since the epoch (UTC), or the special string
* {@link #NOW}, meaning the current time (UTC).
* The default value is the special string {@link #KEEP},
* which asks the unpacker to preserve all transmitted
* modification time information.
* (installer only)
*/
static final String UNPACK_MODIFICATION_TIME = COM_PREFIX+"unpack.modification.time";
/*
* Property indicating that the unpacker strip the
* Debug Attributes, if they are present, in the pack stream.
* The default value is false.
* (installer only)
*/
static final String UNPACK_STRIP_DEBUG = COM_PREFIX+"unpack.strip.debug";
/*
* Remove the input file after unpacking.
* (installer only)
*/
static final String UNPACK_REMOVE_PACKFILE = COM_PREFIX+"unpack.remove.packfile";
/*
* A possible value for MODIFICATION_TIME
*/
static final String NOW = "now";
// Other debug options:
// com...debug.bands=false add band IDs to pack file, to verify sync
// com...dump.bands=false dump band contents to local disk
// com...no.vary.codings=false turn off coding variation heuristics
// com...no.big.strings=false turn off "big string" feature
/*
* If this property is set to {@link #TRUE}, the packer will preserve
* the ordering of class files of the original jar in the output archive.
* The ordering is preserved only for class-files; resource files
* may be reordered.
* <p>
* If the packer is allowed to reorder class files, it can marginally
* decrease the transmitted size of the archive.
*/
static final String PACK_KEEP_CLASS_ORDER = COM_PREFIX+"keep.class.order";
/*
* This string PACK200 is given as a zip comment on all JAR files
* produced by this utility.
*/
static final String PACK_ZIP_ARCHIVE_MARKER_COMMENT = "PACK200";
/*
* behaviour when we hit a class format error, but not necessarily
* an unknown attribute, by default it is allowed to PASS.
*/
static final String CLASS_FORMAT_ERROR = COM_PREFIX+"class.format.error";
// Keep a TLS point to the global data and environment.
// This makes it simpler to supply environmental options
// to the engine code, especially the native code.
static final ThreadLocal<TLGlobals> currentInstance = new ThreadLocal<>();
// convenience method to access the TL globals
static TLGlobals getTLGlobals() {
return currentInstance.get();
}
static PropMap currentPropMap() {
Object obj = currentInstance.get();
if (obj instanceof PackerImpl)
return ((PackerImpl)obj).props;
if (obj instanceof UnpackerImpl)
return ((UnpackerImpl)obj).props;
return null;
}
static final boolean nolog
= Boolean.getBoolean(COM_PREFIX+"nolog");
static final boolean SORT_MEMBERS_DESCR_MAJOR
= Boolean.getBoolean(COM_PREFIX+"sort.members.descr.major");
static final boolean SORT_HANDLES_KIND_MAJOR
= Boolean.getBoolean(COM_PREFIX+"sort.handles.kind.major");
static final boolean SORT_INDY_BSS_MAJOR
= Boolean.getBoolean(COM_PREFIX+"sort.indy.bss.major");
static final boolean SORT_BSS_BSM_MAJOR
= Boolean.getBoolean(COM_PREFIX+"sort.bss.bsm.major");
static class Pack200Logger {
private final String name;
private PlatformLogger log;
Pack200Logger(String name) {
this.name = name;
}
private synchronized PlatformLogger getLogger() {
if (log == null) {
log = PlatformLogger.getLogger(name);
}
return log;
}
public void warning(String msg, Object param) {
getLogger().warning(msg, param);
}
public void warning(String msg) {
warning(msg, null);
}
public void info(String msg) {
int verbose = currentPropMap().getInteger(DEBUG_VERBOSE);
if (verbose > 0) {
if (nolog) {
System.out.println(msg);
} else {
getLogger().info(msg);
}
}
}
public void fine(String msg) {
int verbose = currentPropMap().getInteger(DEBUG_VERBOSE);
if (verbose > 0) {
System.out.println(msg);
}
}
}
static final Pack200Logger log
= new Pack200Logger("java.util.jar.Pack200");
// Returns the Max Version String of this implementation
static String getVersionString() {
return "Pack200, Vendor: " +
System.getProperty("java.vendor") +
", Version: " + Constants.MAX_PACKAGE_VERSION;
}
static void markJarFile(JarOutputStream out) throws IOException {
out.setComment(PACK_ZIP_ARCHIVE_MARKER_COMMENT);
}
// -0 mode helper
static void copyJarFile(JarInputStream in, JarOutputStream out) throws IOException {
if (in.getManifest() != null) {
ZipEntry me = new ZipEntry(JarFile.MANIFEST_NAME);
out.putNextEntry(me);
in.getManifest().write(out);
out.closeEntry();
}
byte[] buffer = new byte[1 << 14];
for (JarEntry je; (je = in.getNextJarEntry()) != null; ) {
out.putNextEntry(je);
for (int nr; 0 < (nr = in.read(buffer)); ) {
out.write(buffer, 0, nr);
}
}
in.close();
markJarFile(out); // add PACK200 comment
}
static void copyJarFile(JarFile in, JarOutputStream out) throws IOException {
byte[] buffer = new byte[1 << 14];
for (JarEntry je : Collections.list(in.entries())) {
out.putNextEntry(je);
InputStream ein = in.getInputStream(je);
for (int nr; 0 < (nr = ein.read(buffer)); ) {
out.write(buffer, 0, nr);
}
}
in.close();
markJarFile(out); // add PACK200 comment
}
static void copyJarFile(JarInputStream in, OutputStream out) throws IOException {
// 4947205 : Peformance is slow when using pack-effort=0
out = new BufferedOutputStream(out);
out = new NonCloser(out); // protect from JarOutputStream.close()
try (JarOutputStream jout = new JarOutputStream(out)) {
copyJarFile(in, jout);
}
}
static void copyJarFile(JarFile in, OutputStream out) throws IOException {
// 4947205 : Peformance is slow when using pack-effort=0
out = new BufferedOutputStream(out);
out = new NonCloser(out); // protect from JarOutputStream.close()
try (JarOutputStream jout = new JarOutputStream(out)) {
copyJarFile(in, jout);
}
}
// Wrapper to prevent closing of client-supplied stream.
private static
class NonCloser extends FilterOutputStream {
NonCloser(OutputStream out) { super(out); }
public void close() throws IOException { flush(); }
}
static String getJarEntryName(String name) {
if (name == null) return null;
return name.replace(File.separatorChar, '/');
}
static String zeString(ZipEntry ze) {
int store = (ze.getCompressedSize() > 0) ?
(int)( (1.0 - ((double)ze.getCompressedSize()/(double)ze.getSize()))*100 )
: 0 ;
// Follow unzip -lv output
return ze.getSize() + "\t" + ze.getMethod()
+ "\t" + ze.getCompressedSize() + "\t"
+ store + "%\t"
+ new Date(ze.getTime()) + "\t"
+ Long.toHexString(ze.getCrc()) + "\t"
+ ze.getName() ;
}
static byte[] readMagic(BufferedInputStream in) throws IOException {
in.mark(4);
byte[] magic = new byte[4];
for (int i = 0; i < magic.length; i++) {
// read 1 byte at a time, so we always get 4
if (1 != in.read(magic, i, 1))
break;
}
in.reset();
return magic;
}
// magic number recognizers
static boolean isJarMagic(byte[] magic) {
return (magic[0] == (byte)'P' &&
magic[1] == (byte)'K' &&
magic[2] >= 1 &&
magic[2] < 8 &&
magic[3] == magic[2] + 1);
}
static boolean isPackMagic(byte[] magic) {
return (magic[0] == (byte)0xCA &&
magic[1] == (byte)0xFE &&
magic[2] == (byte)0xD0 &&
magic[3] == (byte)0x0D);
}
static boolean isGZIPMagic(byte[] magic) {
return (magic[0] == (byte)0x1F &&
magic[1] == (byte)0x8B &&
magic[2] == (byte)0x08);
// fourth byte is variable "flg" field
}
private Utils() { } // do not instantiate
}

View file

@ -1,48 +0,0 @@
# Properties which are known to Pack without further instruction,
# but are not part of the JSR 200 standard.
# They amount to default Pack properties.
# Additional standard properties are hardwired into the code,
# but may be overridden here, if necessary.
# The makefile for this directory must copy this file
# into the target class hierarchy so it will get into runtime image.
# JCOV attributes
pack.code.attribute.CoverageTable = NH[PHHII]
pack.code.attribute.CharacterRangeTable = NH[PHPOHIIH]
pack.class.attribute.SourceID = RUH
pack.class.attribute.CompilationID = RUH
pack.class.attribute.NestHost = RCH
pack.class.attribute.NestMembers = NH[RCH]
# Note: Zero-length ("marker") attributes do not need to be specified here.
# They are automatically defined to have an empty layout.
#pack.class.attribute.Deprecated =
#pack.field.attribute.Deprecated =
#pack.method.attribute.Deprecated =
## Example layouts for builtins. (Do not uncomment.)
#pack.class.attribute.Signature = RSH
#pack.field.attribute.Signature = RSH
#pack.method.attribute.Signature = RSH
# Signature = { signature:u2 }
#pack.class.attribute.SourceFile = RUH
# SourceFile = { utf8:u2 }
#pack.class.attribute.InnerClasses = NH[RCH RCNH RUNH FH]
# InnerClasses = { (N:u2)*{ class:u2 class?:u2 utf8?:u2 flags:u2 } }
#pack.method.attribute.Exceptions = NH[RCH]
# Exceptions = { (N:u2)*{ class:u2 } }
#pack.code.attribute.StackMap = \
# [NH[PH NH[(1)] NH[(1)]]] \
# [TB (7)[RCH] (8,9)[PH] ()[]]
#pack.code.attribute.LineNumberTable = NH[PH H]
#pack.code.attribute.LocalVariableTable = NH[PH OH RUH RSH H]
# Set this property if the classfile version number is bumped,
# as long as the format remains compatible with Pack200.
#com.sun.java.util.jar.pack.max.class.majver = 59
# Uncomment this line if the C-coded unpacker seems broken.
#com.sun.java.util.jar.pack.disable.native = true

View file

@ -1,101 +0,0 @@
/*
* Copyright (c) 2003, 2011, 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.
*/
/**
* This package provides methods to read files from a JAR file and to
* transform them to a more compact transfer format called Pack200.
* It also provides methods to receive the transmitted data and expand
* it into a JAR file equivalent to the original JAR file.
*
* <p>
* The {@code pack} methods may be used by application developers who
* wish to deploy large JARs on the web. The {@code unpack} methods
* may be used by deployment applications such as Java Web Start and
* Java Plugin.
*
* <p>
* In typical use, the packed output should be further compressed
* using a suitable tool such as gzip or
* {@code java.util.zip.GZIPOutputStream}. The resulting file (with
* a suffix ".pack.gz") should be hosted on a HTTP/1.1 compliant
* server, which will be capable of handling "Accept-Encoding", as
* specified by the HTTP 1.1 RFC2616 specification.
*
* <p>
* <b>NOTE:</b> It is recommended that the original ".jar" file be
* hosted in addition to the ".pack.gz" file, so that older client
* implementations will continue to work reliably. (On-demand
* compression by the server is not recommended.)
*
* <p>
* When a client application requests a ".jar" file (call it
* "Large.jar"), the client will transmit the headers
* "Content-Type=application/x-java-archive" as well as
* "Accept-Encoding=pack200-gzip". This indicates to the server that
* the client application desires an version of the file encoded with
* Pack200 and further compressed with gzip.
*
* <p>
* The server implementation will typically check for the existence of
* "Large.pack.gz". If that file is available, the server will
* transmit it with the headers "Content-Encoding=pack200-gzip" and
* "Content-Type=application/x-java-archive".
*
* <p>
* If the ".pack.gz" file, is not available, then the server will
* transmit the original ".jar" with "Content-Encoding=null" and
* "Content-Type=application/x-java-archive".
*
* <p>
* A MIME type of "application/x-java-pack200" may be specified by the
* client application to indicate a ".pack" file is required.
* However, this has limited capability, and is not recommended.
*
* <h2> Package Specification</h2>
* Network Transfer Format Specification :<a href="http://jcp.org/en/jsr/detail?id=200">
* http://jcp.org/en/jsr/detail?id=200</a>
*
* <h2> Related Documentation</h2>
* For overviews, tutorials, examples, guides, and tool documentation, please
* see:
* <ul>
*
* <li>
* Jar File Specification :<a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html">
* http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html</a></li>
*
* <li>
* Class File Specification: Chapter 4 of
* <em>The Java&trade; Virtual Machine Specification</em>
*
* <li>
* Hypertext Transfer Protocol -- HTTP/1.1 : <a href="http://www.ietf.org/rfc/rfc2616.txt">
* http://www.ietf.org/rfc/rfc2616.txt
* </ul>
*
* <li>
* @since 1.5</li>
*/
package com.sun.java.util.jar.pack;