feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
298
jdkSrc/jdk8/com/sun/java/util/jar/pack/AdaptiveCoding.java
Normal file
298
jdkSrc/jdk8/com/sun/java/util/jar/pack/AdaptiveCoding.java
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
//*/
|
||||
|
||||
}
|
||||
1698
jdkSrc/jdk8/com/sun/java/util/jar/pack/Attribute.java
Normal file
1698
jdkSrc/jdk8/com/sun/java/util/jar/pack/Attribute.java
Normal file
File diff suppressed because it is too large
Load Diff
2761
jdkSrc/jdk8/com/sun/java/util/jar/pack/BandStructure.java
Normal file
2761
jdkSrc/jdk8/com/sun/java/util/jar/pack/BandStructure.java
Normal file
File diff suppressed because it is too large
Load Diff
647
jdkSrc/jdk8/com/sun/java/util/jar/pack/ClassReader.java
Normal file
647
jdkSrc/jdk8/com/sun/java/util/jar/pack/ClassReader.java
Normal file
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.java.util.jar.pack;
|
||||
|
||||
import com.sun.java.util.jar.pack.ConstantPool.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;
|
||||
}
|
||||
|
||||
private Entry checkValid(Entry e) {
|
||||
if (e == INVALID_ENTRY) {
|
||||
throw new IllegalStateException("Invalid constant pool reference");
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// use this identity for invalid references
|
||||
private static final Entry INVALID_ENTRY = new Entry((byte) -1) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
throw new IllegalStateException("Should not call this");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int computeValueHash() {
|
||||
throw new IllegalStateException("Should not call this");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
throw new IllegalStateException("Should not call this");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stringValue() {
|
||||
throw new IllegalStateException("Should not call this");
|
||||
}
|
||||
};
|
||||
|
||||
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] = INVALID_ENTRY;
|
||||
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] = INVALID_ENTRY;
|
||||
}
|
||||
break;
|
||||
case CONSTANT_Double:
|
||||
{
|
||||
cpMap[i] = ConstantPool.getLiteralEntry(in.readDouble());
|
||||
cpMap[++i] = INVALID_ENTRY;
|
||||
}
|
||||
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 && checkValid(cpMap[ref]) == null || ref2 >= 0 && checkValid(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 this 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 {
|
||||
private static final long serialVersionUID = -3564121733989501833L;
|
||||
|
||||
public ClassFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ClassFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
317
jdkSrc/jdk8/com/sun/java/util/jar/pack/ClassWriter.java
Normal file
317
jdkSrc/jdk8/com/sun/java/util/jar/pack/ClassWriter.java
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
398
jdkSrc/jdk8/com/sun/java/util/jar/pack/Code.java
Normal file
398
jdkSrc/jdk8/com/sun/java/util/jar/pack/Code.java
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
static private 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;
|
||||
}
|
||||
//*/
|
||||
}
|
||||
906
jdkSrc/jdk8/com/sun/java/util/jar/pack/Coding.java
Normal file
906
jdkSrc/jdk8/com/sun/java/util/jar/pack/Coding.java
Normal file
@@ -0,0 +1,906 @@
|
||||
/*
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
static private 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;
|
||||
}
|
||||
}
|
||||
1489
jdkSrc/jdk8/com/sun/java/util/jar/pack/CodingChooser.java
Normal file
1489
jdkSrc/jdk8/com/sun/java/util/jar/pack/CodingChooser.java
Normal file
File diff suppressed because it is too large
Load Diff
43
jdkSrc/jdk8/com/sun/java/util/jar/pack/CodingMethod.java
Normal file
43
jdkSrc/jdk8/com/sun/java/util/jar/pack/CodingMethod.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
1658
jdkSrc/jdk8/com/sun/java/util/jar/pack/ConstantPool.java
Normal file
1658
jdkSrc/jdk8/com/sun/java/util/jar/pack/ConstantPool.java
Normal file
File diff suppressed because it is too large
Load Diff
491
jdkSrc/jdk8/com/sun/java/util/jar/pack/Constants.java
Normal file
491
jdkSrc/jdk8/com/sun/java/util/jar/pack/Constants.java
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shared constants
|
||||
* @author John Rose
|
||||
*/
|
||||
class Constants {
|
||||
|
||||
private Constants(){}
|
||||
|
||||
public final static 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.5.x 50,0
|
||||
1.7 to 1.6.x 51,0
|
||||
1.8 to 1.7.x 52,0
|
||||
*/
|
||||
|
||||
public final static Package.Version JAVA_MIN_CLASS_VERSION =
|
||||
Package.Version.of(45, 03);
|
||||
|
||||
public final static Package.Version JAVA5_MAX_CLASS_VERSION =
|
||||
Package.Version.of(49, 00);
|
||||
|
||||
public final static Package.Version JAVA6_MAX_CLASS_VERSION =
|
||||
Package.Version.of(50, 00);
|
||||
|
||||
public final static Package.Version JAVA7_MAX_CLASS_VERSION =
|
||||
Package.Version.of(51, 00);
|
||||
|
||||
public final static Package.Version JAVA8_MAX_CLASS_VERSION =
|
||||
Package.Version.of(52, 00);
|
||||
|
||||
public final static int JAVA_PACKAGE_MAGIC = 0xCAFED00D;
|
||||
|
||||
public final static Package.Version JAVA5_PACKAGE_VERSION =
|
||||
Package.Version.of(150, 7);
|
||||
|
||||
public final static Package.Version JAVA6_PACKAGE_VERSION =
|
||||
Package.Version.of(160, 1);
|
||||
|
||||
public final static Package.Version JAVA7_PACKAGE_VERSION =
|
||||
Package.Version.of(170, 1);
|
||||
|
||||
public final static Package.Version JAVA8_PACKAGE_VERSION =
|
||||
Package.Version.of(171, 0);
|
||||
|
||||
// upper limit, should point to the latest class version
|
||||
public final static Package.Version JAVA_MAX_CLASS_VERSION =
|
||||
JAVA8_MAX_CLASS_VERSION;
|
||||
|
||||
// upper limit should point to the latest package version, for version info!.
|
||||
public final static Package.Version MAX_PACKAGE_VERSION =
|
||||
JAVA7_PACKAGE_VERSION;
|
||||
|
||||
public final static int CONSTANT_POOL_INDEX_LIMIT = 0x10000;
|
||||
public final static int CONSTANT_POOL_NARROW_LIMIT = 0x00100;
|
||||
|
||||
public final static String JAVA_SIGNATURE_CHARS = "BSCIJFDZLV([";
|
||||
|
||||
public final static byte CONSTANT_Utf8 = 1;
|
||||
public final static byte CONSTANT_unused2 = 2; // unused, was Unicode
|
||||
public final static byte CONSTANT_Integer = 3;
|
||||
public final static byte CONSTANT_Float = 4;
|
||||
public final static byte CONSTANT_Long = 5;
|
||||
public final static byte CONSTANT_Double = 6;
|
||||
public final static byte CONSTANT_Class = 7;
|
||||
public final static byte CONSTANT_String = 8;
|
||||
public final static byte CONSTANT_Fieldref = 9;
|
||||
public final static byte CONSTANT_Methodref = 10;
|
||||
public final static byte CONSTANT_InterfaceMethodref = 11;
|
||||
public final static byte CONSTANT_NameandType = 12;
|
||||
public final static byte CONSTANT_unused13 = 13;
|
||||
public final static byte CONSTANT_unused14 = 14;
|
||||
public final static byte CONSTANT_MethodHandle = 15;
|
||||
public final static byte CONSTANT_MethodType = 16;
|
||||
public final static byte CONSTANT_unused17 = 17; // unused
|
||||
public final static byte CONSTANT_InvokeDynamic = 18;
|
||||
|
||||
// pseudo-constants:
|
||||
public final static byte CONSTANT_None = 0;
|
||||
public final static byte CONSTANT_Signature = CONSTANT_unused13;
|
||||
public final static byte CONSTANT_BootstrapMethod = CONSTANT_unused17; // used only in InvokeDynamic constants
|
||||
public final static byte CONSTANT_Limit = 19;
|
||||
|
||||
public final static byte CONSTANT_All = 50; // combined global map
|
||||
public final static byte CONSTANT_LoadableValue = 51; // used for 'KL' and qldc operands
|
||||
public final static byte CONSTANT_AnyMember = 52; // union of refs to field or (interface) method
|
||||
public final static byte CONSTANT_FieldSpecific = 53; // used only for 'KQ' ConstantValue attrs
|
||||
public final static byte CONSTANT_GroupFirst = CONSTANT_All;
|
||||
public final static byte CONSTANT_GroupLimit = CONSTANT_FieldSpecific+1;
|
||||
|
||||
// CONSTANT_MethodHandle reference kinds
|
||||
public final static byte REF_getField = 1;
|
||||
public final static byte REF_getStatic = 2;
|
||||
public final static byte REF_putField = 3;
|
||||
public final static byte REF_putStatic = 4;
|
||||
public final static byte REF_invokeVirtual = 5;
|
||||
public final static byte REF_invokeStatic = 6;
|
||||
public final static byte REF_invokeSpecial = 7;
|
||||
public final static byte REF_newInvokeSpecial = 8;
|
||||
public final static byte REF_invokeInterface = 9;
|
||||
|
||||
// pseudo-access bits
|
||||
public final static 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 final static int[] noInts = {};
|
||||
public final static byte[] noBytes = {};
|
||||
public final static Object[] noValues = {};
|
||||
public final static String[] noStrings = {};
|
||||
public final static List<Object> emptyList = Arrays.asList(noValues);
|
||||
|
||||
// meta-coding
|
||||
public final static 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 final static 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 final static int _end_marker = 255;
|
||||
// Escapes:
|
||||
public final static int _byte_escape = 254;
|
||||
public final static 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 final static int _first_linker_op = _getstatic;
|
||||
public final static int _last_linker_op = _invokestatic;
|
||||
public final static int _num_linker_ops = (_last_linker_op - _first_linker_op) + 1;
|
||||
public final static int _self_linker_op = _bytecode_limit;
|
||||
public final static int _self_linker_aload_flag = 1*_num_linker_ops;
|
||||
public final static int _self_linker_super_flag = 2*_num_linker_ops;
|
||||
public final static 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 final static int _invokeinit_op = _self_linker_limit;
|
||||
public final static int _invokeinit_self_option = 0;
|
||||
public final static int _invokeinit_super_option = 1;
|
||||
public final static int _invokeinit_new_option = 2;
|
||||
public final static int _invokeinit_limit = _invokeinit_op+3;
|
||||
|
||||
public final static 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 final static int _xldc_op = _invokeinit_limit;
|
||||
public final static int _sldc = _ldc; // previously named _aldc
|
||||
public final static int _cldc = _xldc_op+0;
|
||||
public final static int _ildc = _xldc_op+1;
|
||||
public final static int _fldc = _xldc_op+2;
|
||||
public final static int _sldc_w = _ldc_w; // previously named _aldc_w
|
||||
public final static int _cldc_w = _xldc_op+3;
|
||||
public final static int _ildc_w = _xldc_op+4;
|
||||
public final static int _fldc_w = _xldc_op+5;
|
||||
public final static int _lldc2_w = _ldc2_w;
|
||||
public final static int _dldc2_w = _xldc_op+6;
|
||||
// anything other than primitive, string, or class must be handled with qldc:
|
||||
public final static int _qldc = _xldc_op+7;
|
||||
public final static int _qldc_w = _xldc_op+8;
|
||||
public final static int _xldc_limit = _xldc_op+9;
|
||||
|
||||
// handling of InterfaceMethodRef
|
||||
public final static int _invoke_int_op = _xldc_limit;
|
||||
public final static int _invokespecial_int = _invoke_int_op+0;
|
||||
public final static int _invokestatic_int = _invoke_int_op+1;
|
||||
public final static int _invoke_int_limit = _invoke_int_op+2;
|
||||
}
|
||||
728
jdkSrc/jdk8/com/sun/java/util/jar/pack/Driver.java
Normal file
728
jdkSrc/jdk8/com/sun/java/util/jar/pack/Driver.java
Normal file
@@ -0,0 +1,728 @@
|
||||
/*
|
||||
* 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.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.
|
||||
*/
|
||||
class Driver {
|
||||
private static final ResourceBundle RESOURCE =
|
||||
ResourceBundle.getBundle("com.sun.java.util.jar.pack.DriverResource");
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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(1);
|
||||
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.equals("")) {
|
||||
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.equals("")) {
|
||||
// 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.length() > 0)
|
||||
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.equals("")? 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.equals("")) {
|
||||
// On success, abort jarfile recovery bracket.
|
||||
new File(bakfile).delete();
|
||||
bakfile = "";
|
||||
}
|
||||
|
||||
} finally {
|
||||
// Close jarfile recovery bracket.
|
||||
if (!bakfile.equals("")) {
|
||||
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.equals(""))
|
||||
new File(tmpfile).delete();
|
||||
}
|
||||
}
|
||||
|
||||
static private
|
||||
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();
|
||||
}
|
||||
|
||||
static private
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private
|
||||
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, "UTF8");
|
||||
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.length() == 0 && 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.length() != 0);
|
||||
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.length() != 0)? specarg.intern(): opt;
|
||||
break doArgs;
|
||||
case '?': // abort the option sequence
|
||||
resultString = (specarg.length() != 0)? specarg.intern(): arg;
|
||||
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.length() != 0)? specarg.intern(): opt;
|
||||
properties.remove(negopt);
|
||||
properties.put(negopt, null); // leave placeholder
|
||||
didAction = true;
|
||||
break;
|
||||
case '$': // normal "boolean" option
|
||||
String boolval;
|
||||
if (specarg.length() != 0) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
120
jdkSrc/jdk8/com/sun/java/util/jar/pack/DriverResource.java
Normal file
120
jdkSrc/jdk8/com/sun/java/util/jar/pack/DriverResource.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 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.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";
|
||||
|
||||
/*
|
||||
* 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",
|
||||
" -g, --no-gzip output a plain *.pack file with no zipping",
|
||||
" --gzip (default) post-process the pack output with gzip",
|
||||
" -G, --strip-debug remove debugging attributes 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} output segment limit (default N=1Mb)",
|
||||
" -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) uncompressed",
|
||||
" -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 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"
|
||||
}
|
||||
},
|
||||
{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 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
|
||||
};
|
||||
|
||||
protected Object[][] getContents() {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
120
jdkSrc/jdk8/com/sun/java/util/jar/pack/DriverResource_ja.java
Normal file
120
jdkSrc/jdk8/com/sun/java/util/jar/pack/DriverResource_ja.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.sun.java.util.jar.pack;
|
||||
|
||||
import 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";
|
||||
|
||||
/*
|
||||
* 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",
|
||||
" -g\u3001--no-gzip \u5727\u7E2E\u305B\u305A\u306B\u30D7\u30EC\u30FC\u30F3\u306A*.pack\u30D5\u30A1\u30A4\u30EB\u3092\u51FA\u529B\u3057\u307E\u3059",
|
||||
" --gzip (\u30C7\u30D5\u30A9\u30EB\u30C8) pack\u51FA\u529B\u3092gzip\u3067\u5F8C\u51E6\u7406\u3057\u307E\u3059",
|
||||
" -G\u3001--strip-debug \u5727\u7E2E\u4E2D\u306B\u30C7\u30D0\u30C3\u30B0\u5C5E\u6027\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\u5236\u9650\u3092\u51FA\u529B\u3057\u307E\u3059(\u30C7\u30D5\u30A9\u30EB\u30C8N=1Mb)",
|
||||
" -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\u5727\u7E2E\u3055\u308C\u3066\u3044\u306A\u3044\u5165\u529B\u8981\u7D20\u3092\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\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"
|
||||
}
|
||||
},
|
||||
{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\u306FSystem.out ('-'\u306E\u5834\u5408)\u306B\u51FA\u529B\u3057\u307E\u3059",
|
||||
" -?\u3001-h\u3001--help \u3053\u306E\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
|
||||
};
|
||||
|
||||
protected Object[][] getContents() {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
120
jdkSrc/jdk8/com/sun/java/util/jar/pack/DriverResource_zh_CN.java
Normal file
120
jdkSrc/jdk8/com/sun/java/util/jar/pack/DriverResource_zh_CN.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2014, 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";
|
||||
|
||||
/*
|
||||
* 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\u91CD\u65B0\u6253\u5305: {0}"}, // parameter 0:filename
|
||||
{PACK_HELP, new String[] {
|
||||
"\u7528\u6CD5: pack200 [-opt... | --option=value]... x.pack[.gz] y.jar",
|
||||
"",
|
||||
"\u6253\u5305\u9009\u9879",
|
||||
" -g, --no-gzip \u8F93\u51FA\u65E0\u683C\u5F0F\u7684 *.pack \u6587\u4EF6, \u4E0D\u538B\u7F29",
|
||||
" --gzip (\u9ED8\u8BA4\u503C) \u4F7F\u7528 gzip \u5BF9\u6253\u5305\u8FDB\u884C\u540E\u5904\u7406",
|
||||
" -G, --strip-debug \u6253\u5305\u65F6\u5220\u9664\u8C03\u8BD5\u5C5E\u6027",
|
||||
" -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} \u8F93\u51FA\u6BB5\u9650\u5236 (\u9ED8\u8BA4\u503C N=1Mb)",
|
||||
" -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\u89E3\u538B\u7F29\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\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"
|
||||
}
|
||||
},
|
||||
{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\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
|
||||
};
|
||||
|
||||
protected Object[][] getContents() {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
174
jdkSrc/jdk8/com/sun/java/util/jar/pack/FixedList.java
Normal file
174
jdkSrc/jdk8/com/sun/java/util/jar/pack/FixedList.java
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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 + '}';
|
||||
}
|
||||
}
|
||||
|
||||
570
jdkSrc/jdk8/com/sun/java/util/jar/pack/Fixups.java
Normal file
570
jdkSrc/jdk8/com/sun/java/util/jar/pack/Fixups.java
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
818
jdkSrc/jdk8/com/sun/java/util/jar/pack/Histogram.java
Normal file
818
jdkSrc/jdk8/com/sun/java/util/jar/pack/Histogram.java
Normal file
@@ -0,0 +1,818 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
static private
|
||||
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);
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
688
jdkSrc/jdk8/com/sun/java/util/jar/pack/Instruction.java
Normal file
688
jdkSrc/jdk8/com/sun/java/util/jar/pack/Instruction.java
Normal file
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* 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 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.
|
||||
|
||||
static private final byte[][] BC_LENGTH = new byte[2][0x100];
|
||||
static private final byte[][] BC_INDEX = new byte[2][0x100];
|
||||
static private final byte[][] BC_TAG = new byte[2][0x100];
|
||||
static private final byte[][] BC_BRANCH = new byte[2][0x100];
|
||||
static private final byte[][] BC_SLOT = new byte[2][0x100];
|
||||
static private final byte[][] BC_CON = new byte[2][0x100];
|
||||
static private final String[] BC_NAME = new String[0x100]; // debug only
|
||||
static private 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 {
|
||||
private static final long serialVersionUID = 3175572275651367015L;
|
||||
|
||||
FormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
410
jdkSrc/jdk8/com/sun/java/util/jar/pack/NativeUnpack.java
Normal file
410
jdkSrc/jdk8/com/sun/java/util/jar/pack/NativeUnpack.java
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* 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.LinkedList;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
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.
|
||||
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 static final int MAX_BUFFER_SIZE = 1 << 20; // 1 MB byte[]
|
||||
private byte[] _buf = new byte[1 << 14]; // 16 KB byte[] initially
|
||||
private List<byte[]> _extra_buf = new LinkedList<>(); // extra buffers
|
||||
// for large files
|
||||
private byte[] _current_buf; // buffer being filled
|
||||
private int _current_buf_pos; // position to fill in more data
|
||||
|
||||
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<Void>() {
|
||||
public Void run() {
|
||||
System.loadLibrary("unpack");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
initIDs();
|
||||
}
|
||||
|
||||
NativeUnpack(UnpackerImpl p200) {
|
||||
super();
|
||||
_p200 = p200;
|
||||
_props = p200.props;
|
||||
p200._nunp = this;
|
||||
}
|
||||
|
||||
// for JNI callbacks
|
||||
static private 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), consumed;
|
||||
try {
|
||||
_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();
|
||||
} finally {
|
||||
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 {
|
||||
if (lsize < 0 || lsize > Integer.MAX_VALUE) {
|
||||
throw new IOException("file too large: " + lsize);
|
||||
}
|
||||
int size = (int) lsize;
|
||||
|
||||
if (_verbose > 1) {
|
||||
Utils.log.fine("Writing entry: " + name + " size=" + size +
|
||||
(deflateHint ? " deflated" : ""));
|
||||
}
|
||||
|
||||
ZipEntry z = new ZipEntry(name);
|
||||
z.setTime(mtime * 1000);
|
||||
z.setSize(size);
|
||||
if (size == 0) {
|
||||
z.setMethod(ZipOutputStream.STORED);
|
||||
z.setCompressedSize(size);
|
||||
z.setCrc(0);
|
||||
j.putNextEntry(z);
|
||||
} else if (!deflateHint) {
|
||||
z.setMethod(ZipOutputStream.STORED);
|
||||
z.setCompressedSize(size);
|
||||
writeEntryData(j, z, data0, data1, size, true);
|
||||
} else {
|
||||
z.setMethod(Deflater.DEFLATED);
|
||||
writeEntryData(j, z, data0, data1, size, false);
|
||||
}
|
||||
j.closeEntry();
|
||||
|
||||
if (_verbose > 0) Utils.log.info("Writing " + Utils.zeString(z));
|
||||
}
|
||||
|
||||
private void writeEntryData(JarOutputStream j, ZipEntry z, ByteBuffer data0,
|
||||
ByteBuffer data1, int size, boolean computeCrc32)
|
||||
throws IOException {
|
||||
prepareReadBuffers(size);
|
||||
try {
|
||||
int inBytes = size;
|
||||
inBytes -= readDataByteBuffer(data0);
|
||||
inBytes -= readDataByteBuffer(data1);
|
||||
inBytes -= readDataInputStream(inBytes);
|
||||
if (inBytes != 0L) {
|
||||
throw new IOException("invalid size: " + size);
|
||||
}
|
||||
if (computeCrc32) {
|
||||
_crc32.reset();
|
||||
processReadData((byte[] buff, int offset, int len) -> {
|
||||
_crc32.update(buff, offset, len);
|
||||
});
|
||||
z.setCrc(_crc32.getValue());
|
||||
}
|
||||
j.putNextEntry(z);
|
||||
processReadData((byte[] buff, int offset, int len) -> {
|
||||
j.write(buff, offset, len);
|
||||
});
|
||||
} finally {
|
||||
resetReadBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareReadBuffers(int size) {
|
||||
if (_buf.length < size && _buf.length < MAX_BUFFER_SIZE) {
|
||||
// Grow the regular buffer to accomodate lsize up to a limit.
|
||||
long newIdealSize = _buf.length;
|
||||
while (newIdealSize < size && newIdealSize < MAX_BUFFER_SIZE) {
|
||||
// Never overflows: size is [0, 0x7FFFFFFF].
|
||||
newIdealSize <<= 1;
|
||||
}
|
||||
int newSize = (int) Long.min(newIdealSize, MAX_BUFFER_SIZE);
|
||||
_buf = new byte[newSize];
|
||||
}
|
||||
resetReadBuffers();
|
||||
}
|
||||
|
||||
private void resetReadBuffers() {
|
||||
_extra_buf.clear();
|
||||
_current_buf = _buf;
|
||||
_current_buf_pos = 0;
|
||||
}
|
||||
|
||||
private int readDataByteBuffer(ByteBuffer data) throws IOException {
|
||||
if (data == null) {
|
||||
return 0;
|
||||
}
|
||||
return readData(data.remaining(),
|
||||
(byte[] buff, int offset, int len) -> {
|
||||
data.get(buff, offset, len);
|
||||
return len;
|
||||
});
|
||||
}
|
||||
|
||||
private int readDataInputStream(int inBytes) throws IOException {
|
||||
return readData(inBytes, (byte[] buff, int offset, int len) -> {
|
||||
return in.read(buff, offset, len);
|
||||
});
|
||||
}
|
||||
|
||||
private static interface ReadDataCB {
|
||||
public int read(byte[] buff, int offset, int len) throws IOException;
|
||||
}
|
||||
|
||||
private int readData(int bytesToRead, ReadDataCB readDataCb)
|
||||
throws IOException {
|
||||
int bytesRemaining = bytesToRead;
|
||||
while (bytesRemaining > 0) {
|
||||
if (_current_buf_pos == _current_buf.length) {
|
||||
byte[] newBuff = new byte[Integer.min(bytesRemaining,
|
||||
MAX_BUFFER_SIZE)];
|
||||
_extra_buf.add(newBuff);
|
||||
_current_buf = newBuff;
|
||||
_current_buf_pos = 0;
|
||||
}
|
||||
int current_buffer_space = _current_buf.length - _current_buf_pos;
|
||||
int nextRead = Integer.min(current_buffer_space, bytesRemaining);
|
||||
int bytesRead = readDataCb.read(_current_buf, _current_buf_pos,
|
||||
nextRead);
|
||||
if (bytesRead <= 0) {
|
||||
throw new IOException("EOF at end of archive");
|
||||
}
|
||||
_current_buf_pos += bytesRead;
|
||||
bytesRemaining -= bytesRead;
|
||||
}
|
||||
return bytesToRead - bytesRemaining;
|
||||
}
|
||||
|
||||
private static interface ProcessDataCB {
|
||||
public void apply(byte[] buff, int offset, int len) throws IOException;
|
||||
}
|
||||
|
||||
private void processReadData(ProcessDataCB processDataCB)
|
||||
throws IOException {
|
||||
processDataCB.apply(_buf, 0, _buf == _current_buf ? _current_buf_pos :
|
||||
_buf.length);
|
||||
for (byte[] buff : _extra_buf) {
|
||||
// Extra buffers are allocated of a size such that they are always
|
||||
// full, including the last one.
|
||||
processDataCB.apply(buff, 0, buff.length);
|
||||
};
|
||||
}
|
||||
}
|
||||
1376
jdkSrc/jdk8/com/sun/java/util/jar/pack/Package.java
Normal file
1376
jdkSrc/jdk8/com/sun/java/util/jar/pack/Package.java
Normal file
File diff suppressed because it is too large
Load Diff
2383
jdkSrc/jdk8/com/sun/java/util/jar/pack/PackageReader.java
Normal file
2383
jdkSrc/jdk8/com/sun/java/util/jar/pack/PackageReader.java
Normal file
File diff suppressed because it is too large
Load Diff
1743
jdkSrc/jdk8/com/sun/java/util/jar/pack/PackageWriter.java
Normal file
1743
jdkSrc/jdk8/com/sun/java/util/jar/pack/PackageWriter.java
Normal file
File diff suppressed because it is too large
Load Diff
638
jdkSrc/jdk8/com/sun/java/util/jar/pack/PackerImpl.java
Normal file
638
jdkSrc/jdk8/com/sun/java/util/jar/pack/PackerImpl.java
Normal file
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
* 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.Attribute.Layout;
|
||||
import java.beans.PropertyChangeListener;
|
||||
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.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.TimeZone;
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
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);
|
||||
|
||||
boolean needUTC = !props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE);
|
||||
try {
|
||||
Utils.currentInstance.set(this);
|
||||
if (needUTC) {
|
||||
Utils.changeDefaultTimeZoneToUtc();
|
||||
}
|
||||
|
||||
if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
|
||||
Utils.copyJarFile(in, out);
|
||||
} else {
|
||||
(new DoPack()).run(in, out);
|
||||
}
|
||||
} finally {
|
||||
Utils.currentInstance.set(null);
|
||||
if (needUTC) {
|
||||
Utils.restoreDefaultTimeZone();
|
||||
}
|
||||
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);
|
||||
boolean needUTC = !props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE);
|
||||
try {
|
||||
Utils.currentInstance.set(this);
|
||||
if (needUTC) {
|
||||
Utils.changeDefaultTimeZoneToUtc();
|
||||
}
|
||||
if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
|
||||
Utils.copyJarFile(in, out);
|
||||
} else {
|
||||
(new DoPack()).run(in, out);
|
||||
}
|
||||
} finally {
|
||||
Utils.currentInstance.set(null);
|
||||
if (needUTC) {
|
||||
Utils.restoreDefaultTimeZone();
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for changes to options.
|
||||
* @param listener An object to be invoked when a property is changed.
|
||||
*/
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
props.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener for the PropertyChange event.
|
||||
* @param listener The PropertyChange listener to be removed.
|
||||
*/
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
props.removeListener(listener);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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, "").equals(""))
|
||||
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.)
|
||||
|
||||
boolean isClassFile(String name) {
|
||||
if (!name.endsWith(".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(String name) {
|
||||
return name.startsWith("/" + Utils.METAINF) ||
|
||||
name.startsWith(Utils.METAINF);
|
||||
}
|
||||
|
||||
// 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 = getModtime(je.getTime());
|
||||
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);
|
||||
}
|
||||
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 = (isMetaInfFile(name))
|
||||
? 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 (isClassFile(name)) {
|
||||
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 = (isMetaInfFile(name))
|
||||
? 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 (isClassFile(name)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
500
jdkSrc/jdk8/com/sun/java/util/jar/pack/PopulationCoding.java
Normal file
500
jdkSrc/jdk8/com/sun/java/util/jar/pack/PopulationCoding.java
Normal file
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
453
jdkSrc/jdk8/com/sun/java/util/jar/pack/PropMap.java
Normal file
453
jdkSrc/jdk8/com/sun/java/util/jar/pack/PropMap.java
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* 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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
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;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Control block for publishing Pack200 options to the other classes.
|
||||
*/
|
||||
|
||||
final class PropMap implements SortedMap<String, String> {
|
||||
private final TreeMap<String, String> theMap = new TreeMap<>();;
|
||||
|
||||
// type is erased, elements are of type java.beans.PropertyChangeListener
|
||||
private final List<Object> listenerList = new ArrayList<>(1);
|
||||
|
||||
void addListener(Object listener) {
|
||||
assert Beans.isPropertyChangeListener(listener);
|
||||
listenerList.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(Object listener) {
|
||||
assert Beans.isPropertyChangeListener(listener);
|
||||
listenerList.remove(listener);
|
||||
}
|
||||
|
||||
// Override:
|
||||
public String put(String key, String value) {
|
||||
String oldValue = theMap.put(key, value);
|
||||
if (value != oldValue && !listenerList.isEmpty()) {
|
||||
assert Beans.isBeansPresent();
|
||||
// Post the property change event.
|
||||
Object event = Beans.newPropertyChangeEvent(this, key, oldValue, value);
|
||||
for (Object listener : listenerList) {
|
||||
Beans.invokePropertyChange(listener, event);
|
||||
}
|
||||
}
|
||||
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
|
||||
props.put(Utils.DEBUG_DISABLE_NATIVE,
|
||||
String.valueOf(Boolean.getBoolean(Utils.DEBUG_DISABLE_NATIVE)));
|
||||
|
||||
// Set the DEBUG_VERBOSE from system
|
||||
props.put(Utils.DEBUG_VERBOSE,
|
||||
String.valueOf(Integer.getInteger(Utils.DEBUG_VERBOSE,0)));
|
||||
|
||||
// Set the PACK_TIMEZONE_NO_UTC
|
||||
props.put(Utils.PACK_DEFAULT_TIMEZONE,
|
||||
String.valueOf(Boolean.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)));
|
||||
|
||||
// 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,
|
||||
System.getProperty(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";
|
||||
|
||||
try (InputStream propStr = PackerImpl.class.getResourceAsStream(propFile)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that provides access to the java.beans.PropertyChangeListener
|
||||
* and java.beans.PropertyChangeEvent without creating a static dependency
|
||||
* on java.beans. This class can be removed once the addPropertyChangeListener
|
||||
* and removePropertyChangeListener methods are removed from Packer and
|
||||
* Unpacker.
|
||||
*/
|
||||
private static class Beans {
|
||||
private static final Class<?> propertyChangeListenerClass =
|
||||
getClass("java.beans.PropertyChangeListener");
|
||||
|
||||
private static final Class<?> propertyChangeEventClass =
|
||||
getClass("java.beans.PropertyChangeEvent");
|
||||
|
||||
private static final Method propertyChangeMethod =
|
||||
getMethod(propertyChangeListenerClass,
|
||||
"propertyChange",
|
||||
propertyChangeEventClass);
|
||||
|
||||
private static final Constructor<?> propertyEventCtor =
|
||||
getConstructor(propertyChangeEventClass,
|
||||
Object.class,
|
||||
String.class,
|
||||
Object.class,
|
||||
Object.class);
|
||||
|
||||
private static Class<?> getClass(String name) {
|
||||
try {
|
||||
return Class.forName(name, true, Beans.class.getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
|
||||
try {
|
||||
return (c == null) ? null : c.getDeclaredConstructor(types);
|
||||
} catch (NoSuchMethodException x) {
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static Method getMethod(Class<?> c, String name, Class<?>... types) {
|
||||
try {
|
||||
return (c == null) ? null : c.getMethod(name, types);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if java.beans is present.
|
||||
*/
|
||||
static boolean isBeansPresent() {
|
||||
return propertyChangeListenerClass != null &&
|
||||
propertyChangeEventClass != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given object is a PropertyChangeListener
|
||||
*/
|
||||
static boolean isPropertyChangeListener(Object obj) {
|
||||
if (propertyChangeListenerClass == null) {
|
||||
return false;
|
||||
} else {
|
||||
return propertyChangeListenerClass.isInstance(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new PropertyChangeEvent with the given source, property
|
||||
* name, old and new values.
|
||||
*/
|
||||
static Object newPropertyChangeEvent(Object source, String prop,
|
||||
Object oldValue, Object newValue)
|
||||
{
|
||||
try {
|
||||
return propertyEventCtor.newInstance(source, prop, oldValue, newValue);
|
||||
} catch (InstantiationException | IllegalAccessException x) {
|
||||
throw new AssertionError(x);
|
||||
} catch (InvocationTargetException x) {
|
||||
Throwable cause = x.getCause();
|
||||
if (cause instanceof Error)
|
||||
throw (Error)cause;
|
||||
if (cause instanceof RuntimeException)
|
||||
throw (RuntimeException)cause;
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given PropertyChangeListener's propertyChange method
|
||||
* with the given event.
|
||||
*/
|
||||
static void invokePropertyChange(Object listener, Object ev) {
|
||||
try {
|
||||
propertyChangeMethod.invoke(listener, ev);
|
||||
} catch (IllegalAccessException x) {
|
||||
throw new AssertionError(x);
|
||||
} catch (InvocationTargetException x) {
|
||||
Throwable cause = x.getCause();
|
||||
if (cause instanceof Error)
|
||||
throw (Error)cause;
|
||||
if (cause instanceof RuntimeException)
|
||||
throw (RuntimeException)cause;
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
jdkSrc/jdk8/com/sun/java/util/jar/pack/TLGlobals.java
Normal file
125
jdkSrc/jdk8/com/sun/java/util/jar/pack/TLGlobals.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
295
jdkSrc/jdk8/com/sun/java/util/jar/pack/UnpackerImpl.java
Normal file
295
jdkSrc/jdk8/com/sun/java/util/jar/pack/UnpackerImpl.java
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.beans.PropertyChangeListener;
|
||||
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.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TimeZone;
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
public class UnpackerImpl extends TLGlobals implements Pack200.Unpacker {
|
||||
|
||||
|
||||
/**
|
||||
* Register a listener for changes to options.
|
||||
* @param listener An object to be invoked when a property is changed.
|
||||
*/
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
props.addListener(listener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a listener for the PropertyChange event.
|
||||
* @param listener The PropertyChange listener to be removed.
|
||||
*/
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
props.removeListener(listener);
|
||||
}
|
||||
|
||||
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);
|
||||
boolean needUTC = !props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE);
|
||||
try {
|
||||
Utils.currentInstance.set(this);
|
||||
if (needUTC) {
|
||||
Utils.changeDefaultTimeZoneToUtc();
|
||||
}
|
||||
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);
|
||||
} finally {
|
||||
if (_nunp != null) {
|
||||
// Free up native memory and JNI handles to prevent leaks
|
||||
((NativeUnpack) _nunp).finish();
|
||||
}
|
||||
}
|
||||
in0.close();
|
||||
Utils.markJarFile(out);
|
||||
}
|
||||
} finally {
|
||||
_nunp = null;
|
||||
Utils.currentInstance.set(null);
|
||||
if (needUTC) {
|
||||
Utils.restoreDefaultTimeZone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
je.setTime(file.modtime);
|
||||
// Convert back to milliseconds
|
||||
je.setTime((long)file.modtime * 1000);
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
351
jdkSrc/jdk8/com/sun/java/util/jar/pack/Utils.java
Normal file
351
jdkSrc/jdk8/com/sun/java/util/jar/pack/Utils.java
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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.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.TimeZone;
|
||||
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";
|
||||
|
||||
/*
|
||||
* Use the default working TimeZone instead of UTC.
|
||||
* Note: This has installer unpacker implications.
|
||||
* see: zip.cpp which uses gmtime vs. localtime.
|
||||
*/
|
||||
static final String PACK_DEFAULT_TIMEZONE = COM_PREFIX+"default.timezone";
|
||||
|
||||
/*
|
||||
* 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<>();
|
||||
|
||||
private static TimeZone tz;
|
||||
private static int workingPackerCount = 0;
|
||||
|
||||
// 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 synchronized void changeDefaultTimeZoneToUtc() {
|
||||
if (workingPackerCount++ == 0) {
|
||||
// only first thread saves default TZ
|
||||
tz = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
}
|
||||
|
||||
static synchronized void restoreDefaultTimeZone() {
|
||||
if (--workingPackerCount == 0) {
|
||||
// reset timezone when all the packer/unpacker instances have terminated
|
||||
if (tz != null) {
|
||||
TimeZone.setDefault(tz);
|
||||
}
|
||||
tz = null;
|
||||
}
|
||||
}
|
||||
|
||||
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; ) {
|
||||
je.setCompressedSize(-1);
|
||||
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())) {
|
||||
je.setCompressedSize(-1);
|
||||
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.
|
||||
static private
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user