Fri, 24 Apr 2020 03:58:51 +0100
Merge
jjg@416 | 1 | /* |
ohair@554 | 2 | * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. |
jjg@416 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@416 | 4 | * |
jjg@416 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@416 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jjg@416 | 7 | * published by the Free Software Foundation. |
jjg@416 | 8 | * |
jjg@416 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@416 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@416 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@416 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@416 | 13 | * accompanied this code). |
jjg@416 | 14 | * |
jjg@416 | 15 | * You should have received a copy of the GNU General Public License version |
jjg@416 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@416 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@416 | 18 | * |
ohair@554 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@554 | 20 | * or visit www.oracle.com if you need additional information or have any |
ohair@554 | 21 | * questions. |
jjg@416 | 22 | */ |
jjg@416 | 23 | |
jjg@416 | 24 | import java.io.DataInputStream; |
jjg@416 | 25 | import java.io.File; |
jjg@416 | 26 | import java.io.IOException; |
jjg@416 | 27 | import java.io.InputStream; |
jjg@416 | 28 | import java.io.PrintWriter; |
jjg@416 | 29 | import java.io.StringWriter; |
jjg@416 | 30 | import java.util.ArrayList; |
jjg@416 | 31 | import java.util.Arrays; |
jjg@416 | 32 | import java.util.Enumeration; |
jjg@416 | 33 | import java.util.List; |
jjg@416 | 34 | import java.util.Set; |
jjg@416 | 35 | import java.util.TreeSet; |
jjg@416 | 36 | import java.util.jar.JarEntry; |
jjg@416 | 37 | import java.util.jar.JarFile; |
jjg@416 | 38 | |
jjg@416 | 39 | import com.sun.tools.classfile.AccessFlags; |
jjg@416 | 40 | import com.sun.tools.classfile.ClassFile; |
jjg@416 | 41 | import com.sun.tools.classfile.ConstantPoolException; |
jjg@416 | 42 | import com.sun.tools.classfile.Method; |
jjg@416 | 43 | import java.io.BufferedReader; |
jjg@416 | 44 | import java.io.FileInputStream; |
jjg@416 | 45 | import java.io.InputStreamReader; |
jjg@416 | 46 | import java.util.LinkedHashSet; |
jjg@416 | 47 | |
jjg@416 | 48 | public class CompareTest { |
jjg@416 | 49 | String[][] testCases = { |
jjg@416 | 50 | { }, |
jjg@416 | 51 | { "-jni" }, |
jjg@416 | 52 | // { "-llni" }, |
jjg@416 | 53 | }; |
jjg@416 | 54 | |
jjg@416 | 55 | public static void main(String... args) throws Exception { |
jjg@416 | 56 | new CompareTest().run(args); |
jjg@416 | 57 | } |
jjg@416 | 58 | |
jjg@416 | 59 | public void run(String... args) throws Exception { |
jjg@416 | 60 | old_javah_cmd = new File(args[0]); |
jjg@416 | 61 | rt_jar = new File(args[1]); |
jjg@416 | 62 | |
jjg@416 | 63 | Set<String> testClasses; |
jjg@416 | 64 | if (args.length > 2) { |
jjg@416 | 65 | testClasses = new LinkedHashSet<String>(Arrays.asList(Arrays.copyOfRange(args, 2, args.length))); |
jjg@416 | 66 | } else |
jjg@416 | 67 | testClasses = getNativeClasses(new JarFile(rt_jar)); |
jjg@416 | 68 | |
jjg@416 | 69 | for (String[] options: testCases) { |
jjg@416 | 70 | for (String name: testClasses) { |
jjg@416 | 71 | test(Arrays.asList(options), rt_jar, name); |
jjg@416 | 72 | } |
jjg@416 | 73 | } |
jjg@416 | 74 | |
jjg@416 | 75 | if (errors == 0) |
jjg@416 | 76 | System.out.println(count + " tests passed"); |
jjg@416 | 77 | else |
jjg@416 | 78 | throw new Exception(errors + "/" + count + " tests failed"); |
jjg@416 | 79 | } |
jjg@416 | 80 | |
jjg@416 | 81 | public void test(List<String> options, File bootclasspath, String className) |
jjg@416 | 82 | throws IOException, InterruptedException { |
jjg@416 | 83 | System.err.println("test: " + options + " " + className); |
jjg@416 | 84 | count++; |
jjg@416 | 85 | |
jjg@416 | 86 | testOptions = options; |
jjg@416 | 87 | testClassName = className; |
jjg@416 | 88 | |
jjg@416 | 89 | File oldOutDir = initDir(file(new File("old"), className)); |
jjg@416 | 90 | int old_rc = old_javah(options, oldOutDir, bootclasspath, className); |
jjg@416 | 91 | |
jjg@416 | 92 | File newOutDir = initDir(file(new File("new"), className)); |
jjg@416 | 93 | int new_rc = new_javah(options, newOutDir, bootclasspath, className); |
jjg@416 | 94 | |
jjg@416 | 95 | if (old_rc != new_rc) |
jjg@416 | 96 | error("return codes differ; old: " + old_rc + ", new: " + new_rc); |
jjg@416 | 97 | |
jjg@416 | 98 | compare(oldOutDir, newOutDir); |
jjg@416 | 99 | } |
jjg@416 | 100 | |
jjg@416 | 101 | int old_javah(List<String> options, File outDir, File bootclasspath, String className) |
jjg@416 | 102 | throws IOException, InterruptedException { |
jjg@416 | 103 | List<String> cmd = new ArrayList<String>(); |
jjg@416 | 104 | cmd.add(old_javah_cmd.getPath()); |
jjg@416 | 105 | cmd.addAll(options); |
jjg@416 | 106 | cmd.add("-d"); |
jjg@416 | 107 | cmd.add(outDir.getPath()); |
jjg@416 | 108 | cmd.add("-bootclasspath"); |
jjg@416 | 109 | cmd.add(bootclasspath.getPath()); |
jjg@416 | 110 | cmd.add(className); |
jjg@416 | 111 | System.err.println("old_javah: " + cmd); |
jjg@416 | 112 | ProcessBuilder pb = new ProcessBuilder(cmd); |
jjg@416 | 113 | pb.redirectErrorStream(true); |
jjg@416 | 114 | Process p = pb.start(); |
jjg@416 | 115 | BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); |
jjg@416 | 116 | String line; |
jjg@416 | 117 | StringBuilder sb = new StringBuilder(); |
jjg@416 | 118 | while ((line = in.readLine()) != null) { |
jjg@416 | 119 | sb.append(line); |
jjg@416 | 120 | sb.append("\n"); |
jjg@416 | 121 | } |
jjg@416 | 122 | System.err.println("old javah out: " + sb.toString()); |
jjg@416 | 123 | return p.waitFor(); |
jjg@416 | 124 | } |
jjg@416 | 125 | |
jjg@416 | 126 | int new_javah(List<String> options, File outDir, File bootclasspath, String className) { |
jjg@416 | 127 | List<String> args = new ArrayList<String>(); |
jjg@416 | 128 | args.addAll(options); |
jjg@416 | 129 | args.add("-d"); |
jjg@416 | 130 | args.add(outDir.getPath()); |
jjg@416 | 131 | args.add("-bootclasspath"); |
jjg@416 | 132 | args.add(bootclasspath.getPath()); |
jjg@416 | 133 | args.add(className); |
jjg@416 | 134 | StringWriter sw = new StringWriter(); |
jjg@416 | 135 | PrintWriter pw = new PrintWriter(sw); |
jjg@416 | 136 | int rc = com.sun.tools.javah.Main.run(args.toArray(new String[args.size()]), pw); |
jjg@416 | 137 | pw.close(); |
jjg@416 | 138 | System.err.println("new javah out: " + sw.toString()); |
jjg@416 | 139 | return rc; |
jjg@416 | 140 | } |
jjg@416 | 141 | |
jjg@416 | 142 | Set<String> getNativeClasses(JarFile jar) throws IOException, ConstantPoolException { |
jjg@416 | 143 | System.err.println("getNativeClasses: " + jar.getName()); |
jjg@416 | 144 | Set<String> results = new TreeSet<String>(); |
jjg@416 | 145 | Enumeration<JarEntry> e = jar.entries(); |
jjg@416 | 146 | while (e.hasMoreElements()) { |
jjg@416 | 147 | JarEntry je = e.nextElement(); |
jjg@416 | 148 | if (isNativeClass(jar, je)) { |
jjg@416 | 149 | String name = je.getName(); |
jjg@416 | 150 | results.add(name.substring(0, name.length() - 6).replace("/", ".")); |
jjg@416 | 151 | } |
jjg@416 | 152 | } |
jjg@416 | 153 | return results; |
jjg@416 | 154 | } |
jjg@416 | 155 | |
jjg@416 | 156 | boolean isNativeClass(JarFile jar, JarEntry entry) throws IOException, ConstantPoolException { |
jjg@416 | 157 | String name = entry.getName(); |
jjg@416 | 158 | if (name.startsWith("META-INF") || !name.endsWith(".class")) |
jjg@416 | 159 | return false; |
jjg@416 | 160 | //String className = name.substring(0, name.length() - 6).replace("/", "."); |
jjg@416 | 161 | //System.err.println("check " + className); |
jjg@416 | 162 | InputStream in = jar.getInputStream(entry); |
jjg@416 | 163 | ClassFile cf = ClassFile.read(in); |
jjg@416 | 164 | for (int i = 0; i < cf.methods.length; i++) { |
jjg@416 | 165 | Method m = cf.methods[i]; |
jjg@416 | 166 | if (m.access_flags.is(AccessFlags.ACC_NATIVE)) { |
jjg@416 | 167 | // System.err.println(className); |
jjg@416 | 168 | return true; |
jjg@416 | 169 | } |
jjg@416 | 170 | } |
jjg@416 | 171 | return false; |
jjg@416 | 172 | } |
jjg@416 | 173 | |
jjg@416 | 174 | void compare(File f1, File f2) throws IOException { |
jjg@416 | 175 | if (f1.isFile() && f2.isFile()) |
jjg@416 | 176 | compareFiles(f1, f2); |
jjg@416 | 177 | else if (f1.isDirectory() && f2.isDirectory()) |
jjg@416 | 178 | compareDirectories(f1, f2); |
jjg@416 | 179 | else |
jjg@416 | 180 | error("files differ: " |
jjg@416 | 181 | + f1 + " (" + getFileType(f1) + "), " |
jjg@416 | 182 | + f2 + " (" + getFileType(f2) + ")"); |
jjg@416 | 183 | } |
jjg@416 | 184 | |
jjg@416 | 185 | void compareDirectories(File d1, File d2) throws IOException { |
jjg@416 | 186 | Set<String> list = new TreeSet<String>(); |
jjg@416 | 187 | list.addAll(Arrays.asList(d1.list())); |
jjg@416 | 188 | list.addAll(Arrays.asList(d2.list())); |
jjg@416 | 189 | for (String c: list) |
jjg@416 | 190 | compare(new File(d1, c), new File(d2, c)); |
jjg@416 | 191 | } |
jjg@416 | 192 | |
jjg@416 | 193 | void compareFiles(File f1, File f2) throws IOException { |
jjg@416 | 194 | byte[] c1 = readFile(f1); |
jjg@416 | 195 | byte[] c2 = readFile(f2); |
jjg@416 | 196 | if (!Arrays.equals(c1, c2)) |
jjg@416 | 197 | error("files differ: " + f1 + ", " + f2); |
jjg@416 | 198 | } |
jjg@416 | 199 | |
jjg@416 | 200 | byte[] readFile(File file) throws IOException { |
jjg@416 | 201 | int size = (int) file.length(); |
jjg@416 | 202 | byte[] data = new byte[size]; |
jjg@416 | 203 | DataInputStream in = new DataInputStream(new FileInputStream(file)); |
jjg@416 | 204 | try { |
jjg@416 | 205 | in.readFully(data); |
jjg@416 | 206 | } finally { |
jjg@416 | 207 | in.close(); |
jjg@416 | 208 | } |
jjg@416 | 209 | return data; |
jjg@416 | 210 | } |
jjg@416 | 211 | |
jjg@416 | 212 | String getFileType(File f) { |
jjg@416 | 213 | return f.isDirectory() ? "directory" |
jjg@416 | 214 | : f.isFile() ? "file" |
jjg@416 | 215 | : f.exists() ? "other" |
jjg@416 | 216 | : "not found"; |
jjg@416 | 217 | } |
jjg@416 | 218 | |
jjg@416 | 219 | /** |
jjg@416 | 220 | * Set up an empty directory. |
jjg@416 | 221 | */ |
jjg@416 | 222 | public File initDir(File dir) { |
jjg@416 | 223 | if (dir.exists()) |
jjg@416 | 224 | deleteAll(dir); |
jjg@416 | 225 | dir.mkdirs(); |
jjg@416 | 226 | return dir; |
jjg@416 | 227 | } |
jjg@416 | 228 | |
jjg@416 | 229 | /** |
jjg@416 | 230 | * Delete a file or a directory (including all its contents). |
jjg@416 | 231 | */ |
jjg@416 | 232 | boolean deleteAll(File file) { |
jjg@416 | 233 | if (file.isDirectory()) { |
jjg@416 | 234 | for (File f: file.listFiles()) |
jjg@416 | 235 | deleteAll(f); |
jjg@416 | 236 | } |
jjg@416 | 237 | return file.delete(); |
jjg@416 | 238 | } |
jjg@416 | 239 | |
jjg@416 | 240 | File file(File dir, String... path) { |
jjg@416 | 241 | File f = dir; |
jjg@416 | 242 | for (String p: path) |
jjg@416 | 243 | f = new File(f, p); |
jjg@416 | 244 | return f; |
jjg@416 | 245 | } |
jjg@416 | 246 | |
jjg@416 | 247 | /** |
jjg@416 | 248 | * Report an error. |
jjg@416 | 249 | */ |
jjg@416 | 250 | void error(String msg, String... more) { |
jjg@416 | 251 | System.err.println("test: " + testOptions + " " + testClassName); |
jjg@416 | 252 | System.err.println("error: " + msg); |
jjg@416 | 253 | for (String s: more) |
jjg@416 | 254 | System.err.println(s); |
jjg@416 | 255 | errors++; |
jjg@416 | 256 | System.exit(1); |
jjg@416 | 257 | } |
jjg@416 | 258 | |
jjg@416 | 259 | File old_javah_cmd; |
jjg@416 | 260 | File rt_jar; |
jjg@416 | 261 | List<String> testOptions; |
jjg@416 | 262 | String testClassName; |
jjg@416 | 263 | int count; |
jjg@416 | 264 | int errors; |
jjg@416 | 265 | } |