1 /* 2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 7150368 8003412 8000407 27 * @summary javac should include basic ability to generate native headers 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.file 30 */ 31 32 import java.io.File; 33 import java.io.FileWriter; 34 import java.io.IOException; 35 import java.lang.annotation.Annotation; 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.lang.reflect.InvocationTargetException; 39 import java.lang.reflect.Method; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Set; 45 46 import javax.tools.StandardJavaFileManager; 47 import javax.tools.StandardLocation; 48 49 import com.sun.source.util.JavacTask; 50 import com.sun.tools.javac.api.JavacTool; 51 52 public class NativeHeaderTest { 53 public static void main(String... args) throws Exception { 54 new NativeHeaderTest().run(); 55 } 56 57 /** How to invoke javac. */ 58 enum RunKind { 59 /** Use the command line entry point. */ 60 CMD, 61 /** Use the JavaCompiler API. */ 62 API 63 }; 64 65 /** Which classes for which to generate headers. */ 66 enum GenKind { 67 /** Just classes with native methods or the marker annotation. */ 68 SIMPLE, 69 /** All appropriate classes within the top level class. */ 70 FULL 71 }; 72 73 // ---------- Test cases, invoked reflectively via run. ---------- 74 75 @Test 76 void simpleTest(RunKind rk, GenKind gk) throws Exception { 77 List<File> files = new ArrayList<File>(); 78 files.add(createFile("p/C.java", 79 "class C { native void m(); }")); 80 81 Set<String> expect = createSet("C.h"); 82 83 test(rk, gk, files, expect); 84 } 85 86 @Test 87 void nestedClassTest(RunKind rk, GenKind gk) throws Exception { 88 List<File> files = new ArrayList<File>(); 89 files.add(createFile("p/C.java", 90 "class C { static class Inner { native void m(); } }")); 91 92 Set<String> expect = createSet("C_Inner.h"); 93 if (gk == GenKind.FULL) expect.add("C.h"); 94 95 test(rk, gk, files, expect); 96 } 97 98 @Test 99 void localClassTest(RunKind rk, GenKind gk) throws Exception { 100 List<File> files = new ArrayList<File>(); 101 files.add(createFile("p/C.java", 102 "class C { native void m(); void m2() { class Local { } } }")); 103 104 Set<String> expect = createSet("C.h"); 105 106 test(rk, gk, files, expect); 107 } 108 109 @Test 110 void syntheticClassTest(RunKind rk, GenKind gk) throws Exception { 111 List<File> files = new ArrayList<File>(); 112 files.add(createFile("p/C.java", 113 "class C {\n" 114 + " private C() { }\n" 115 + " class Inner extends C { native void m(); }\n" 116 + "}")); 117 118 Set<String> expect = createSet("C_Inner.h"); 119 if (gk == GenKind.FULL) expect.add("C.h"); 120 121 test(rk, gk, files, expect); 122 123 // double check the synthetic class was generated 124 checkEqual("generatedClasses", 125 createSet("C.class", "C$Inner.class"), 126 createSet(classesDir.list())); 127 } 128 129 @Test 130 void annoTest(RunKind rk, GenKind gk) throws Exception { 131 List<File> files = new ArrayList<File>(); 132 files.add(createFile("p/C.java", 133 "class C { @java.lang.annotation.Native public static final int i = 1907; }")); 134 135 Set<String> expect = createSet("C.h"); 136 137 test(rk, gk, files, expect); 138 } 139 140 @Test 141 void annoNestedClassTest(RunKind rk, GenKind gk) throws Exception { 142 List<File> files = new ArrayList<File>(); 143 files.add(createFile("p/C.java", 144 "class C { class Inner { @java.lang.annotation.Native public static final int i = 1907; } }")); 145 146 Set<String> expect = createSet("C_Inner.h"); 147 if (gk == GenKind.FULL) expect.add("C.h"); 148 149 test(rk, gk, files, expect); 150 } 151 152 /** 153 * The worker method for each test case. 154 * Compile the files and verify that exactly the expected set of header files 155 * is generated. 156 */ 157 void test(RunKind rk, GenKind gk, List<File> files, Set<String> expect) throws Exception { 158 List<String> args = new ArrayList<String>(); 159 if (gk == GenKind.FULL) 160 args.add("-XDjavah:full"); 161 162 switch (rk) { 163 case CMD: 164 args.add("-d"); 165 args.add(classesDir.getPath()); 166 args.add("-h"); 167 args.add(headersDir.getPath()); 168 for (File f: files) 169 args.add(f.getPath()); 170 int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()])); 171 if (rc != 0) 172 throw new Exception("compilation failed, rc=" + rc); 173 break; 174 175 case API: 176 fm.setLocation(StandardLocation.SOURCE_PATH, Arrays.asList(srcDir)); 177 fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(classesDir)); 178 fm.setLocation(StandardLocation.NATIVE_HEADER_OUTPUT, Arrays.asList(headersDir)); 179 JavacTask task = javac.getTask(null, fm, null, args, null, 180 fm.getJavaFileObjectsFromFiles(files)); 181 if (!task.call()) 182 throw new Exception("compilation failed"); 183 break; 184 } 185 186 Set<String> found = createSet(headersDir.list()); 187 checkEqual("header files", expect, found); 188 } 189 190 /** Marker annotation for test cases. */ 191 @Retention(RetentionPolicy.RUNTIME) 192 @interface Test { } 193 194 /** Combo test to run all test cases in all modes. */ 195 void run() throws Exception { 196 javac = JavacTool.create(); 197 fm = javac.getStandardFileManager(null, null, null); 198 try { 199 for (RunKind rk: RunKind.values()) { 200 for (GenKind gk: GenKind.values()) { 201 for (Method m: getClass().getDeclaredMethods()) { 202 Annotation a = m.getAnnotation(Test.class); 203 if (a != null) { 204 init(rk, gk, m.getName()); 205 try { 206 m.invoke(this, new Object[] { rk, gk }); 207 } catch (InvocationTargetException e) { 208 Throwable cause = e.getCause(); 209 throw (cause instanceof Exception) ? ((Exception) cause) : e; 210 } 211 System.err.println(); 212 } 213 } 214 } 215 } 216 System.err.println(testCount + " tests" + ((errorCount == 0) ? "" : ", " + errorCount + " errors")); 217 if (errorCount > 0) 218 throw new Exception(errorCount + " errors found"); 219 } finally { 220 fm.close(); 221 } 222 } 223 224 /** 225 * Init directories for a test case. 226 */ 227 void init(RunKind rk, GenKind gk, String name) throws IOException { 228 System.err.println("Test " + rk + " " + gk + " " + name); 229 testCount++; 230 231 testDir = new File(rk.toString().toLowerCase() + "_" + gk.toString().toLowerCase() + "-" + name); 232 srcDir = new File(testDir, "src"); 233 srcDir.mkdirs(); 234 classesDir = new File(testDir, "classes"); 235 classesDir.mkdirs(); 236 headersDir = new File(testDir, "headers"); 237 headersDir.mkdirs(); 238 } 239 240 /** Create a source file with given body text. */ 241 File createFile(String path, final String body) throws IOException { 242 File f = new File(srcDir, path); 243 f.getParentFile().mkdirs(); 244 try (FileWriter out = new FileWriter(f)) { 245 out.write(body); 246 } 247 return f; 248 } 249 250 /** Convenience method to create a set of items. */ 251 <T> Set<T> createSet(T... items) { 252 return new HashSet<T>(Arrays.asList(items)); 253 } 254 255 /** Convenience method to check two values are equal, and report an error if not. */ 256 <T> void checkEqual(String label, T expect, T found) { 257 if ((found == null) ? (expect == null) : found.equals(expect)) 258 return; 259 System.err.println("Error: mismatch"); 260 System.err.println(" expected: " + expect); 261 System.err.println(" found: " + found); 262 errorCount++; 263 } 264 265 // Shared across API test cases 266 JavacTool javac; 267 StandardJavaFileManager fm; 268 269 // Directories set up by init 270 File testDir; 271 File srcDir; 272 File classesDir; 273 File headersDir; 274 275 // Statistics 276 int testCount; 277 int errorCount; 278 } 279