提交 a1c492e8 编写于 作者: K ksrini

6966737: (pack200) the pack200 regression tests need to be more robust.

Reviewed-by: jrose, ohair
上级 b1f0edac
/*
* Copyright (c) 2007, 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.
*
* 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.
*/
/*
* @test CommandLineTests.sh
* @bug 6521334 6965836 6965836
* @compile -XDignore.symbol.file CommandLineTests.java Pack200Test.java
* @run main/timeout=1200 CommandLineTests
* @summary An ad hoc test to verify the behavior of pack200/unpack200 CLIs,
* and a simulation of pack/unpacking in the install repo.
* @author ksrini
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
/*
* We try a potpouri of things ie. we have pack.conf to setup some
* options as well as a couple of command line options. We also test
* the packing and unpacking mechanism using the Java APIs. This also
* simulates pack200 the install workspace, noting that this is a simulation
* and can only test jars that are guaranteed to be available, also the
* configuration may not be in sync with the installer workspace.
*/
public class CommandLineTests {
private static final File CWD = new File(".");
private static final File EXP_SDK = new File(CWD, "exp-sdk-image");
private static final File EXP_SDK_LIB_DIR = new File(EXP_SDK, "lib");
private static final File EXP_SDK_BIN_DIR = new File(EXP_SDK, "bin");
private static final File EXP_JRE_DIR = new File(EXP_SDK, "jre");
private static final File EXP_JRE_LIB_DIR = new File(EXP_JRE_DIR, "lib");
private static final File RtJar = new File(EXP_JRE_LIB_DIR, "rt.jar");
private static final File CharsetsJar = new File(EXP_JRE_LIB_DIR, "charsets.jar");
private static final File JsseJar = new File(EXP_JRE_LIB_DIR, "jsse.jar");
private static final File ToolsJar = new File(EXP_SDK_LIB_DIR, "tools.jar");
private static final File javaCmd;
private static final File javacCmd;
private static final File ConfigFile = new File("pack.conf");
private static final List<File> jarList;
static {
javaCmd = Utils.IsWindows
? new File(EXP_SDK_BIN_DIR, "java.exe")
: new File(EXP_SDK_BIN_DIR, "java");
javacCmd = Utils.IsWindows
? new File(EXP_SDK_BIN_DIR, "javac.exe")
: new File(EXP_SDK_BIN_DIR, "javac");
jarList = new ArrayList<File>();
jarList.add(RtJar);
jarList.add(CharsetsJar);
jarList.add(JsseJar);
jarList.add(ToolsJar);
}
// init test area with a copy of the sdk
static void init() throws IOException {
Utils.recursiveCopy(Utils.JavaSDK, EXP_SDK);
creatConfigFile();
}
// Hopefully, this should be kept in sync with what the installer does.
static void creatConfigFile() throws IOException {
FileOutputStream fos = null;
PrintStream ps = null;
try {
fos = new FileOutputStream(ConfigFile);
ps = new PrintStream(fos);
ps.println("com.sun.java.util.jar.pack.debug.verbose=0");
ps.println("pack.modification.time=keep");
ps.println("pack.keep.class.order=true");
ps.println("pack.deflate.hint=false");
// Fail the build, if new or unknown attributes are introduced.
ps.println("pack.unknown.attribute=error");
ps.println("pack.segment.limit=-1");
// BugId: 6328502, These files will be passed-through as-is.
ps.println("pack.pass.file.0=java/lang/Error.class");
ps.println("pack.pass.file.1=java/lang/LinkageError.class");
ps.println("pack.pass.file.2=java/lang/Object.class");
ps.println("pack.pass.file.3=java/lang/Throwable.class");
ps.println("pack.pass.file.4=java/lang/VerifyError.class");
ps.println("pack.pass.file.5=com/sun/demo/jvmti/hprof/Tracker.class");
} finally {
Utils.close(ps);
Utils.close(fos);
}
}
static void runPack200(boolean jre) throws IOException {
List<String> cmdsList = new ArrayList<String>();
for (File f : jarList) {
if (jre && f.getName().equals("tools.jar")) {
continue; // need not worry about tools.jar for JRE
}
// make a backup copy for re-use
File bakFile = new File(f.getName() + ".bak");
if (!bakFile.exists()) { // backup
Utils.copyFile(f.getAbsoluteFile(), bakFile.getAbsoluteFile());
} else { // restore
Utils.copyFile(bakFile.getAbsoluteFile(), f.getAbsoluteFile());
}
cmdsList.clear();
cmdsList.add(Utils.getPack200Cmd());
cmdsList.add("-J-esa");
cmdsList.add("-J-ea");
cmdsList.add(Utils.Is64Bit ? "-J-Xmx1g" : "-J-Xmx512m");
cmdsList.add("--repack");
cmdsList.add("--config-file=" + ConfigFile.getAbsolutePath());
if (jre) {
cmdsList.add("--strip-debug");
}
// NOTE: commented until 6965836 is fixed
// cmdsList.add("--code-attribute=StackMapTable=strip");
cmdsList.add(f.getAbsolutePath());
Utils.runExec(cmdsList);
}
}
static void testJRE() throws IOException {
runPack200(true);
// the speciment JRE
List<String> cmdsList = new ArrayList<String>();
cmdsList.add(javaCmd.getAbsolutePath());
cmdsList.add("-verify");
cmdsList.add("-version");
Utils.runExec(cmdsList);
}
static void testJDK() throws IOException {
runPack200(false);
// test the specimen JDK
List<String> cmdsList = new ArrayList<String>();
cmdsList.add(javaCmd.getAbsolutePath());
cmdsList.add("-verify");
cmdsList.add("-version");
Utils.runExec(cmdsList);
// invoke javac to test the tools.jar
cmdsList.clear();
cmdsList.add(javacCmd.getAbsolutePath());
cmdsList.add("-J-verify");
cmdsList.add("-help");
Utils.runExec(cmdsList);
}
public static void main(String... args) {
try {
init();
testJRE();
testJDK();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
#
# Copyright (c) 2003, 2007, 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.
#
# 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.
#
# @test Pack200Simple.sh
# @bug 6521334
# @build Pack200Test
# @run shell/timeout=1200 Pack200Simple.sh
# @summary An ad hoc test to verify class-file format.
# @author Kumar Srinivasan
# The goal of this test is to assist javac or other developers
# who modify class file formats, to quickly test those modifications
# without having to build the install workspace. However it must
# be noted that building the install workspace is the only know
# way to prevent build breakages.
# Pack200 developers could use this as a basic smoke-test, however
# please note, there are other more elaborate and thorough tests for
# this very purpose.
# We try a potpouri of things ie. we have pack.conf to setup some
# options as well as a couple of command line options. We also test
# the packing and unpacking mechanism using the Java APIs.
# print error and exit with a message
errorOut() {
if [ "x$1" = "x" ]; then
printf "Error: Unknown error\n"
else
printf "Error: %s\n" "$1"
fi
exit 1
}
# Verify directory context variables are set
if [ "${TESTJAVA}" = "" ]; then
errorOut "TESTJAVA not set. Test cannot execute. Failed."
fi
if [ "${TESTSRC}" = "" ]; then
errorOut "TESTSRC not set. Test cannot execute. Failed."
fi
if [ "${TESTCLASSES}" = "" ]; then
errorOut "TESTCLASSES not set. Test cannot execute. Failed."
fi
# The common java utils we need
PACK200=${TESTJAVA}/bin/pack200
UNPACK200=${TESTJAVA}/bin/unpack200
JAR=${TESTJAVA}/bin/jar
# For Windows and Linux needs the heap to be set, for others ergonomics
# will do the rest. It is important to use ea, which can expose class
# format errors much earlier than later.
OS=`uname -s`
case "$OS" in
Windows*|CYGWIN* )
PackOptions="-J-Xmx512m -J-ea"
break
;;
Linux )
PackOptions="-J-Xmx512m -J-ea"
break
;;
* )
PackOptions="-J-ea"
;;
esac
# Creates a packfile of choice expects 1 argument the filename
createConfigFile() {
# optimize for speed
printf "pack.effort=1\n" > $1
# we DO want to know about new attributes
printf "pack.unknown.attribute=error\n" >> $1
# optimize for speed
printf "pack.deflate.hint=false\n" >> $1
# keep the ordering for easy compare
printf "pack.keep.class.order=true\n" >> $1
}
# Tests a given jar, expects 1 argument the fully qualified
# name to a test jar, it writes all output to the current
# directory which is a scratch area.
testAJar() {
PackConf="pack.conf"
createConfigFile $PackConf
# Try some command line options
CLIPackOptions="$PackOptions -v --no-gzip --segment-limit=10000 --config-file=$PackConf"
jfName=`basename $1`
${PACK200} $CLIPackOptions ${jfName}.pack $1 > ${jfName}.pack.log 2>&1
if [ $? != 0 ]; then
errorOut "$jfName packing failed"
fi
# We want to test unpack200, therefore we dont use -r with pack
${UNPACK200} -v ${jfName}.pack $jfName > ${jfName}.unpack.log 2>&1
if [ $? != 0 ]; then
errorOut "$jfName unpacking failed"
fi
# A quick crc compare test to ensure a well formed zip
# archive, this is a critical unpack200 behaviour.
unzip -t $jfName > ${jfName}.unzip.log 2>&1
if [ $? != 0 ]; then
errorOut "$jfName unzip -t test failed"
fi
# The PACK200 signature should be at the top of the log
# this tag is critical for deployment related tools.
head -5 ${jfName}.unzip.log | grep PACK200 > /dev/null 2>&1
if [ $? != 0 ]; then
errorOut "$jfName PACK200 signature missing"
fi
# we know the size fields don't match, strip 'em out, its
# extremely important to ensure that the date stamps match up.
# Don't EVER sort the output we are checking for correct ordering.
${JAR} -tvf $1 | sed -e 's/^ *[0-9]* //g'> ${jfName}.ref.txt
${JAR} -tvf $jfName | sed -e 's/^ *[0-9]* //g'> ${jfName}.cmp.txt
diff ${jfName}.ref.txt ${jfName}.cmp.txt > ${jfName}.diff.log 2>&1
if [ $? != 0 ]; then
errorOut "$jfName files missing"
fi
}
# These JARs are the largest and also the most likely specimens to
# expose class format issues and stress the packer as well.
JLIST="${TESTJAVA}/lib/tools.jar ${TESTJAVA}/jre/lib/rt.jar"
# Test the Command Line Interfaces (CLI).
mkdir cliTestDir
_pwd=`pwd`
cd cliTestDir
for jarfile in $JLIST ; do
if [ -f $jarfile ]; then
testAJar $jarfile
else
errorOut "Error: '$jarFile' does not exist\nTest requires a j2sdk-image\n"
fi
done
cd $_pwd
# Test the Java APIs.
mkdir apiTestDir
_pwd=`pwd`
cd apiTestDir
# Strip out the -J prefixes.
JavaPackOptions=`printf %s "$PackOptions" | sed -e 's/-J//g'`
# Test the Java APIs now.
$TESTJAVA/bin/java $JavaPackOptions -cp $TESTCLASSES Pack200Test $JLIST || exit 1
cd $_pwd
exit 0
...@@ -24,111 +24,97 @@ ...@@ -24,111 +24,97 @@
import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.jar.*; import java.util.jar.*;
import java.util.zip.*;
/* /*
* Pack200Test.java * @test
* * @bug 6521334 6712743
* @author ksrini * @summary check for memory leaks, test general packer/unpacker functionality\
*/ * using native and java unpackers
* @compile -XDignore.symbol.file Utils.java Pack200Test.java
* @run main/othervm/timeout=1200 -Xmx512m Pack200Test
* @author ksrini
*/
/** /**
* These tests are very rudimentary smoke tests to ensure that the packing * Tests the packing/unpacking via the APIs.
* unpacking process works on a select set of JARs.
*/ */
public class Pack200Test { public class Pack200Test {
private static ArrayList <File> jarList = new ArrayList<File>(); private static ArrayList <File> jarList = new ArrayList<File>();
static final String PACKEXT = ".pack"; static final MemoryMXBean mmxbean = ManagementFactory.getMemoryMXBean();
static final long m0 = getUsedMemory();
static final int LEAK_TOLERANCE = 20000; // OS and GC related variations.
/** Creates a new instance of Pack200Test */ /** Creates a new instance of Pack200Test */
private Pack200Test() {} private Pack200Test() {}
static long getUsedMemory() {
mmxbean.gc();
mmxbean.gc();
mmxbean.gc();
return mmxbean.getHeapMemoryUsage().getUsed()/1024;
}
private static void leakCheck() throws Exception {
long diff = getUsedMemory() - m0;
System.out.println(" Info: memory diff = " + diff + "K");
if ( diff > LEAK_TOLERANCE) {
throw new Exception("memory leak detected " + diff);
}
}
private static void doPackUnpack() { private static void doPackUnpack() {
for (File in : jarList) { for (File in : jarList) {
Pack200.Packer packer = Pack200.newPacker(); JarOutputStream javaUnpackerStream = null;
Map<String, String> p = packer.properties(); JarOutputStream nativeUnpackerStream = null;
// Take the time optimization vs. space JarFile jarFile = null;
p.put(packer.EFFORT, "1"); // CAUTION: do not use 0.
// Make the memory consumption as effective as possible
p.put(packer.SEGMENT_LIMIT,"10000");
// throw an error if an attribute is unrecognized
p.put(packer.UNKNOWN_ATTRIBUTE, packer.ERROR);
// ignore all JAR deflation requests to save time
p.put(packer.DEFLATE_HINT, packer.FALSE);
// save the file ordering of the original JAR
p.put(packer.KEEP_FILE_ORDER, packer.TRUE);
try { try {
JarFile jarFile = new JarFile(in); jarFile = new JarFile(in);
// Write out to a jtreg scratch area // Write out to a jtreg scratch area
FileOutputStream fos = new FileOutputStream(in.getName() + PACKEXT); File packFile = new File(in.getName() + Utils.PACK_FILE_EXT);
System.out.print("Packing [" + in.toString() + "]..."); System.out.println("Packing [" + in.toString() + "]");
// Call the packer // Call the packer
packer.pack(jarFile, fos); Utils.pack(jarFile, packFile);
jarFile.close(); jarFile.close();
fos.close(); leakCheck();
System.out.print("Unpacking...");
File f = new File(in.getName() + PACKEXT);
System.out.println(" Unpacking using java unpacker");
File javaUnpackedJar = new File("java-" + in.getName());
// Write out to current directory, jtreg will setup a scratch area // Write out to current directory, jtreg will setup a scratch area
JarOutputStream jostream = new JarOutputStream(new FileOutputStream(in.getName())); javaUnpackerStream = new JarOutputStream(
new FileOutputStream(javaUnpackedJar));
// Unpack the files Utils.unpackj(packFile, javaUnpackerStream);
Pack200.Unpacker unpacker = Pack200.newUnpacker(); javaUnpackerStream.close();
// Call the unpacker System.out.println(" Testing...java unpacker");
unpacker.unpack(f, jostream); leakCheck();
// Must explicitly close the output.
jostream.close();
System.out.print("Testing...");
// Ok we have unpacked the file, lets test it. // Ok we have unpacked the file, lets test it.
doTest(in); Utils.doCompareVerify(in.getAbsoluteFile(), javaUnpackedJar);
System.out.println(" Unpacking using native unpacker");
// Write out to current directory
File nativeUnpackedJar = new File("native-" + in.getName());
nativeUnpackerStream = new JarOutputStream(
new FileOutputStream(nativeUnpackedJar));
Utils.unpackn(packFile, nativeUnpackerStream);
nativeUnpackerStream.close();
System.out.println(" Testing...native unpacker");
leakCheck();
// the unpackers (native and java) should produce identical bits
// so we use use bit wise compare, the verification compare is
// very expensive wrt. time.
Utils.doCompareBitWise(javaUnpackedJar, nativeUnpackedJar);
System.out.println("Done."); System.out.println("Done.");
} catch (Exception e) { } catch (Exception e) {
System.out.println("ERROR: " + e.getMessage()); throw new RuntimeException(e);
System.exit(1); } finally {
} Utils.close(nativeUnpackerStream);
} Utils.close(javaUnpackerStream);
} Utils.close((Closeable) jarFile);
private static ArrayList <String> getZipFileEntryNames(ZipFile z) {
ArrayList <String> out = new ArrayList<String>();
for (ZipEntry ze : Collections.list(z.entries())) {
out.add(ze.getName());
}
return out;
}
private static void doTest(File in) throws Exception {
// make sure all the files in the original jar exists in the other
ArrayList <String> refList = getZipFileEntryNames(new ZipFile(in));
ArrayList <String> cmpList = getZipFileEntryNames(new ZipFile(in.getName()));
System.out.print(refList.size() + "/" + cmpList.size() + " entries...");
if (refList.size() != cmpList.size()) {
throw new Exception("Missing: files ?, entries don't match");
}
for (String ename: refList) {
if (!cmpList.contains(ename)) {
throw new Exception("Does not contain : " + ename);
}
}
}
private static void doSanity(String[] args) {
for (String s: args) {
File f = new File(s);
if (f.exists()) {
jarList.add(f);
} else {
System.out.println("Warning: The JAR file " + f.toString() + " does not exist,");
System.out.println(" this test requires a JDK image, this file will be skipped.");
} }
} }
} }
...@@ -137,11 +123,12 @@ public class Pack200Test { ...@@ -137,11 +123,12 @@ public class Pack200Test {
* @param args the command line arguments * @param args the command line arguments
*/ */
public static void main(String[] args) { public static void main(String[] args) {
if (args.length < 1) { // select the jars carefully, adding more jars will increase the
System.out.println("Usage: jar1 jar2 jar3 ....."); // testing time, especially for jprt.
System.exit(1); jarList.add(Utils.locateJar("tools.jar"));
} jarList.add(Utils.locateJar("rt.jar"));
doSanity(args); jarList.add(Utils.locateJar("golden.jar"));
System.out.println(jarList);
doPackUnpack(); doPackUnpack();
} }
} }
...@@ -22,13 +22,14 @@ ...@@ -22,13 +22,14 @@
* questions. * questions.
*/ */
/** /*
* @test * @test
* @bug 6712743 * @bug 6712743
* @summary verify package versioning * @summary verify package versions
* @compile -XDignore.symbol.file PackageVersionTest.java * @compile -XDignore.symbol.file Utils.java PackageVersionTest.java
* @run main PackageVersionTest * @run main PackageVersionTest
*/ * @author ksrini
*/
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
...@@ -74,14 +75,6 @@ public class PackageVersionTest { ...@@ -74,14 +75,6 @@ public class PackageVersionTest {
JAVA5_PACKAGE_MINOR_VERSION); JAVA5_PACKAGE_MINOR_VERSION);
} }
static void close(Closeable c) {
if (c == null) {
return;
}
try {
c.close();
} catch (IOException ignore) {}
}
static void createClassFile(String name) { static void createClassFile(String name) {
createJavaFile(name); createJavaFile(name);
...@@ -93,7 +86,7 @@ public class PackageVersionTest { ...@@ -93,7 +86,7 @@ public class PackageVersionTest {
name.substring(name.length() - 1), name.substring(name.length() - 1),
name + ".java" name + ".java"
}; };
compileJava(javacCmds); Utils.compiler(javacCmds);
} }
static void createJavaFile(String name) { static void createJavaFile(String name) {
...@@ -108,22 +101,8 @@ public class PackageVersionTest { ...@@ -108,22 +101,8 @@ public class PackageVersionTest {
} catch (IOException ioe) { } catch (IOException ioe) {
throw new RuntimeException("creation of test file failed"); throw new RuntimeException("creation of test file failed");
} finally { } finally {
close(ps); Utils.close(ps);
close(fos); Utils.close(fos);
}
}
static void compileJava(String... javacCmds) {
if (com.sun.tools.javac.Main.compile(javacCmds) != 0) {
throw new RuntimeException("compilation failed");
}
}
static void makeJar(String... jargs) {
sun.tools.jar.Main jarTool =
new sun.tools.jar.Main(System.out, System.err, "jartool");
if (!jarTool.run(jargs)) {
throw new RuntimeException("jar command failed");
} }
} }
...@@ -136,7 +115,7 @@ public class PackageVersionTest { ...@@ -136,7 +115,7 @@ public class PackageVersionTest {
jarFileName.getName(), jarFileName.getName(),
filename filename
}; };
makeJar(jargs); Utils.jar(jargs);
JarFile jfin = null; JarFile jfin = null;
try { try {
...@@ -163,7 +142,7 @@ public class PackageVersionTest { ...@@ -163,7 +142,7 @@ public class PackageVersionTest {
} catch (IOException ioe) { } catch (IOException ioe) {
throw new RuntimeException(ioe.getMessage()); throw new RuntimeException(ioe.getMessage());
} finally { } finally {
close(jfin); Utils.close((Closeable) jfin);
} }
} }
} }
...@@ -21,22 +21,18 @@ ...@@ -21,22 +21,18 @@
* questions. * questions.
*/ */
/** /*
* @test * @test
* @bug 6575373 * @bug 6575373
* @summary verify default segment limit * @summary verify default segment limit
* @compile SegmentLimit.java * @compile -XDignore.symbol.file Utils.java SegmentLimit.java
* @run main SegmentLimit * @run main SegmentLimit
* @author ksrini
*/ */
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.util.ArrayList;
import java.io.IOException; import java.util.List;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
/* /*
* Run this against a large jar file, by default the packer should generate only * Run this against a large jar file, by default the packer should generate only
...@@ -45,89 +41,36 @@ import java.io.PrintStream; ...@@ -45,89 +41,36 @@ import java.io.PrintStream;
public class SegmentLimit { public class SegmentLimit {
private static final File javaHome = new File(System.getProperty("java.home"));
public static void main(String... args) { public static void main(String... args) {
if (!javaHome.getName().endsWith("jre")) { File out = new File("test" + Utils.PACK_FILE_EXT);
throw new RuntimeException("Error: requires an SDK to run");
}
File out = new File("test" + Pack200Test.PACKEXT);
out.delete(); out.delete();
runPack200(out); runPack200(out);
} }
static void close(Closeable c) {
if (c == null) {
return;
}
try {
c.close();
} catch (IOException ignore) {}
}
static void runPack200(File outFile) { static void runPack200(File outFile) {
File binDir = new File(javaHome, "bin"); File sdkHome = Utils.JavaSDK;
File pack200Exe = System.getProperty("os.name").startsWith("Windows")
? new File(binDir, "pack200.exe")
: new File(binDir, "pack200");
File sdkHome = javaHome.getParentFile();
File testJar = new File(new File(sdkHome, "lib"), "tools.jar"); File testJar = new File(new File(sdkHome, "lib"), "tools.jar");
System.out.println("using pack200: " + pack200Exe.getAbsolutePath()); System.out.println("using pack200: " + Utils.getPack200Cmd());
String[] cmds = { pack200Exe.getAbsolutePath(), List<String> cmdsList = new ArrayList<String>();
"--effort=1", cmdsList.add(Utils.getPack200Cmd());
"--verbose", cmdsList.add("--effort=1");
"--no-gzip", cmdsList.add("--verbose");
outFile.getName(), cmdsList.add("--no-gzip");
testJar.getAbsolutePath() cmdsList.add(outFile.getName());
}; cmdsList.add(testJar.getAbsolutePath());
InputStream is = null; List<String> outList = Utils.runExec(cmdsList);
BufferedReader br = null;
InputStreamReader ir = null; int count = 0;
for (String line : outList) {
FileOutputStream fos = null; System.out.println(line);
PrintStream ps = null; if (line.matches(".*Transmitted.*files of.*input bytes in a segment of.*bytes")) {
count++;
try {
ProcessBuilder pb = new ProcessBuilder(cmds);
pb.redirectErrorStream(true);
Process p = pb.start();
is = p.getInputStream();
ir = new InputStreamReader(is);
br = new BufferedReader(ir);
File logFile = new File("pack200.log");
fos = new FileOutputStream(logFile);
ps = new PrintStream(fos);
String line = br.readLine();
int count = 0;
while (line != null) {
line = line.trim();
if (line.matches(".*Transmitted.*files of.*input bytes in a segment of.*bytes")) {
count++;
}
ps.println(line);
line=br.readLine();
} }
p.waitFor(); }
if (p.exitValue() != 0) { if (count != 1) {
throw new RuntimeException("pack200 failed"); throw new Error("test fails: check for 0 or multiple segments");
}
p.destroy();
if (count > 1) {
throw new Error("test fails: check for multiple segments(" +
count + ") in: " + logFile.getAbsolutePath());
}
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage());
} catch (InterruptedException ignore){
} finally {
close(is);
close(ps);
close(fos);
} }
} }
} }
......
/*
* Copyright (c) 2007, 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.
*
* 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.
*/
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
*
* @author ksrini
*/
/*
* This class contains all the commonly used utilities used by various tests
* in this directory.
*/
class Utils {
static final String JavaHome = System.getProperty("test.java",
System.getProperty("java.home"));
static final boolean IsWindows =
System.getProperty("os.name").startsWith("Windows");
static final boolean Is64Bit =
System.getProperty("sun.arch.data.model", "32").equals("64");
static final File JavaSDK = new File(JavaHome).getParentFile();
static final String PACK_FILE_EXT = ".pack";
static final String JAVA_FILE_EXT = ".java";
static final String CLASS_FILE_EXT = ".class";
static final String JAR_FILE_EXT = ".jar";
static final File TEST_SRC_DIR = new File(System.getProperty("test.src"));
static final String VERIFIER_DIR_NAME = "pack200-verifier";
static final File VerifierJar = new File(VERIFIER_DIR_NAME + JAR_FILE_EXT);
private Utils() {} // all static
static {
if (!JavaHome.endsWith("jre")) {
throw new RuntimeException("Error: requires an SDK to run");
}
}
private static void init() throws IOException {
if (VerifierJar.exists()) {
return;
}
File srcDir = new File(TEST_SRC_DIR, VERIFIER_DIR_NAME);
List<File> javaFileList = findFiles(srcDir, createFilter(JAVA_FILE_EXT));
File tmpFile = File.createTempFile("javac", ".tmp");
File classesDir = new File("xclasses");
classesDir.mkdirs();
FileOutputStream fos = null;
PrintStream ps = null;
try {
fos = new FileOutputStream(tmpFile);
ps = new PrintStream(fos);
for (File f : javaFileList) {
ps.println(f.getAbsolutePath());
}
} finally {
close(ps);
close(fos);
}
compiler("-d",
"xclasses",
"@" + tmpFile.getAbsolutePath());
jar("cvfe",
VerifierJar.getName(),
"sun.tools.pack.verify.Main",
"-C",
"xclasses",
".");
}
static void dirlist(File dir) {
File[] files = dir.listFiles();
System.out.println("--listing " + dir.getAbsolutePath() + "---");
for (File f : files) {
StringBuffer sb = new StringBuffer();
sb.append(f.isDirectory() ? "d " : "- ");
sb.append(f.getName());
System.out.println(sb);
}
}
static void doCompareVerify(File reference, File specimen) throws IOException {
init();
List<String> cmds = new ArrayList<String>();
cmds.add(getJavaCmd());
cmds.add("-jar");
cmds.add(VerifierJar.getName());
cmds.add(reference.getAbsolutePath());
cmds.add(specimen.getAbsolutePath());
cmds.add("-O");
runExec(cmds);
}
static void doCompareBitWise(File reference, File specimen)
throws IOException {
init();
List<String> cmds = new ArrayList<String>();
cmds.add(getJavaCmd());
cmds.add("-jar");
cmds.add(VerifierJar.getName());
cmds.add(reference.getName());
cmds.add(specimen.getName());
cmds.add("-O");
cmds.add("-b");
runExec(cmds);
}
static FileFilter createFilter(final String extension) {
return new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName();
if (name.endsWith(extension)) {
return true;
}
return false;
}
};
}
static final FileFilter DIR_FILTER = new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
}
return false;
}
};
static final FileFilter FILE_FILTER = new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isFile()) {
return true;
}
return false;
}
};
private static void setFileAttributes(File src, File dst) throws IOException {
dst.setExecutable(src.canExecute());
dst.setReadable(src.canRead());
dst.setWritable(src.canWrite());
dst.setLastModified(src.lastModified());
}
static void copyFile(File src, File dst) throws IOException {
if (src.isDirectory()) {
dst.mkdirs();
setFileAttributes(src, dst);
return;
} else {
File baseDirFile = dst.getParentFile();
if (!baseDirFile.exists()) {
baseDirFile.mkdirs();
}
}
FileInputStream in = null;
FileOutputStream out = null;
FileChannel srcChannel = null;
FileChannel dstChannel = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dst);
srcChannel = in.getChannel();
dstChannel = out.getChannel();
long retval = srcChannel.transferTo(0, src.length(), dstChannel);
if (src.length() != dst.length()) {
throw new IOException("file copy failed for " + src);
}
} finally {
close(srcChannel);
close(dstChannel);
close(in);
close(out);
}
setFileAttributes(src, dst);
}
/*
* Suppose a path is provided which consists of a full path
* this method returns the sub path for a full path ex: /foo/bar/baz/foobar.z
* and the base path is /foo/bar it will will return baz/foobar.z.
*/
private static String getEntryPath(String basePath, String fullPath) {
if (!fullPath.startsWith(basePath)) {
return null;
}
return fullPath.substring(basePath.length());
}
static String getEntryPath(File basePathFile, File fullPathFile) {
return getEntryPath(basePathFile.toString(), fullPathFile.toString());
}
public static void recursiveCopy(File src, File dest) throws IOException {
if (!src.exists() || !src.canRead()) {
throw new IOException("file not found or readable: " + src);
}
if (dest.exists() && !dest.isDirectory() && !dest.canWrite()) {
throw new IOException("file not found or writeable: " + dest);
}
if (!dest.exists()) {
dest.mkdirs();
}
List<File> a = directoryList(src);
for (File f : a) {
copyFile(f, new File(dest, getEntryPath(src, f)));
}
}
static List<File> directoryList(File dirname) {
List<File> dirList = new ArrayList<File>();
return directoryList(dirname, dirList, null);
}
private static List<File> directoryList(File dirname, List<File> dirList,
File[] dirs) {
dirList.addAll(Arrays.asList(dirname.listFiles(FILE_FILTER)));
dirs = dirname.listFiles(DIR_FILTER);
for (File f : dirs) {
if (f.isDirectory() && !f.equals(dirname)) {
dirList.add(f);
directoryList(f, dirList, dirs);
}
}
return dirList;
}
static void recursiveDelete(File dir) throws IOException {
if (dir.isFile()) {
dir.delete();
} else if (dir.isDirectory()) {
File[] entries = dir.listFiles();
for (int i = 0; i < entries.length; i++) {
if (entries[i].isDirectory()) {
recursiveDelete(entries[i]);
}
entries[i].delete();
}
dir.delete();
}
}
static List<File> findFiles(File startDir, FileFilter filter)
throws IOException {
List<File> list = new ArrayList<File>();
findFiles0(startDir, list, filter);
return list;
}
/*
* finds files in the start directory using the the filter, appends
* the files to the dirList.
*/
private static void findFiles0(File startDir, List<File> list,
FileFilter filter) throws IOException {
File[] foundFiles = startDir.listFiles(filter);
list.addAll(Arrays.asList(foundFiles));
File[] dirs = startDir.listFiles(DIR_FILTER);
for (File dir : dirs) {
findFiles0(dir, list, filter);
}
}
static void close(Closeable c) {
if (c == null) {
return;
}
try {
c.close();
} catch (IOException ignore) {
}
}
static void compiler(String... javacCmds) {
if (com.sun.tools.javac.Main.compile(javacCmds) != 0) {
throw new RuntimeException("compilation failed");
}
}
static void jar(String... jargs) {
sun.tools.jar.Main jarTool =
new sun.tools.jar.Main(System.out, System.err, "jartool");
if (!jarTool.run(jargs)) {
throw new RuntimeException("jar command failed");
}
}
// given a jar file foo.jar will write to foo.pack
static void pack(JarFile jarFile, File packFile) throws IOException {
Pack200.Packer packer = Pack200.newPacker();
Map<String, String> p = packer.properties();
// Take the time optimization vs. space
p.put(packer.EFFORT, "1"); // CAUTION: do not use 0.
// Make the memory consumption as effective as possible
p.put(packer.SEGMENT_LIMIT, "10000");
// ignore all JAR deflation requests to save time
p.put(packer.DEFLATE_HINT, packer.FALSE);
// save the file ordering of the original JAR
p.put(packer.KEEP_FILE_ORDER, packer.TRUE);
FileOutputStream fos = null;
try {
// Write out to a jtreg scratch area
fos = new FileOutputStream(packFile);
// Call the packer
packer.pack(jarFile, fos);
} finally {
close(fos);
}
}
// uses java unpacker, slow but useful to discover issues with the packer
static void unpackj(File inFile, JarOutputStream jarStream)
throws IOException {
unpack0(inFile, jarStream, true);
}
// uses native unpacker using the java APIs
static void unpackn(File inFile, JarOutputStream jarStream)
throws IOException {
unpack0(inFile, jarStream, false);
}
// given a packed file, create the jar file in the current directory.
private static void unpack0(File inFile, JarOutputStream jarStream,
boolean useJavaUnpack) throws IOException {
// Unpack the files
Pack200.Unpacker unpacker = Pack200.newUnpacker();
Map<String, String> props = unpacker.properties();
if (useJavaUnpack) {
props.put("com.sun.java.util.jar.pack.disable.native", "true");
}
// Call the unpacker
unpacker.unpack(inFile, jarStream);
}
static byte[] getBuffer(ZipFile zf, ZipEntry ze) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buf[] = new byte[8192];
InputStream is = null;
try {
is = zf.getInputStream(ze);
int n = is.read(buf);
while (n > 0) {
baos.write(buf, 0, n);
n = is.read(buf);
}
return baos.toByteArray();
} finally {
close(is);
}
}
static ArrayList<String> getZipFileEntryNames(ZipFile z) {
ArrayList<String> out = new ArrayList<String>();
for (ZipEntry ze : Collections.list(z.entries())) {
out.add(ze.getName());
}
return out;
}
static List<String> runExec(List<String> cmdsList) {
ArrayList<String> alist = new ArrayList<String>();
ProcessBuilder pb =
new ProcessBuilder(cmdsList);
Map<String, String> env = pb.environment();
pb.directory(new File("."));
dirlist(new File("."));
for (String x : cmdsList) {
System.out.print(x + " ");
}
System.out.println("");
int retval = 0;
Process p = null;
InputStreamReader ir = null;
BufferedReader rd = null;
InputStream is = null;
try {
pb.redirectErrorStream(true);
p = pb.start();
is = p.getInputStream();
ir = new InputStreamReader(is);
rd = new BufferedReader(ir, 8192);
String in = rd.readLine();
while (in != null) {
alist.add(in);
System.out.println(in);
in = rd.readLine();
}
retval = p.waitFor();
if (retval != 0) {
throw new RuntimeException("process failed with non-zero exit");
}
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage());
} finally {
close(rd);
close(ir);
close(is);
if (p != null) {
p.destroy();
}
}
return alist;
}
static String getUnpack200Cmd() {
return getAjavaCmd("unpack200");
}
static String getPack200Cmd() {
return getAjavaCmd("pack200");
}
static String getJavaCmd() {
return getAjavaCmd("java");
}
static String getAjavaCmd(String cmdStr) {
File binDir = new File(JavaHome, "bin");
File unpack200File = IsWindows
? new File(binDir, cmdStr + ".exe")
: new File(binDir, cmdStr);
String cmd = unpack200File.getAbsolutePath();
if (!unpack200File.canExecute()) {
throw new RuntimeException("please check" +
cmd + " exists and is executable");
}
return cmd;
}
private static List<File> locaterCache = null;
// search the source dir and jdk dir for requested file and returns
// the first location it finds.
static File locateJar(String name) {
try {
if (locaterCache == null) {
locaterCache = new ArrayList<File>();
locaterCache.addAll(findFiles(TEST_SRC_DIR, createFilter(JAR_FILE_EXT)));
locaterCache.addAll(findFiles(JavaSDK, createFilter(JAR_FILE_EXT)));
}
for (File f : locaterCache) {
if (f.getName().equals(name)) {
return f;
}
}
throw new IOException("file not found: " + name);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
The files contained in the golden.jar have been harvested from many
different sources, some are hand-crafted invalid class files (odds directory),
or from random JDK builds.
Generally these files serve to ensure the integrity of the packer and unpacker
by,
1. maximizing the test coverage.
2. exercising all the Bands in the pack200 specification.
2. testing the behavior of the packer with invalid classes.
3. testing the archive integrity, ordering and description (date, sizes,
CRC etc.)
Build:
To rebuild this JAR follow these steps:
1. unzip the golden.jar to some directory lets call it "example"
2. now we can add any directories with files into example.
2. run the script BUILDME.sh as
% sh BUILDME.sh example
Note: the BUILDME.sh is known to work on all Unix platforms as well as Windows
using Cygwin.
The above will create two JAR files in the current directory,
example.jar and example-cls.jar, now the example.jar can be used as the
golden.jar.
To ensure the JAR has been built correctly use jar -tvf and compare the
results of the old jar and the newly built one, note that the compressed sizes
may differ, however the timestamps etc. should be consistent.
Test:
Basic:
% pack200 --repack test.jar golden.jar
Advanced:
Create a pack.conf as follows:
% cat pack.conf
com.sun.java.util.jar.pack.dump.bands=true
% pack200 --no-gzip --config-file=pack.conf \
--verbose golden.jar.pack golden.jar
This command will dump the Bands in a unique directory BD_XXXXXX,
one can inspect the directory to ensure all of the bands are being
generated. Familiarity of the Pack200 specification is suggested.
\ No newline at end of file
<project name="PackageVerify" default="dist" basedir="..">
<!-- Requires ant 1.6.1+ and JDK 1.6+-->
<!-- set global properties for this build -->
<property name="src" value="${basedir}/src"/>
<property name="build" value="${basedir}/build"/>
<property name="dist" value="${basedir}/dist"/>
<property name="make" value="${basedir}/make"/>
<property name="classes" value="${build}/classes"/>
<property name="api" value="${build}/api"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${classes}"/>
<mkdir dir="${api}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
source="1.6"
srcdir="${src}"
destdir="${build}/classes"
verbose="no"
debug="on"
/>
</target>
<target name="doc" depends="init, compile">
<javadoc
source="1.6"
sourcepath="${src}"
destdir="${api}"
/>
</target>
<target name="dist" depends="compile, doc">
<!-- Put everything in jar file -->
<jar destfile="${dist}/pack200-verifier.jar">
<manifest>
<attribute name="Main-Class" value="sun.tools.pack.verify.Main"/>
</manifest>
<fileset dir="${classes}"/>
</jar>
<zip destfile="dist/pack200-verifier-doc.zip">
<fileset dir="${api}"/>
</zip>
</target>
<target name="clean">
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
/*
* 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.
*
* 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 sun.tools.pack.verify;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import xmlkit.*;
public class ClassCompare {
/*
* @author ksrini
*/
private static XMLKit.Element getXMLelement(InputStream is,
boolean ignoreUnkAttrs,
List<String> ignoreElements) throws IOException {
ClassReader cr = new ClassReader();
cr.keepOrder = false;
XMLKit.Element e = cr.readFrom(is);
if (ignoreElements != null) {
XMLKit.Filter filter = XMLKit.elementFilter(ignoreElements);
e.removeAllInTree(filter);
}
if (ignoreUnkAttrs == true) {
// This removes any unknown attributes
e.removeAllInTree(XMLKit.elementFilter("Attribute"));
}
return e;
}
private static String getXMLPrettyString(XMLKit.Element e) throws IOException {
StringWriter out = new StringWriter();
e.writePrettyTo(out);
return out.toString();
}
private static boolean compareClass0(JarFile jf1, JarFile jf2,
JarEntry je, boolean ignoreUnkAttrs,
List<String> ignoreElements)
throws IOException {
InputStream is1 = jf1.getInputStream(je);
InputStream is2 = jf2.getInputStream(je);
// First we try to compare the bits if they are the same
boolean bCompare = JarFileCompare.compareStreams(is1, is2);
// If they are the same there is nothing more to do.
if (bCompare) {
Globals.println("+++" + je.getName() + "+++\t"
+ "b/b:PASS");
return bCompare;
}
is1.close();
is2.close();
is1 = jf1.getInputStream(je);
is2 = jf2.getInputStream(je);
XMLKit.Element e1 = getXMLelement(is1, ignoreUnkAttrs, ignoreElements);
XMLKit.Element e2 = getXMLelement(is2, ignoreUnkAttrs, ignoreElements);
Globals.print("+++" + je.getName() + "+++\t"
+ e1.size() + "/" + e1.size() + ":");
boolean result = true;
if (e1.equals(e2)) {
Globals.println("PASS");
} else {
Globals.println("FAIL");
Globals.log("Strings differs");
Globals.log(getXMLPrettyString(e1));
Globals.log("----------");
Globals.log(getXMLPrettyString(e2));
result = false;
}
return result;
}
/*
* Given two Class Paths could be jars the first being a reference
* will execute a series of comparisons on the classname specified
* The className could be null in which case it will iterate through
* all the classes, otherwise it will compare one class and exit.
*/
public static boolean compareClass(String jar1, String jar2,
String className, boolean ignoreUnkAttrs,
List<String> ignoreElements)
throws IOException {
Globals.println("Unknown attributes ignored:" + ignoreUnkAttrs);
if (ignoreElements != null) {
Globals.println(ignoreElements.toString());
}
JarFile jf1 = new JarFile(jar1);
JarFile jf2 = new JarFile(jar2);
boolean result = true;
if (className == null) {
for (JarEntry je1 : Collections.list((Enumeration<JarEntry>) jf1.entries())) {
if (je1.getName().endsWith(".class")) {
JarEntry je2 = jf2.getJarEntry(je1.getName());
boolean pf = compareClass0(jf1, jf2, je1, ignoreUnkAttrs, ignoreElements);
if (result == true) {
result = pf;
}
}
}
} else {
JarEntry je1 = jf1.getJarEntry(className);
result = compareClass0(jf1, jf2, je1, ignoreUnkAttrs, ignoreElements);
}
if (result == false) {
throw new RuntimeException("Class structural comparison failure");
}
return result;
}
public static boolean compareClass(String jar1, String jar2,
String className) throws IOException {
Stack<String> s = new Stack();
if (Globals.ignoreDebugAttributes()) {
s = new Stack();
s.push("LocalVariable");
s.push("LocalVariableType");
s.push("LineNumber");
s.push("SourceFile");
}
return compareClass(jar1, jar2, className, Globals.ignoreUnknownAttributes(), s);
}
}
/*
* 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.
*
* 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.
*/
/*
* A collection of useful global utilities commonly used.
*/
package sun.tools.pack.verify;
import java.io.*;
import java.util.*;
/*
* @author ksrini
*/
class Globals {
private static int errors = 0;
private static PrintWriter _pw = null;
private static String _logFileName = null;
private static final String DEFAULT_LOG_FILE = "verifier.log";
private static boolean _verbose = true;
private static boolean _ignoreJarDirectories = false;
private static boolean _checkJarClassOrdering = true;
private static boolean _bitWiseClassCompare = false;
// Ignore Deprecated, SourceFile and Synthetic
private static boolean _ignoreCompileAttributes = false;
// Ignore Debug Attributes LocalVariableTable, LocalVariableType,LineNumberTable
private static boolean _ignoreDebugAttributes = false;
private static boolean _ignoreUnknownAttributes = false;
private static boolean _validateClass = true;
private static Globals _instance = null;
static Globals getInstance() {
if (_instance == null) {
_instance = new Globals();
_verbose = (System.getProperty("sun.tools.pack.verify.verbose") == null)
? false : true;
_ignoreJarDirectories = (System.getProperty("ignoreJarDirectories") == null)
? false : true;
}
return _instance;
}
static boolean ignoreCompileAttributes() {
return _ignoreCompileAttributes;
}
static boolean ignoreDebugAttributes() {
return _ignoreDebugAttributes;
}
static boolean ignoreUnknownAttributes() {
return _ignoreUnknownAttributes;
}
static boolean ignoreJarDirectories() {
return _ignoreJarDirectories;
}
static boolean validateClass() {
return _validateClass;
}
static void setCheckJarClassOrdering(boolean flag) {
_checkJarClassOrdering = flag;
}
static boolean checkJarClassOrdering() {
return _checkJarClassOrdering;
}
static boolean bitWiseClassCompare() {
return _bitWiseClassCompare;
}
static boolean setBitWiseClassCompare(boolean flag) {
return _bitWiseClassCompare = flag;
}
public static boolean setIgnoreCompileAttributes(boolean flag) {
return _ignoreCompileAttributes = flag;
}
static boolean setIgnoreDebugAttributes(boolean flag) {
return _ignoreDebugAttributes = flag;
}
static boolean setIgnoreUnknownAttributes(boolean flag) {
return _ignoreUnknownAttributes = flag;
}
static boolean setValidateClass(boolean flag) {
return _validateClass = flag;
}
static int getErrors() {
return errors;
}
static void trace(String s) {
if (_verbose) {
println(s);
}
}
static void print(String s) {
_pw.print(s);
}
static void println(String s) {
_pw.println(s);
}
static void log(String s) {
errors++;
_pw.println("ERROR:" + s);
}
static void lognoln(String s) {
errors++;
_pw.print(s);
}
private static PrintWriter openFile(String fileName) {
//Lets create the directory if it does not exist.
File f = new File(fileName);
File baseDir = f.getParentFile();
if (baseDir != null && baseDir.exists() == false) {
baseDir.mkdirs();
}
try {
return new PrintWriter(new FileWriter(f), true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void closeFile() {
_pw.flush();
_pw.close();
}
static void printPropsToLog() {
println("Log started " + new Date(System.currentTimeMillis()));
print(System.getProperty("java.vm.version"));
println("\t" + System.getProperty("java.vm.name"));
println("System properties");
println("\tjava.home=" + System.getProperty("java.home"));
println("\tjava.class.version=" + System.getProperty("java.class.version"));
println("\tjava.class.path=" + System.getProperty("java.class.path"));
println("\tjava.ext.dirs=" + System.getProperty("java.ext.dirs"));
println("\tos.name=" + System.getProperty("os.name"));
println("\tos.arch=" + System.getProperty("os.arch"));
println("\tos.version=" + System.getProperty("os.version"));
println("\tuser.name=" + System.getProperty("user.name"));
println("\tuser.home=" + System.getProperty("user.home"));
println("\tuser.dir=" + System.getProperty("user.dir"));
println("\tLocale.getDefault=" + Locale.getDefault());
println("System properties end");
}
static void openLog(String s) {
_logFileName = (s != null) ? s : "." + File.separator + DEFAULT_LOG_FILE;
_logFileName = (new File(_logFileName).isDirectory())
? _logFileName + File.separator + DEFAULT_LOG_FILE : _logFileName;
_pw = openFile(_logFileName);
printPropsToLog();
}
static void closeLog() {
closeFile();
}
static String getLogFileName() {
return _logFileName;
}
static void diffCharData(String s1, String s2) {
boolean diff = false;
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
if (c1.length != c2.length) {
diff = true;
Globals.log("Length differs: " + (c1.length - c2.length));
}
// Take the smaller of the two arrays to prevent Array...Exception
int minlen = (c1.length < c2.length) ? c1.length : c2.length;
for (int i = 0; i < c1.length; i++) {
if (c1[i] != c2[i]) {
diff = true;
Globals.lognoln("\t idx[" + i + "] 0x" + Integer.toHexString(c1[i]) + "<>" + "0x" + Integer.toHexString(c2[i]));
Globals.log(" -> " + c1[i] + "<>" + c2[i]);
}
}
}
static void diffByteData(String s1, String s2) {
boolean diff = false;
byte[] b1 = s1.getBytes();
byte[] b2 = s2.getBytes();
if (b1.length != b2.length) {
diff = true;
//(+) b1 is greater, (-) b2 is greater
Globals.log("Length differs diff: " + (b1.length - b2.length));
}
// Take the smaller of the two array to prevent Array...Exception
int minlen = (b1.length < b2.length) ? b1.length : b2.length;
for (int i = 0; i < b1.length; i++) {
if (b1[i] != b2[i]) {
diff = true;
Globals.log("\t" + "idx[" + i + "] 0x" + Integer.toHexString(b1[i]) + "<>" + "0x" + Integer.toHexString(b2[i]));
}
}
}
static void dumpToHex(String s) {
try {
dumpToHex(s.getBytes("UTF-8"));
} catch (UnsupportedEncodingException uce) {
throw new RuntimeException(uce);
}
}
static void dumpToHex(byte[] buffer) {
int linecount = 0;
byte[] b = new byte[16];
for (int i = 0; i < buffer.length; i += 16) {
if (buffer.length - i > 16) {
System.arraycopy(buffer, i, b, 0, 16);
print16Bytes(b, linecount);
linecount += 16;
} else {
System.arraycopy(buffer, i, b, 0, buffer.length - i);
for (int n = buffer.length - (i + 1); n < 16; n++) {
b[n] = 0;
}
print16Bytes(b, linecount);
linecount += 16;
}
}
Globals.log("-----------------------------------------------------------------");
}
static void print16Bytes(byte[] buffer, int linecount) {
final int MAX = 4;
Globals.lognoln(paddedHexString(linecount, 4) + " ");
for (int i = 0; i < buffer.length; i += 2) {
int iOut = pack2Bytes2Int(buffer[i], buffer[i + 1]);
Globals.lognoln(paddedHexString(iOut, 4) + " ");
}
Globals.lognoln("| ");
StringBuilder sb = new StringBuilder(new String(buffer));
for (int i = 0; i < buffer.length; i++) {
if (Character.isISOControl(sb.charAt(i))) {
sb.setCharAt(i, '.');
}
}
Globals.log(sb.toString());
}
static int pack2Bytes2Int(byte b1, byte b2) {
int out = 0x0;
out += b1;
out <<= 8;
out &= 0x0000ffff;
out |= 0x000000ff & b2;
return out;
}
static String paddedHexString(int n, int max) {
char[] c = Integer.toHexString(n).toCharArray();
char[] out = new char[max];
for (int i = 0; i < max; i++) {
out[i] = '0';
}
int offset = (max - c.length < 0) ? 0 : max - c.length;
for (int i = 0; i < c.length; i++) {
out[offset + i] = c[i];
}
return new String(out);
}
}
/*
* 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.
*
* 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 sun.tools.pack.verify;
import java.io.*;
import java.util.*;
import java.util.jar.*;
class JarFileCompare {
/*
* @author ksrini
*/
private static VerifyTreeSet getVerifyTreeSet(String jarPath) {
VerifyTreeSet vts = new VerifyTreeSet();
try {
JarFile j = new JarFile(jarPath);
for (JarEntry je : Collections.list((Enumeration<JarEntry>) j.entries())) {
if (!je.isDirectory()) { // totally ignore directories
vts.add(je.getName());
}
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
return vts;
}
private static LinkedList getListOfClasses(String jarPath) {
LinkedList l = new LinkedList();
try {
JarFile j = new JarFile(jarPath);
for (JarEntry je : Collections.list((Enumeration<JarEntry>) j.entries())) {
if (!je.isDirectory() && je.getName().endsWith(".class")) {
l.add(je.getName());
}
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
return l;
}
private static void jarDirectoryCompare(String jarPath1, String jarPath2) {
VerifyTreeSet vts1 = getVerifyTreeSet(jarPath1);
VerifyTreeSet vts2 = getVerifyTreeSet(jarPath2);
TreeSet diff1 = vts1.diff(vts2);
if (diff1.size() > 0) {
Globals.log("Left has the following entries that right does not have");
Globals.log(diff1.toString());
}
TreeSet diff2 = vts2.diff(vts1);
if (diff2.size() > 0) {
Globals.log("Right has the following entries that left does not have");
Globals.log(diff2.toString());
}
if (Globals.checkJarClassOrdering()) {
boolean error = false;
Globals.println("Checking Class Ordering");
LinkedList l1 = getListOfClasses(jarPath1);
LinkedList l2 = getListOfClasses(jarPath2);
if (l1.size() != l2.size()) {
error = true;
Globals.log("The number of classes differs");
Globals.log("\t" + l1.size() + "<>" + l2.size());
}
for (int i = 0; i < l1.size(); i++) {
String s1 = (String) l1.get(i);
String s2 = (String) l2.get(i);
if (s1.compareTo(s2) != 0) {
error = true;
Globals.log("Ordering differs at[" + i + "] = " + s1);
Globals.log("\t" + s2);
}
}
}
}
/*
* Returns true if the two Streams are bit identical, and false if they
* are not, no further diagnostics
*/
static boolean compareStreams(InputStream is1, InputStream is2) {
BufferedInputStream bis1 = new BufferedInputStream(is1, 8192);
BufferedInputStream bis2 = new BufferedInputStream(is2, 8192);
try {
int i1, i2;
int count = 0;
while ((i1 = bis1.read()) == (i2 = bis2.read())) {
count++;
if (i1 < 0) {
// System.out.println("bytes read " + count);
return true; // got all the way to EOF
}
}
return false; // reads returned dif
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
private static void checkEntry(JarFile jf1, JarFile jf2, JarEntry je) throws IOException {
InputStream is1 = jf1.getInputStream(je);
InputStream is2 = jf2.getInputStream(je);
if (is1 != null && is2 != null) {
if (!compareStreams(jf1.getInputStream(je), jf2.getInputStream(je))) {
Globals.println("+++" + je.getName() + "+++");
Globals.log("Error: File:" + je.getName()
+ " differs, use a diff util for further diagnostics");
}
} else {
Globals.println("+++" + je.getName() + "+++");
Globals.log("Error: File:" + je.getName() + " not found in " + jf2.getName());
}
}
/*
* Given two jar files we compare and see if the jarfiles have all the
* entries. The property ignoreJarDirectories is set to true by default
* which means that Directory entries in a jar may be ignore.
*/
static void jarCompare(String jarPath1, String jarPath2) {
jarDirectoryCompare(jarPath1, jarPath2);
try {
JarFile jf1 = new JarFile(jarPath1);
JarFile jf2 = new JarFile(jarPath2);
int nclasses = 0;
int nentries = 0;
int entries_checked = 0;
int classes_checked = 0;
for (JarEntry je : Collections.list((Enumeration<JarEntry>) jf1.entries())) {
if (!je.isDirectory() && !je.getName().endsWith(".class")) {
nentries++;
} else if (je.getName().endsWith(".class")) {
nclasses++;
}
}
for (JarEntry je : Collections.list((Enumeration<JarEntry>) jf1.entries())) {
if (je.isDirectory()) {
continue; // Ignore directories
}
if (!je.getName().endsWith(".class")) {
entries_checked++;
if (je.getName().compareTo("META-INF/MANIFEST.MF") == 0) {
Manifest mf1 = new Manifest(jf1.getInputStream(je));
Manifest mf2 = new Manifest(jf2.getInputStream(je));
if (!mf1.equals(mf2)) {
Globals.log("Error: Manifests differ");
Globals.log("Manifest1");
Globals.log(mf1.getMainAttributes().entrySet().toString());
Globals.log("Manifest2");
Globals.log(mf2.getMainAttributes().entrySet().toString());
}
} else {
checkEntry(jf1, jf2, je);
}
} else if (Globals.bitWiseClassCompare() == true) {
checkEntry(jf1, jf2, je);
classes_checked++;
}
}
if (Globals.bitWiseClassCompare()) {
Globals.println("Class entries checked (byte wise)/Total Class entries = "
+ classes_checked + "/" + nclasses);
}
Globals.println("Non-class entries checked/Total non-class entries = "
+ entries_checked + "/" + nentries);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
/*
* 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.
*
* 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.
*/
// The Main Entry point
package sun.tools.pack.verify;
import java.io.*;
/**
* This class provides a convenient entry point to the pack200 verifier. This
* compares two classes, either in path or in an archive.
* @see xmlkit.XMLKit
* @author ksrini
*/
public class Main {
private static void syntax() {
System.out.println("Usage: ");
System.out.println("\tREFERENCE_CLASSPATH COMPARED_CLASSPATH [Options]");
System.out.println("\tOptions:");
System.out.println("\t\t-O check jar ordering");
System.out.println("\t\t-C ignore compile attributes (Deprecated, SourceFile, Synthetic, )");
System.out.println("\t\t-D ignore debug attributes (LocalVariable, LineNumber)");
System.out.println("\t\t-u ignore unknown attributes");
System.out.println("\t\t-V turn off class validation");
System.out.println("\t\t-c CLASS, compare CLASS only");
System.out.println("\t\t-b Compares all entries bitwise only");
System.out.println("\t\t-l Directory or Log File Name");
}
/**
* main entry point to the class file comparator, which compares semantically
* class files in a classpath or an archive.
* @param args String array as described below
* @throws RuntimeException
* <pre>
* Usage:
* ReferenceClasspath SpecimenClaspath [Options]
* Options:
* -O check jar ordering
* -C do not compare compile attributes (Deprecated, SourceFile, Synthetic)
* -D do not compare debug attribute (LocalVariableTable, LineNumberTable)
* -u ignore unknown attributes
* -V turn off class validation
* -c class, compare a single class
* -b compares all entries bitwise (fastest)
* -l directory or log file name
* </pre>
*/
public static void main(String args[]) {
Globals.getInstance();
if (args == null || args.length < 2) {
syntax();
System.exit(1);
}
String refJarFileName = null;
String cmpJarFileName = null;
String specificClass = null;
String logDirFileName = null;
for (int i = 0; i < args.length; i++) {
if (i == 0) {
refJarFileName = args[0];
continue;
}
if (i == 1) {
cmpJarFileName = args[1];
continue;
}
if (args[i].startsWith("-O")) {
Globals.setCheckJarClassOrdering(true);
}
if (args[i].startsWith("-b")) {
Globals.setBitWiseClassCompare(true);
}
if (args[i].startsWith("-C")) {
Globals.setIgnoreCompileAttributes(true);
}
if (args[i].startsWith("-D")) {
Globals.setIgnoreDebugAttributes(true);
}
if (args[i].startsWith("-V")) {
Globals.setValidateClass(false);
}
if (args[i].startsWith("-c")) {
i++;
specificClass = args[i].trim();
}
if (args[i].startsWith("-u")) {
i++;
Globals.setIgnoreUnknownAttributes(true);
}
if (args[i].startsWith("-l")) {
i++;
logDirFileName = args[i].trim();
}
}
Globals.openLog(logDirFileName);
File refJarFile = new File(refJarFileName);
File cmpJarFile = new File(cmpJarFileName);
String f1 = refJarFile.getAbsoluteFile().toString();
String f2 = cmpJarFile.getAbsoluteFile().toString();
System.out.println("LogFile:" + Globals.getLogFileName());
System.out.println("Reference JAR:" + f1);
System.out.println("Compared JAR:" + f2);
Globals.println("LogFile:" + Globals.getLogFileName());
Globals.println("Reference JAR:" + f1);
Globals.println("Compared JAR:" + f2);
Globals.println("Ignore Compile Attributes:" + Globals.ignoreCompileAttributes());
Globals.println("Ignore Debug Attributes:" + Globals.ignoreDebugAttributes());
Globals.println("Ignore Unknown Attributes:" + Globals.ignoreUnknownAttributes());
Globals.println("Class ordering check:" + Globals.checkJarClassOrdering());
Globals.println("Class validation check:" + Globals.validateClass());
Globals.println("Bit-wise compare:" + Globals.bitWiseClassCompare());
Globals.println("ClassName:" + ((specificClass == null) ? "ALL" : specificClass));
if (specificClass == null && Globals.bitWiseClassCompare() == true) {
JarFileCompare.jarCompare(refJarFileName, cmpJarFileName);
} else {
try {
ClassCompare.compareClass(refJarFileName, cmpJarFileName, specificClass);
} catch (Exception e) {
Globals.log("Exception " + e);
throw new RuntimeException(e);
}
}
if (Globals.getErrors() > 0) {
System.out.println("FAIL");
Globals.println("FAIL");
System.exit(Globals.getErrors());
}
System.out.println("PASS");
Globals.println("PASS");
System.exit(Globals.getErrors());
}
}
/*
* 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.
*
* 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 sun.tools.pack.verify;
import java.util.*;
/*
* @author ksrini
*/
class VerifyTreeSet<K> extends java.util.TreeSet {
VerifyTreeSet() {
super();
}
public VerifyTreeSet(Comparator c) {
super(c);
}
public TreeSet<K> diff(TreeSet in) {
TreeSet<K> delta = (TreeSet<K>) this.clone();
delta.removeAll(in);
return delta;
}
}
/*
* 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
import xmlkit.XMLKit.*;
import java.util.*;
import java.security.MessageDigest;
import java.nio.ByteBuffer;
import xmlkit.XMLKit.Element;
/*
* @author jrose
*/
public abstract class ClassSyntax {
public interface GetCPIndex {
int getCPIndex(int tag, String name); // cp finder
}
public static final int CONSTANT_Utf8 = 1,
CONSTANT_Integer = 3,
CONSTANT_Float = 4,
CONSTANT_Long = 5,
CONSTANT_Double = 6,
CONSTANT_Class = 7,
CONSTANT_String = 8,
CONSTANT_Fieldref = 9,
CONSTANT_Methodref = 10,
CONSTANT_InterfaceMethodref = 11,
CONSTANT_NameAndType = 12;
private static final String[] cpTagName = {
/* 0: */null,
/* 1: */ "Utf8",
/* 2: */ null,
/* 3: */ "Integer",
/* 4: */ "Float",
/* 5: */ "Long",
/* 6: */ "Double",
/* 7: */ "Class",
/* 8: */ "String",
/* 9: */ "Fieldref",
/* 10: */ "Methodref",
/* 11: */ "InterfaceMethodref",
/* 12: */ "NameAndType",
null
};
private static final Set<String> cpTagNames;
static {
Set<String> set = new HashSet<String>(Arrays.asList(cpTagName));
set.remove(null);
cpTagNames = Collections.unmodifiableSet(set);
}
public static final int ITEM_Top = 0, // replicates by [1..4,1..4]
ITEM_Integer = 1, // (ditto)
ITEM_Float = 2,
ITEM_Double = 3,
ITEM_Long = 4,
ITEM_Null = 5,
ITEM_UninitializedThis = 6,
ITEM_Object = 7,
ITEM_Uninitialized = 8,
ITEM_ReturnAddress = 9,
ITEM_LIMIT = 10;
private static final String[] itemTagName = {
"Top",
"Integer",
"Float",
"Double",
"Long",
"Null",
"UninitializedThis",
"Object",
"Uninitialized",
"ReturnAddress",};
private static final Set<String> itemTagNames;
static {
Set<String> set = new HashSet<String>(Arrays.asList(itemTagName));
set.remove(null);
itemTagNames = Collections.unmodifiableSet(set);
}
protected static final HashMap<String, String> attrTypesBacking;
protected static final Map<String, String> attrTypesInit;
static {
HashMap<String, String> at = new HashMap<String, String>();
//at.put("*.Deprecated", "<deprecated=true>");
//at.put("*.Synthetic", "<synthetic=true>");
////at.put("Field.ConstantValue", "<constantValue=>KQH");
//at.put("Class.SourceFile", "<sourceFile=>RUH");
at.put("Method.Bridge", "<Bridge>");
at.put("Method.Varargs", "<Varargs>");
at.put("Class.Enum", "<Enum>");
at.put("*.Signature", "<Signature>RSH");
//at.put("*.Deprecated", "<Deprecated>");
//at.put("*.Synthetic", "<Synthetic>");
at.put("Field.ConstantValue", "<ConstantValue>KQH");
at.put("Class.SourceFile", "<SourceFile>RUH");
at.put("Class.InnerClasses", "NH[<InnerClass><class=>RCH<outer=>RCH<name=>RUH<flags=>FH]");
at.put("Code.LineNumberTable", "NH[<LineNumber><bci=>PH<line=>H]");
at.put("Code.LocalVariableTable", "NH[<LocalVariable><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
at.put("Code.LocalVariableTypeTable", "NH[<LocalVariableType><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
at.put("Method.Exceptions", "NH[<Exception><name=>RCH]");
at.put("Method.Code", "<Code>...");
at.put("Code.StackMapTable", "<Frame>...");
//at.put("Code.StkMapX", "<FrameX>...");
if (true) {
at.put("Code.StackMapTable",
"[NH[<Frame>(1)]]"
+ "[TB"
+ "(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79"
+ ",80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95"
+ ",96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111"
+ ",112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127"
+ ")[<SameLocals1StackItemFrame>(4)]"
+ "(247)[<SameLocals1StackItemExtended>H(4)]"
+ "(248)[<Chop3>H]"
+ "(249)[<Chop2>H]"
+ "(250)[<Chop1>H]"
+ "(251)[<SameFrameExtended>H]"
+ "(252)[<Append1>H(4)]"
+ "(253)[<Append2>H(4)(4)]"
+ "(254)[<Append3>H(4)(4)(4)]"
+ "(255)[<FullFrame>H(2)(3)]"
+ "()[<SameFrame>]]"
+ "[NH[<Local>(4)]]"
+ "[NH[<Stack>(4)]]"
+ "[TB"
+ ("(0)[<Top>]"
+ "(1)[<ItemInteger>](2)[<ItemFloat>](3)[<ItemDouble>](4)[<ItemLong>]"
+ "(5)[<ItemNull>](6)[<ItemUninitializedThis>]"
+ "(7)[<ItemObject><class=>RCH]"
+ "(8)[<ItemUninitialized><bci=>PH]"
+ "()[<ItemUnknown>]]"));
}
at.put("Class.EnclosingMethod", "<EnclosingMethod><class=>RCH<desc=>RDH");//RDNH
// Layouts of metadata attrs:
String vpf = "[<RuntimeVisibleAnnotation>";
String ipf = "[<RuntimeInvisibleAnnotation>";
String apf = "[<Annotation>";
String mdanno2 = ""
+ "<type=>RSHNH[<Member><name=>RUH(3)]]"
+ ("[TB"
+ "(\\B,\\C,\\I,\\S,\\Z)[<value=>KIH]"
+ "(\\D)[<value=>KDH]"
+ "(\\F)[<value=>KFH]"
+ "(\\J)[<value=>KJH]"
+ "(\\c)[<class=>RSH]"
+ "(\\e)[<type=>RSH<name=>RUH]"
+ "(\\s)[<String>RUH]"
+ "(\\@)[(2)]"
+ "(\\[)[NH[<Element>(3)]]"
+ "()[]"
+ "]");
String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2;
String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2;
String vparamanno = ""
+ "[NB[<RuntimeVisibleParameterAnnotation>(1)]][NH[(2)]]"
+ apf + mdanno2;
String iparamanno = ""
+ "[NB[<RuntimeInvisibleParameterAnnotation>(1)]][NH[(2)]]"
+ apf + mdanno2;
String mdannodef = "[<AnnotationDefault>(3)][(1)]" + apf + mdanno2;
String[] mdplaces = {"Class", "Field", "Method"};
for (String place : mdplaces) {
at.put(place + ".RuntimeVisibleAnnotations", visanno);
at.put(place + ".RuntimeInvisibleAnnotations", invanno);
}
at.put("Method.RuntimeVisibleParameterAnnotations", vparamanno);
at.put("Method.RuntimeInvisibleParameterAnnotations", iparamanno);
at.put("Method.AnnotationDefault", mdannodef);
attrTypesBacking = at;
attrTypesInit = Collections.unmodifiableMap(at);
}
;
private static final String[] jcovAttrTypes = {
"Code.CoverageTable=NH[<Coverage><bci=>PH<type=>H<line=>I<pos=>I]",
"Code.CharacterRangeTable=NH[<CharacterRange><bci=>PH<endbci=>POH<from=>I<to=>I<flag=>H]",
"Class.SourceID=<SourceID><id=>RUH",
"Class.CompilationID=<CompilationID><id=>RUH"
};
protected static final String[][] modifierNames = {
{"public"},
{"private"},
{"protected"},
{"static"},
{"final"},
{"synchronized"},
{null, "volatile", "bridge"},
{null, "transient", "varargs"},
{null, null, "native"},
{"interface"},
{"abstract"},
{"strictfp"},
{"synthetic"},
{"annotation"},
{"enum"},};
protected static final String EIGHT_BIT_CHAR_ENCODING = "ISO8859_1";
protected static final String UTF8_ENCODING = "UTF8";
// What XML tags are used by this syntax, apart from attributes?
protected static final Set<String> nonAttrTags;
static {
HashSet<String> tagSet = new HashSet<String>();
Collections.addAll(tagSet, new String[]{
"ConstantPool",// the CP
"Class", // the class
"Interface", // implemented interfaces
"Method", // methods
"Field", // fields
"Handler", // exception handler pseudo-attribute
"Attribute", // unparsed attribute
"Bytes", // bytecodes
"Instructions" // bytecodes, parsed
});
nonAttrTags = Collections.unmodifiableSet(tagSet);
}
// Accessors.
public static Set<String> nonAttrTags() {
return nonAttrTags;
}
public static String cpTagName(int t) {
t &= 0xFF;
String ts = null;
if (t < cpTagName.length) {
ts = cpTagName[t];
}
if (ts != null) {
return ts;
}
return ("UnknownTag" + (int) t).intern();
}
public static int cpTagValue(String name) {
for (int t = 0; t < cpTagName.length; t++) {
if (name.equals(cpTagName[t])) {
return t;
}
}
return 0;
}
public static String itemTagName(int t) {
t &= 0xFF;
String ts = null;
if (t < itemTagName.length) {
ts = itemTagName[t];
}
if (ts != null) {
return ts;
}
return ("UnknownItem" + (int) t).intern();
}
public static int itemTagValue(String name) {
for (int t = 0; t < itemTagName.length; t++) {
if (name.equals(itemTagName[t])) {
return t;
}
}
return -1;
}
public void addJcovAttrTypes() {
addAttrTypes(jcovAttrTypes);
}
// Public methods for declaring attribute types.
protected Map<String, String> attrTypes = attrTypesInit;
public void addAttrType(String opt) {
int eqpos = opt.indexOf('=');
addAttrType(opt.substring(0, eqpos), opt.substring(eqpos + 1));
}
public void addAttrTypes(String[] opts) {
for (String opt : opts) {
addAttrType(opt);
}
}
private void checkAttr(String attr) {
if (!attr.startsWith("Class.")
&& !attr.startsWith("Field.")
&& !attr.startsWith("Method.")
&& !attr.startsWith("Code.")
&& !attr.startsWith("*.")) {
throw new IllegalArgumentException("attr name must start with 'Class.', etc.");
}
String uattr = attr.substring(attr.indexOf('.') + 1);
if (nonAttrTags.contains(uattr)) {
throw new IllegalArgumentException("attr name must not be one of " + nonAttrTags);
}
}
private void checkAttrs(Map<String, String> at) {
for (String attr : at.keySet()) {
checkAttr(attr);
}
}
private void modAttrs() {
if (attrTypes == attrTypesInit) {
// Make modifiable.
attrTypes = new HashMap<String, String>(attrTypesBacking);
}
}
public void addAttrType(String attr, String fmt) {
checkAttr(attr);
modAttrs();
attrTypes.put(attr, fmt);
}
public void addAttrTypes(Map<String, String> at) {
checkAttrs(at);
modAttrs();
attrTypes.putAll(at);
}
public Map<String, String> getAttrTypes() {
if (attrTypes == attrTypesInit) {
return attrTypes;
}
return Collections.unmodifiableMap(attrTypes);
}
public void setAttrTypes(Map<String, String> at) {
checkAttrs(at);
modAttrs();
attrTypes.keySet().retainAll(at.keySet());
attrTypes.putAll(at);
}
// attr format helpers
protected static boolean matchTag(int tagValue, String caseStr) {
//System.out.println("matchTag "+tagValue+" in "+caseStr);
for (int pos = 0, max = caseStr.length(), comma;
pos < max;
pos = comma + 1) {
int caseValue;
if (caseStr.charAt(pos) == '\\') {
caseValue = caseStr.charAt(pos + 1);
comma = pos + 2;
assert (comma == max || caseStr.charAt(comma) == ',');
} else {
comma = caseStr.indexOf(',', pos);
if (comma < 0) {
comma = max;
}
caseValue = Integer.parseInt(caseStr.substring(pos, comma));
}
if (tagValue == caseValue) {
return true;
}
}
return false;
}
protected static String[] getBodies(String type) {
ArrayList<String> bodies = new ArrayList<String>();
for (int i = 0; i < type.length();) {
String body = getBody(type, i);
bodies.add(body);
i += body.length() + 2; // skip body and brackets
}
return bodies.toArray(new String[bodies.size()]);
}
protected static String getBody(String type, int i) {
assert (type.charAt(i) == '[');
int next = ++i; // skip bracket
for (int depth = 1; depth > 0; next++) {
switch (type.charAt(next)) {
case '[':
depth++;
break;
case ']':
depth--;
break;
case '(':
next = type.indexOf(')', next);
break;
case '<':
next = type.indexOf('>', next);
break;
}
assert (next > 0);
}
--next; // get before bracket
assert (type.charAt(next) == ']');
return type.substring(i, next);
}
public Element makeCPDigest(int length) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (java.security.NoSuchAlgorithmException ee) {
throw new Error(ee);
}
int items = 0;
for (Element e : cpool.elements()) {
if (items == length) {
break;
}
if (cpTagNames.contains(e.getName())) {
items += 1;
md.update((byte) cpTagValue(e.getName()));
try {
md.update(e.getText().toString().getBytes(UTF8_ENCODING));
} catch (java.io.UnsupportedEncodingException ee) {
throw new Error(ee);
}
}
}
ByteBuffer bb = ByteBuffer.wrap(md.digest());
String l0 = Long.toHexString(bb.getLong(0));
String l1 = Long.toHexString(bb.getLong(8));
while (l0.length() < 16) {
l0 = "0" + l0;
}
while (l1.length() < 16) {
l1 = "0" + l1;
}
return new Element("Digest",
"length", "" + items,
"bytes", l0 + l1);
}
public Element getCPDigest(int length) {
if (length == -1) {
length = cpool.countAll(XMLKit.elementFilter(cpTagNames));
}
for (Element md : cpool.findAllElements("Digest").elements()) {
if (md.getAttrLong("length") == length) {
return md;
}
}
Element md = makeCPDigest(length);
cpool.add(md);
return md;
}
public Element getCPDigest() {
return getCPDigest(-1);
}
public boolean checkCPDigest(Element md) {
return md.equals(getCPDigest((int) md.getAttrLong("length")));
}
public static int computeInterfaceNum(String intMethRef) {
intMethRef = intMethRef.substring(1 + intMethRef.lastIndexOf(' '));
if (!intMethRef.startsWith("(")) {
return -1;
}
int signum = 1; // start with one for "this"
scanSig:
for (int i = 1; i < intMethRef.length(); i++) {
char ch = intMethRef.charAt(i);
signum++;
switch (ch) {
case ')':
--signum;
break scanSig;
case 'L':
i = intMethRef.indexOf(';', i);
break;
case '[':
while (ch == '[') {
ch = intMethRef.charAt(++i);
}
if (ch == 'L') {
i = intMethRef.indexOf(';', i);
}
break;
}
}
int num = (signum << 8) | 0;
//System.out.println("computeInterfaceNum "+intMethRef+" => "+num);
return num;
}
// Protected state for representing the class file.
protected Element cfile; // <ClassFile ...>
protected Element cpool; // <ConstantPool ...>
protected Element klass; // <Class ...>
protected Element currentMember; // varies during scans
protected Element currentCode; // varies during scans
}
/*
* 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
import java.util.*;
/*
* @author jrose
*/
public class CommandLineParser {
public CommandLineParser(String optionString) {
setOptionMap(optionString);
}
TreeMap<String, String[]> optionMap;
public void setOptionMap(String options) {
// Convert options string into optLines dictionary.
TreeMap<String, String[]> optmap = new TreeMap<String, String[]>();
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("duplicate option: "
+ optline.trim());
}
}
optionMap = optmap;
}
public String getOptionMap() {
TreeMap<String, String[]> optmap = optionMap;
StringBuffer sb = new StringBuffer();
for (String opt : optmap.keySet()) {
sb.append(opt);
for (String spec : optmap.get(opt)) {
sb.append(' ').append(spec);
}
sb.append('\n');
}
return sb.toString();
}
/**
* Remove a set of command-line options from args,
* storing them in the properties map in a canonicalized form.
*/
public String parse(List<String> args, Map<String, String> properties) {
//System.out.println(args+" // "+properties);
String resultString = null;
TreeMap<String, String[]> optmap = optionMap;
// 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("bad spec for "
+ 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;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册