1 /* 2 * Copyright (c) 2019, 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 import java.foreign.annotations.NativeAddressof; 25 import java.foreign.annotations.NativeGetter; 26 import java.foreign.annotations.NativeHeader; 27 import java.foreign.annotations.NativeLocation; 28 import java.foreign.annotations.NativeSetter; 29 import java.foreign.annotations.NativeStruct; 30 import java.foreign.memory.Pointer; 31 import java.io.File; 32 import java.io.IOException; 33 import java.lang.reflect.Method; 34 import java.lang.reflect.Modifier; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.util.Arrays; 39 import java.util.ArrayList; 40 import java.util.spi.ToolProvider; 41 import org.testng.annotations.Test; 42 import static org.testng.Assert.assertTrue; 43 44 /* 45 * @test 46 * @bug 8221154 8221228 8221336 8221419 8221443 47 * @summary jextract should generate java source files 48 * @library .. 49 * @run testng SrcGenTest 50 */ 51 public class SrcGenTest extends JextractToolRunner { 52 private static final ToolProvider JEXTRACT = ToolProvider.findFirst("jextract") 53 .orElseThrow(() -> 54 new RuntimeException("jextract tool not found") 55 ); 56 57 private static final ToolProvider JAVAC = ToolProvider.findFirst("javac") 58 .orElseThrow(() -> 59 new RuntimeException("javac tool not found") 60 ); 61 62 @Test 63 public void test() throws IOException { 64 Path inputDir = Paths.get(System.getProperty("test.src", ".")); 65 Path outputDir = Paths.get(System.getProperty("test.classes", ".")); 66 inputDir = inputDir.toAbsolutePath(); 67 outputDir = outputDir.toAbsolutePath(); 68 String pkgName = "test8221154"; 69 Path jarPath = outputDir.resolve(pkgName + ".jar"); 70 71 // run jextract with --src-dump-dir option 72 ArrayList<String> jextrOpts = new ArrayList<>(); 73 jextrOpts.add("-C-nostdinc"); 74 jextrOpts.add("-I"); 75 jextrOpts.add(inputDir.toString()); 76 jextrOpts.add("-o"); 77 jextrOpts.add(jarPath.toString()); 78 jextrOpts.add("--src-dump-dir"); 79 jextrOpts.add(outputDir.toString()); 80 jextrOpts.add("-t"); 81 jextrOpts.add(pkgName); 82 jextrOpts.add("-l"); 83 jextrOpts.add("srcgentest"); 84 jextrOpts.add(inputDir + File.separator + "srcgentest.h"); 85 86 int result = JEXTRACT.run(System.out, System.err, jextrOpts.toArray(String[]::new)); 87 if (result != 0) { 88 throw new RuntimeException(JEXTRACT.name() + " returns non-zero value"); 89 } 90 91 // delete .jar file generated by jextract 92 Files.delete(jarPath); 93 94 Path pkgDir = outputDir.resolve(pkgName); 95 96 String srcgentestIfaceName = headerInterfaceName("srcgentest.h"); 97 String srcgentestForwarderName = staticForwarderName("srcgentest.h"); 98 String dupnameIfaceName = headerInterfaceName("dupname.h"); 99 String dupnameDupnameNotName = structInterfaceName("dupname.h", "dupnameNot"); 100 String dupnameForwarderName = staticForwarderName("dupname.h"); 101 String srcgentestPointName = structInterfaceName("srcgentest.h", "Point"); 102 String srcgentestColorName = enumInterfaceName("srcgentest.h", "Color"); 103 String srcgentestForwarderEnumName = enumForwarderInterfaceName("srcgentest.h", "Color"); 104 105 // compile jextract generated java sources 106 ArrayList<String> javacOpts = new ArrayList<>(); 107 javacOpts.add("-d"); 108 javacOpts.add(outputDir.toString()); 109 javacOpts.add(pkgDir.resolve(srcgentestIfaceName + ".java").toString()); 110 javacOpts.add(pkgDir.resolve(srcgentestForwarderName + ".java").toString()); 111 javacOpts.add(pkgDir.resolve("sub").resolve(dupnameIfaceName + ".java").toString()); 112 javacOpts.add(pkgDir.resolve("sub").resolve(dupnameForwarderName + ".java").toString()); 113 result = JAVAC.run(System.out, System.err, javacOpts.toArray(String[]::new)); 114 if (result != 0) { 115 throw new RuntimeException(JAVAC.name() + " returns non-zero value"); 116 } 117 118 // sanity checks for .class file existence 119 assertTrue(Files.isRegularFile(pkgDir.resolve(srcgentestIfaceName + ".class"))); 120 assertTrue(Files.isRegularFile(pkgDir.resolve(srcgentestPointName + ".class"))); 121 assertTrue(Files.isRegularFile(pkgDir.resolve(srcgentestColorName + ".class"))); 122 assertTrue(Files.isRegularFile(pkgDir.resolve(srcgentestForwarderName + ".class"))); 123 assertTrue(Files.isRegularFile(pkgDir.resolve(srcgentestForwarderEnumName + ".class"))); 124 assertTrue(Files.isRegularFile(pkgDir.resolve("sub").resolve(dupnameForwarderName + ".class"))); 125 assertTrue(Files.isRegularFile(pkgDir.resolve("sub").resolve(dupnameIfaceName + ".class"))); 126 assertTrue(Files.isRegularFile(pkgDir.resolve("sub").resolve(dupnameDupnameNotName + ".class"))); 127 128 checkClasses(outputDir, pkgName); 129 } 130 131 private void checkClasses(Path outputDir, String pkgName) { 132 Loader loader = classLoader(outputDir); 133 checkSubdirClass(loader, pkgName); 134 checkHeaderClass(loader, pkgName); 135 checkForwarderClass(loader, pkgName); 136 } 137 138 private void checkSubdirClass(Loader loader, String pkgName) { 139 Class<?> dupname = loader.loadClass(pkgName + ".sub." + headerInterfaceName("dupname.h")); 140 assertTrue(dupname != null); 141 } 142 143 private void checkHeaderClass(Loader loader, String pkgName) { 144 Class<?> headerCls = loader.loadClass(pkgName + "." + headerInterfaceName("srcgentest.h")); 145 assertTrue(headerCls != null); 146 assertTrue(headerCls.getAnnotation(NativeHeader.class) != null); 147 148 // global 'num' getter, pointer getter 149 Method numGetter = findGlobalVariableGet(headerCls, "num"); 150 assertTrue(numGetter != null); 151 assertTrue(numGetter.getAnnotation(NativeGetter.class) != null); 152 assertTrue(numGetter.getAnnotation(NativeLocation.class) != null); 153 154 Method numPtrGetter = findGlobalVariablePointerGet(headerCls, "num"); 155 assertTrue(numPtrGetter != null); 156 assertTrue(numPtrGetter.getAnnotation(NativeAddressof.class) != null); 157 158 // global 'num' setter 159 Method numSetter = findGlobalVariableSet(headerCls, "num", int.class); 160 assertTrue(numSetter != null); 161 assertTrue(numSetter.getAnnotation(NativeSetter.class) != null); 162 163 // check "x_coord" method 164 Method xCoordMethod = findFirstMethod(headerCls, "x_coord"); 165 assertTrue(xCoordMethod.getReturnType() == int.class); 166 Class<?>[] xCoordParamTypes = xCoordMethod.getParameterTypes(); 167 assertTrue(xCoordParamTypes.length == 1); 168 assertTrue(xCoordParamTypes[0] == Pointer.class); 169 assertTrue(xCoordMethod.getAnnotation(NativeLocation.class) != null); 170 assertTrue(!Modifier.isStatic(xCoordMethod.getModifiers())); 171 172 // check "y_coord" method 173 Method yCoordMethod = findFirstMethod(headerCls, "y_coord"); 174 assertTrue(yCoordMethod.getReturnType() == int.class); 175 Class<?>[] yCoordParamTypes = yCoordMethod.getParameterTypes(); 176 assertTrue(yCoordParamTypes.length == 1); 177 assertTrue(yCoordParamTypes[0] == Pointer.class); 178 assertTrue(yCoordMethod.getAnnotation(NativeLocation.class) != null); 179 assertTrue(!Modifier.isStatic(yCoordMethod.getModifiers())); 180 181 // check "sum" method 182 Method sumMethod = findFirstMethod(headerCls, "sum"); 183 assertTrue(sumMethod.getReturnType() == int.class); 184 Class<?>[] sumParamTypes = sumMethod.getParameterTypes(); 185 assertTrue(sumParamTypes.length == 2); 186 assertTrue(sumParamTypes[0] == int.class); 187 assertTrue(sumParamTypes[1] == Object[].class); 188 assertTrue(sumMethod.getAnnotation(NativeLocation.class) != null); 189 assertTrue(!Modifier.isStatic(sumMethod.getModifiers())); 190 191 // struct Point 192 Class<?> pointCls = Arrays.stream(headerCls.getClasses()) 193 .filter(c -> c.getSimpleName().equals("Point")).findFirst().get(); 194 assertTrue(Modifier.isInterface(pointCls.getModifiers())); 195 assertTrue(pointCls.getAnnotation(NativeStruct.class) != null); 196 assertTrue(pointCls.getAnnotation(NativeLocation.class) != null); 197 198 // Point extends Struct 199 Class<?> pointSuper = pointCls.getInterfaces()[0]; 200 assertTrue(pointSuper.getName().equals("java.foreign.memory.Struct")); 201 202 // x, y getters, pointer getters 203 Method xGetter = findStructFieldGet(pointCls, "x"); 204 assertTrue(xGetter != null); 205 assertTrue(xGetter.getAnnotation(NativeGetter.class) != null); 206 assertTrue(xGetter.getAnnotation(NativeLocation.class) != null); 207 208 Method yGetter = findStructFieldGet(pointCls, "y"); 209 assertTrue(yGetter != null); 210 assertTrue(yGetter.getAnnotation(NativeGetter.class) != null); 211 assertTrue(yGetter.getAnnotation(NativeLocation.class) != null); 212 213 Method xPtrGetter = findStructFieldPointerGet(pointCls, "x"); 214 assertTrue(xPtrGetter != null); 215 assertTrue(xPtrGetter.getAnnotation(NativeAddressof.class) != null); 216 217 Method yPtrGetter = findStructFieldPointerGet(pointCls, "y"); 218 assertTrue(yPtrGetter != null); 219 assertTrue(yPtrGetter.getAnnotation(NativeAddressof.class) != null); 220 221 // x, y setters 222 Method xSetter = findStructFieldSet(pointCls, "x", int.class); 223 assertTrue(xSetter != null); 224 assertTrue(xSetter.getAnnotation(NativeSetter.class) != null); 225 226 Method ySetter = findStructFieldSet(pointCls, "y", int.class); 227 assertTrue(ySetter != null); 228 assertTrue(ySetter.getAnnotation(NativeSetter.class) != null); 229 } 230 231 private void checkForwarderClass(Loader loader, String pkgName) { 232 Class<?> forwarderCls = loader.loadClass(pkgName + "." + staticForwarderName("srcgentest.h")); 233 assertTrue(forwarderCls != null); 234 235 // check "sum" method 236 Method sumMethod = findFirstMethod(forwarderCls, "sum"); 237 assertTrue(sumMethod.getReturnType() == int.class); 238 Class<?>[] sumParamTypes = sumMethod.getParameterTypes(); 239 assertTrue(sumParamTypes.length == 2); 240 assertTrue(sumParamTypes[0] == int.class); 241 assertTrue(sumParamTypes[1] == Object[].class); 242 assertTrue(Modifier.isStatic(sumMethod.getModifiers())); 243 244 // check "x_coord" method 245 Method xCoordMethod = findFirstMethod(forwarderCls, "x_coord"); 246 assertTrue(xCoordMethod.getReturnType() == int.class); 247 Class<?>[] xCoordParamTypes = xCoordMethod.getParameterTypes(); 248 assertTrue(xCoordParamTypes.length == 1); 249 assertTrue(xCoordParamTypes[0] == Pointer.class); 250 assertTrue(Modifier.isStatic(xCoordMethod.getModifiers())); 251 252 // check "y_coord" method 253 Method yCoordMethod = findFirstMethod(forwarderCls, "y_coord"); 254 assertTrue(yCoordMethod.getReturnType() == int.class); 255 Class<?>[] yCoordParamTypes = yCoordMethod.getParameterTypes(); 256 assertTrue(yCoordParamTypes.length == 1); 257 assertTrue(yCoordParamTypes[0] == Pointer.class); 258 assertTrue(Modifier.isStatic(xCoordMethod.getModifiers())); 259 260 // global variable "num" getter 261 Method numGet = findGlobalVariableGet(forwarderCls, "num"); 262 assertTrue(numGet != null); 263 264 // anonymous enum fields 265 assertTrue(findField(forwarderCls, "R") != null); 266 assertTrue(findField(forwarderCls, "G") != null); 267 assertTrue(findField(forwarderCls, "B") != null); 268 269 // enum interface class 270 Class<?> colorCls = Arrays.stream(forwarderCls.getClasses()) 271 .filter(c -> c.getSimpleName().equals("Color")).findFirst().get(); 272 assertTrue(Modifier.isInterface(colorCls.getModifiers())); 273 checkIntField(colorCls, "RED", 0); 274 checkIntField(colorCls, "GREEN", 1); 275 checkIntField(colorCls, "BLUE", 2); 276 } 277 }