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 }