1 /* 2 * Copyright (c) 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 import java.foreign.NativeTypes; 25 import java.foreign.annotations.NativeHeader; 26 import java.foreign.annotations.NativeStruct; 27 import java.foreign.layout.Function; 28 import java.foreign.layout.Group; 29 import java.foreign.layout.Layout; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.ParameterizedType; 32 import java.lang.reflect.Type; 33 import java.lang.reflect.WildcardType; 34 import java.nio.file.Path; 35 import java.util.List; 36 import java.util.Map; 37 import jdk.internal.foreign.LayoutResolver; 38 import jdk.internal.foreign.memory.DescriptorParser; 39 import org.testng.annotations.Test; 40 41 import static org.testng.Assert.assertEquals; 42 import static org.testng.Assert.assertFalse; 43 import static org.testng.Assert.assertNotNull; 44 import static org.testng.Assert.assertTrue; 45 46 /* 47 * @test 48 * @modules jdk.jextract java.base/jdk.internal.foreign.memory java.base/jdk.internal.foreign 49 * @library .. 50 * @build JextractToolRunner 51 * @run testng StructTest 52 */ 53 public class StructTest extends JextractToolRunner { 54 private static final String[] ExpectedIfs = { 55 "TypedefNamedAsIs", 56 "TypedefNamedDifferent", 57 "TypedefAnonymous", 58 "Plain", 59 "IncompleteArray", 60 "UndefinedStruct", 61 "UndefinedStructForPointer", 62 "Opaque" 63 }; 64 65 private static final String[] ExpectedFIs = { 66 "FunctionPointer", 67 "FI1", 68 // FIXME: FunctionPointer should only be generated once wo anonymous FI2 69 "FI2" 70 }; 71 72 private static final String[] ExpectedTypeAnnotations = { 73 "UndefinedStructPointer", 74 "TypedefNamedDifferent_t" 75 }; 76 77 private static final String[] ExpectedAnonymousRecord = { 78 "TypedefAnonymous__anonymous_struct", 79 }; 80 81 private static final int NumberOfInnerClasses = ExpectedIfs.length + 82 ExpectedFIs.length + ExpectedTypeAnnotations.length + 83 ExpectedAnonymousRecord.length; 84 85 private void verifyPlain(Class<?> plain) { 86 assertNotNull(plain); 87 checkMethod(plain, "x$get", int.class); 88 checkMethod(plain, "x$set", void.class, int.class); 89 checkMethod(plain, "x$ptr", java.foreign.memory.Pointer.class); 90 checkMethod(plain, "y$get", int.class); 91 checkMethod(plain, "y$set", void.class, int.class); 92 checkMethod(plain, "y$ptr", java.foreign.memory.Pointer.class); 93 } 94 95 private void verifyTypedefNamedAsIs(Class<?> asIs) { 96 assertNotNull(asIs); 97 } 98 99 private void verifyExpectedAnnotations(Class<?>[] declared) { 100 for (String name : ExpectedTypeAnnotations) { 101 Class<?> cls = findClass(declared, name); 102 assertNotNull(cls); 103 assertTrue(cls.isAnnotation()); 104 } 105 } 106 107 private void assertVoidPointer(ParameterizedType pvoid, boolean isWildcard) { 108 assertEquals(pvoid.getRawType(), java.foreign.memory.Pointer.class); 109 Type[] tas = pvoid.getActualTypeArguments(); 110 assertEquals(tas.length, 1); 111 if (isWildcard) { 112 WildcardType wt = (WildcardType) tas[0]; 113 assertEquals(wt.getLowerBounds().length, 0); 114 assertEquals(wt.getUpperBounds().length, 1); 115 assertEquals(wt.getUpperBounds()[0], Object.class); 116 } else { 117 assertEquals(tas[0], Void.class); 118 } 119 } 120 121 private void assertPointerType(ParameterizedType ptr, Class<?> pointee) { 122 assertEquals(ptr.getRawType(), java.foreign.memory.Pointer.class); 123 Type[] tas = ptr.getActualTypeArguments(); 124 assertEquals(tas.length, 1); 125 assertEquals(tas[0], pointee); 126 } 127 128 private void verifyIncompleteArray(Class<?> incomplete) { 129 assertNotNull(incomplete); 130 Method f = findMethod(incomplete, "ptr$get"); 131 ParameterizedType pVoid = (ParameterizedType) f.getGenericReturnType(); 132 assertVoidPointer(pVoid, false); 133 f = findMethod(incomplete, "ptr$set", java.foreign.memory.Pointer.class); 134 // Setter for void* should be wildcard 135 ParameterizedType pWildcard = (ParameterizedType) f.getGenericParameterTypes()[0]; 136 assertVoidPointer(pWildcard, true); 137 138 f = findMethod(incomplete, "junk$get"); 139 ParameterizedType ppVoid = (ParameterizedType) f.getGenericReturnType(); 140 assertEquals(ppVoid.getActualTypeArguments()[0], pVoid); 141 f = findMethod(incomplete, "junk$set", java.foreign.memory.Pointer.class); 142 143 ppVoid = (ParameterizedType) f.getGenericParameterTypes()[0]; 144 assertTrue(ppVoid.getActualTypeArguments()[0] instanceof WildcardType); 145 assertEquals(((WildcardType)ppVoid.getActualTypeArguments()[0]).getUpperBounds()[0], pWildcard); 146 } 147 148 private void verifyFunctionWithVoidPointer(Class<?> cls) { 149 Method m = findMethod(cls, "FunctionWithVoidPointer", 150 java.foreign.memory.Pointer.class, 151 java.foreign.memory.Pointer.class); 152 assertNotNull(m); 153 154 ParameterizedType pVoid = (ParameterizedType) m.getGenericReturnType(); 155 assertVoidPointer(pVoid, false); 156 157 Type[] args = m.getGenericParameterTypes(); 158 assertEquals(args.length, 2); 159 160 ParameterizedType pWildcard = (ParameterizedType) args[0]; 161 assertVoidPointer(pWildcard, true); 162 163 ParameterizedType ppVoid = (ParameterizedType) args[1]; 164 assertTrue(ppVoid.getActualTypeArguments()[0] instanceof WildcardType); 165 assertEquals(((WildcardType)ppVoid.getActualTypeArguments()[0]).getUpperBounds()[0], pWildcard); 166 } 167 168 private void verifyFunctionPointer(Class<?> cls) { 169 Method m = findMethod(cls, "fn", 170 java.foreign.memory.Pointer.class, 171 java.foreign.memory.Pointer.class); 172 assertNotNull(m); 173 174 ParameterizedType pVoid = (ParameterizedType) m.getGenericReturnType(); 175 assertVoidPointer(pVoid, false); 176 177 Type[] args = m.getGenericParameterTypes(); 178 assertEquals(args.length, 2); 179 180 ParameterizedType pWildcard = (ParameterizedType) args[0]; 181 assertVoidPointer(pWildcard, true); 182 183 ParameterizedType ppVoid = (ParameterizedType) args[1]; 184 assertTrue(ppVoid.getActualTypeArguments()[0] instanceof WildcardType); 185 assertEquals(((WildcardType)ppVoid.getActualTypeArguments()[0]).getUpperBounds()[0], pWildcard); 186 } 187 188 private void verifyUndefinedStructFunctions(Class<?> header) { 189 final String POINTEE = "UndefinedStructForPointer"; 190 NativeHeader nh = header.getAnnotation(NativeHeader.class); 191 Map<String, Object> map = DescriptorParser.parseHeaderDeclarations(nh.declarations()); 192 193 Method m = findMethod(header, "getParent", java.foreign.memory.Pointer.class); 194 assertNotNull(m); 195 ParameterizedType pReturn = (ParameterizedType) m.getGenericReturnType(); 196 Class<?> undefined = findClass(header.getDeclaredClasses(), POINTEE); 197 assertNotNull(undefined); 198 assertPointerType(pReturn, undefined); 199 Type[] args = m.getGenericParameterTypes(); 200 assertEquals(args.length, 1); 201 ParameterizedType pArg = (ParameterizedType) args[0]; 202 assertPointerType(pArg, undefined); 203 204 String expected = "(u64:$(" + POINTEE + "))u64:$(" + POINTEE + ")"; 205 Function fn = (Function) map.get("getParent"); 206 assertEquals(fn.toString(), expected); 207 fn = (Function) map.get("getSibling"); 208 assertEquals(fn.toString(), expected); 209 fn = (Function) map.get("getFirstChild"); 210 assertEquals(fn.toString(), expected); 211 } 212 213 private void verifyTypedefAnonymous(Class<?> cls) { 214 NativeStruct ns = cls.getAnnotation(NativeStruct.class); 215 216 Layout layout = Layout.of(ns.value()); 217 assertTrue(layout.isPartial()); 218 System.out.println("Unresolved layout = " + layout.toString()); 219 220 LayoutResolver resolver = LayoutResolver.get(cls); 221 layout = resolver.resolve(layout); 222 assertFalse(layout.isPartial()); 223 224 assertTrue(layout instanceof Group); 225 Group g = (Group) layout; 226 System.out.println("Resolved layout = " + g.toString()); 227 assertEquals(g.bitsSize(), 8 * 4 * NativeTypes.INT.bytesSize()); 228 } 229 230 private void verifyGetAnonymous(Class<?> header) { 231 NativeHeader nh = header.getAnnotation(NativeHeader.class); 232 Map<String, Object> map = DescriptorParser.parseHeaderDeclarations(nh.declarations()); 233 Method m = findFirstMethod(header, "getAnonymous"); 234 assertNotNull(m); 235 236 LayoutResolver resolver = LayoutResolver.get(header); 237 Function fn = (Function) map.get("getAnonymous"); 238 assertTrue(fn.isPartial()); 239 System.out.println("Function unresolved = " + fn.toString()); 240 fn = resolver.resolve(fn); 241 System.out.println("Function resolved as = " + fn.toString()); 242 assertFalse(fn.isPartial()); 243 244 List<Layout> args = fn.argumentLayouts(); 245 Group g = (Group) args.get(0); 246 assertFalse(g.isPartial()); 247 assertEquals(g.bitsSize(), NativeTypes.VOID.pointer().bytesSize() * 8); 248 } 249 250 private void verifyClass(Class<?> cls) { 251 Class<?>[] clz = cls.getDeclaredClasses(); 252 assertEquals(clz.length, NumberOfInnerClasses); 253 verifyPlain(findClass(clz, "Plain")); 254 verifyTypedefNamedAsIs(findClass(clz, "TypedefNamedAsIs")); 255 verifyExpectedAnnotations(clz); 256 verifyFunctionWithVoidPointer(cls); 257 verifyFunctionPointer(findClass(clz,"FI2")); //Todo: is this what jextract needs to emit? 258 verifyIncompleteArray(findClass(clz, "IncompleteArray")); 259 verifyTypedefAnonymous(findClass(clz, "TypedefAnonymous")); 260 checkMethod(cls, "voidArguments", void.class); 261 verifyUndefinedStructFunctions(cls); 262 verifyGetAnonymous(cls); 263 } 264 265 private void verifyAsCpp(Class<?> cls) { 266 verifyClass(cls); 267 checkMethod(cls, "emptyArguments", void.class); 268 } 269 270 private void verifyAsC(Class<?> cls) { 271 verifyClass(cls); 272 checkMethod(cls, "emptyArguments", void.class, Object[].class); 273 } 274 275 @Test 276 public void testCppMode() { 277 Path clzPath = getOutputFilePath("StructTest.cpp.jar"); 278 checkSuccess(null, "-C", "-x", "-C", "c++", 279 "-o", clzPath.toString(), 280 getInputFilePath("struct.h").toString()); 281 Class<?> cls = loadClass("struct", clzPath); 282 verifyAsCpp(cls); 283 deleteFile(clzPath); 284 } 285 286 @Test 287 public void testCMode() { 288 Path clzPath = getOutputFilePath("StructTest.c.jar"); 289 checkSuccess(null,"-o", clzPath.toString(), 290 getInputFilePath("struct.h").toString()); 291 Class<?> cls = loadClass("struct", clzPath); 292 verifyAsC(cls); 293 deleteFile(clzPath); 294 } 295 }