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