jjg@416: /* ohair@554: * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. jjg@416: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@416: * jjg@416: * This code is free software; you can redistribute it and/or modify it jjg@416: * under the terms of the GNU General Public License version 2 only, as jjg@416: * published by the Free Software Foundation. jjg@416: * jjg@416: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@416: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@416: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@416: * version 2 for more details (a copy is included in the LICENSE file that jjg@416: * accompanied this code). jjg@416: * jjg@416: * You should have received a copy of the GNU General Public License version jjg@416: * 2 along with this work; if not, write to the Free Software Foundation, jjg@416: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@416: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. jjg@416: */ jjg@416: jjg@416: import java.io.DataInputStream; jjg@416: import java.io.File; jjg@416: import java.io.IOException; jjg@416: import java.io.InputStream; jjg@416: import java.io.PrintWriter; jjg@416: import java.io.StringWriter; jjg@416: import java.util.ArrayList; jjg@416: import java.util.Arrays; jjg@416: import java.util.Enumeration; jjg@416: import java.util.List; jjg@416: import java.util.Set; jjg@416: import java.util.TreeSet; jjg@416: import java.util.jar.JarEntry; jjg@416: import java.util.jar.JarFile; jjg@416: jjg@416: import com.sun.tools.classfile.AccessFlags; jjg@416: import com.sun.tools.classfile.ClassFile; jjg@416: import com.sun.tools.classfile.ConstantPoolException; jjg@416: import com.sun.tools.classfile.Method; jjg@416: import java.io.BufferedReader; jjg@416: import java.io.FileInputStream; jjg@416: import java.io.InputStreamReader; jjg@416: import java.util.LinkedHashSet; jjg@416: jjg@416: public class CompareTest { jjg@416: String[][] testCases = { jjg@416: { }, jjg@416: { "-jni" }, jjg@416: // { "-llni" }, jjg@416: }; jjg@416: jjg@416: public static void main(String... args) throws Exception { jjg@416: new CompareTest().run(args); jjg@416: } jjg@416: jjg@416: public void run(String... args) throws Exception { jjg@416: old_javah_cmd = new File(args[0]); jjg@416: rt_jar = new File(args[1]); jjg@416: jjg@416: Set testClasses; jjg@416: if (args.length > 2) { jjg@416: testClasses = new LinkedHashSet(Arrays.asList(Arrays.copyOfRange(args, 2, args.length))); jjg@416: } else jjg@416: testClasses = getNativeClasses(new JarFile(rt_jar)); jjg@416: jjg@416: for (String[] options: testCases) { jjg@416: for (String name: testClasses) { jjg@416: test(Arrays.asList(options), rt_jar, name); jjg@416: } jjg@416: } jjg@416: jjg@416: if (errors == 0) jjg@416: System.out.println(count + " tests passed"); jjg@416: else jjg@416: throw new Exception(errors + "/" + count + " tests failed"); jjg@416: } jjg@416: jjg@416: public void test(List options, File bootclasspath, String className) jjg@416: throws IOException, InterruptedException { jjg@416: System.err.println("test: " + options + " " + className); jjg@416: count++; jjg@416: jjg@416: testOptions = options; jjg@416: testClassName = className; jjg@416: jjg@416: File oldOutDir = initDir(file(new File("old"), className)); jjg@416: int old_rc = old_javah(options, oldOutDir, bootclasspath, className); jjg@416: jjg@416: File newOutDir = initDir(file(new File("new"), className)); jjg@416: int new_rc = new_javah(options, newOutDir, bootclasspath, className); jjg@416: jjg@416: if (old_rc != new_rc) jjg@416: error("return codes differ; old: " + old_rc + ", new: " + new_rc); jjg@416: jjg@416: compare(oldOutDir, newOutDir); jjg@416: } jjg@416: jjg@416: int old_javah(List options, File outDir, File bootclasspath, String className) jjg@416: throws IOException, InterruptedException { jjg@416: List cmd = new ArrayList(); jjg@416: cmd.add(old_javah_cmd.getPath()); jjg@416: cmd.addAll(options); jjg@416: cmd.add("-d"); jjg@416: cmd.add(outDir.getPath()); jjg@416: cmd.add("-bootclasspath"); jjg@416: cmd.add(bootclasspath.getPath()); jjg@416: cmd.add(className); jjg@416: System.err.println("old_javah: " + cmd); jjg@416: ProcessBuilder pb = new ProcessBuilder(cmd); jjg@416: pb.redirectErrorStream(true); jjg@416: Process p = pb.start(); jjg@416: BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); jjg@416: String line; jjg@416: StringBuilder sb = new StringBuilder(); jjg@416: while ((line = in.readLine()) != null) { jjg@416: sb.append(line); jjg@416: sb.append("\n"); jjg@416: } jjg@416: System.err.println("old javah out: " + sb.toString()); jjg@416: return p.waitFor(); jjg@416: } jjg@416: jjg@416: int new_javah(List options, File outDir, File bootclasspath, String className) { jjg@416: List args = new ArrayList(); jjg@416: args.addAll(options); jjg@416: args.add("-d"); jjg@416: args.add(outDir.getPath()); jjg@416: args.add("-bootclasspath"); jjg@416: args.add(bootclasspath.getPath()); jjg@416: args.add(className); jjg@416: StringWriter sw = new StringWriter(); jjg@416: PrintWriter pw = new PrintWriter(sw); jjg@416: int rc = com.sun.tools.javah.Main.run(args.toArray(new String[args.size()]), pw); jjg@416: pw.close(); jjg@416: System.err.println("new javah out: " + sw.toString()); jjg@416: return rc; jjg@416: } jjg@416: jjg@416: Set getNativeClasses(JarFile jar) throws IOException, ConstantPoolException { jjg@416: System.err.println("getNativeClasses: " + jar.getName()); jjg@416: Set results = new TreeSet(); jjg@416: Enumeration e = jar.entries(); jjg@416: while (e.hasMoreElements()) { jjg@416: JarEntry je = e.nextElement(); jjg@416: if (isNativeClass(jar, je)) { jjg@416: String name = je.getName(); jjg@416: results.add(name.substring(0, name.length() - 6).replace("/", ".")); jjg@416: } jjg@416: } jjg@416: return results; jjg@416: } jjg@416: jjg@416: boolean isNativeClass(JarFile jar, JarEntry entry) throws IOException, ConstantPoolException { jjg@416: String name = entry.getName(); jjg@416: if (name.startsWith("META-INF") || !name.endsWith(".class")) jjg@416: return false; jjg@416: //String className = name.substring(0, name.length() - 6).replace("/", "."); jjg@416: //System.err.println("check " + className); jjg@416: InputStream in = jar.getInputStream(entry); jjg@416: ClassFile cf = ClassFile.read(in); jjg@416: for (int i = 0; i < cf.methods.length; i++) { jjg@416: Method m = cf.methods[i]; jjg@416: if (m.access_flags.is(AccessFlags.ACC_NATIVE)) { jjg@416: // System.err.println(className); jjg@416: return true; jjg@416: } jjg@416: } jjg@416: return false; jjg@416: } jjg@416: jjg@416: void compare(File f1, File f2) throws IOException { jjg@416: if (f1.isFile() && f2.isFile()) jjg@416: compareFiles(f1, f2); jjg@416: else if (f1.isDirectory() && f2.isDirectory()) jjg@416: compareDirectories(f1, f2); jjg@416: else jjg@416: error("files differ: " jjg@416: + f1 + " (" + getFileType(f1) + "), " jjg@416: + f2 + " (" + getFileType(f2) + ")"); jjg@416: } jjg@416: jjg@416: void compareDirectories(File d1, File d2) throws IOException { jjg@416: Set list = new TreeSet(); jjg@416: list.addAll(Arrays.asList(d1.list())); jjg@416: list.addAll(Arrays.asList(d2.list())); jjg@416: for (String c: list) jjg@416: compare(new File(d1, c), new File(d2, c)); jjg@416: } jjg@416: jjg@416: void compareFiles(File f1, File f2) throws IOException { jjg@416: byte[] c1 = readFile(f1); jjg@416: byte[] c2 = readFile(f2); jjg@416: if (!Arrays.equals(c1, c2)) jjg@416: error("files differ: " + f1 + ", " + f2); jjg@416: } jjg@416: jjg@416: byte[] readFile(File file) throws IOException { jjg@416: int size = (int) file.length(); jjg@416: byte[] data = new byte[size]; jjg@416: DataInputStream in = new DataInputStream(new FileInputStream(file)); jjg@416: try { jjg@416: in.readFully(data); jjg@416: } finally { jjg@416: in.close(); jjg@416: } jjg@416: return data; jjg@416: } jjg@416: jjg@416: String getFileType(File f) { jjg@416: return f.isDirectory() ? "directory" jjg@416: : f.isFile() ? "file" jjg@416: : f.exists() ? "other" jjg@416: : "not found"; jjg@416: } jjg@416: jjg@416: /** jjg@416: * Set up an empty directory. jjg@416: */ jjg@416: public File initDir(File dir) { jjg@416: if (dir.exists()) jjg@416: deleteAll(dir); jjg@416: dir.mkdirs(); jjg@416: return dir; jjg@416: } jjg@416: jjg@416: /** jjg@416: * Delete a file or a directory (including all its contents). jjg@416: */ jjg@416: boolean deleteAll(File file) { jjg@416: if (file.isDirectory()) { jjg@416: for (File f: file.listFiles()) jjg@416: deleteAll(f); jjg@416: } jjg@416: return file.delete(); jjg@416: } jjg@416: jjg@416: File file(File dir, String... path) { jjg@416: File f = dir; jjg@416: for (String p: path) jjg@416: f = new File(f, p); jjg@416: return f; jjg@416: } jjg@416: jjg@416: /** jjg@416: * Report an error. jjg@416: */ jjg@416: void error(String msg, String... more) { jjg@416: System.err.println("test: " + testOptions + " " + testClassName); jjg@416: System.err.println("error: " + msg); jjg@416: for (String s: more) jjg@416: System.err.println(s); jjg@416: errors++; jjg@416: System.exit(1); jjg@416: } jjg@416: jjg@416: File old_javah_cmd; jjg@416: File rt_jar; jjg@416: List testOptions; jjg@416: String testClassName; jjg@416: int count; jjg@416: int errors; jjg@416: }