1 /*
  2  * Copyright (c) 2020, 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  * @modules jdk.incubator.jextract
 27  *          jdk.incubator.foreign/jdk.incubator.foreign.unsafe
 28  *          jdk.incubator.foreign/jdk.internal.foreign
 29  *          jdk.incubator.foreign/jdk.internal.foreign.abi
 30  *          java.base/sun.security.action
 31  * @library .. /test/lib
 32  * @build JextractToolRunner
 33  * @run testng/othervm --enable-native-access=jdk.incubator.jextract,ALL-UNNAMED -Duser.language=en TestClassGeneration
 34  */
 35 
 36 import jdk.incubator.foreign.Addressable;
 37 import jdk.incubator.foreign.CLinker;
 38 import jdk.incubator.foreign.MemoryAddress;
 39 import jdk.incubator.foreign.MemoryLayout;
 40 import jdk.incubator.foreign.MemorySegment;
 41 import jdk.incubator.foreign.NativeSymbol;
 42 import jdk.incubator.foreign.ResourceScope;
 43 import org.testng.annotations.AfterClass;
 44 import org.testng.annotations.BeforeClass;
 45 import org.testng.annotations.DataProvider;
 46 import org.testng.annotations.Test;
 47 
 48 import java.lang.invoke.MethodHandle;
 49 import java.lang.invoke.MethodType;
 50 import java.lang.invoke.VarHandle;
 51 import java.lang.reflect.Method;
 52 import java.nio.charset.StandardCharsets;
 53 import java.nio.file.Path;
 54 
 55 import static java.lang.invoke.MethodType.methodType;
 56 import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
 57 import static org.testng.Assert.assertEquals;
 58 import static org.testng.Assert.assertNotNull;
 59 
 60 public class TestClassGeneration extends JextractToolRunner {
 61 
 62     private static final VarHandle VH_bytes = MemoryLayout.sequenceLayout(C_CHAR).varHandle(sequenceElement());
 63 
 64     private Path outputDir;
 65     private Loader loader;
 66     private Class<?> cls;
 67 
 68     @DataProvider
 69     public static Object[][] simpleConstants() {
 70         return new Object[][]{
 71             { "macro_byte",         byte.class,   (byte) 1                         },
 72             { "macro_short",        short.class, (short) 1                         },
 73             { "macro_int",          int.class,           1                         },
 74             { "macro_long",         long.class,          1L                        },
 75             { "macro_float",        float.class,         1.0F                      },
 76             { "macro_double",       double.class,        1.0D                      },
 77             { "macro_address_NULL", MemoryAddress.class, MemoryAddress.NULL        },
 78             { "macro_address_123",  MemoryAddress.class, MemoryAddress.ofLong(123) },
 79             { "enum_0",             int.class,           0                         },
 80             { "enum_1",             int.class,           1                         },
 81             { "enum_2",             int.class,           2                         },
 82             { "enum_anon_0",        int.class,           0                         },
 83             { "enum_anon_1",        int.class,           1                         },
 84             { "enum_anon_2",        int.class,           2                         },
 85         };
 86     }
 87 
 88     @DataProvider
 89     public static Object[][] stringConstants() {
 90         return new Object[][]{
 91             { "macro_string",         "abc"      },
 92             { "macro_string_noident", "123.asdf" },
 93         };
 94     }
 95 
 96     private static final Object[] NO_ARGS = {};
 97 
 98     @DataProvider
 99     public static Object[][] method() {
100         return new Object[][]{
101             { "func_byte",   methodType(byte.class),   (byte) 1,  NO_ARGS },
102             { "func_short",  methodType(short.class), (short) 2,  NO_ARGS },
103             { "func_int",    methodType(int.class),           3,  NO_ARGS },
104             { "func_long",   methodType(long.class),          4L, NO_ARGS },
105             { "func_float",  methodType(float.class),         5F, NO_ARGS },
106             { "func_double", methodType(double.class),        6D, NO_ARGS },
107         };
108     }
109 
110     @DataProvider
111     public static Object[][] globals() {
112         return new Object[][]{
113             { "global_byte",   byte.class,   C_CHAR,   (byte) 1  },
114             { "global_short",  short.class,  C_SHORT, (short) 2  },
115             { "global_int",    int.class,    C_INT,           3  },
116             { "global_long",   long.class,   C_LONG_LONG,      4L },
117             { "global_float",  float.class,  C_FLOAT,         5F },
118             { "global_double", double.class, C_DOUBLE,        6D },
119         };
120     }
121 
122     @DataProvider
123     public static Object[][] structMembers() {
124         return new Object[][] {
125             { "Foo", C_CHAR.withName("c"),      byte.class,   (byte) 10  },
126             { "Foo", C_SHORT.withName("s"),     short.class, (short) 10  },
127             { "Foo", C_INT.withName("i"),       int.class,           10  },
128             { "Foo", C_LONG_LONG.withName("ll"), long.class,          10L },
129             { "Foo", C_FLOAT.withName("f"),     float.class,         10F },
130             { "Foo", C_DOUBLE.withName("d"),    double.class,        10D },
131             { "Bar", C_INT.withName("a"),       int.class,           10 },
132             { "Bar", C_INT.withName("b"),       int.class,           10 },
133         };
134     }
135 
136     @DataProvider
137     public static Object[][] functionalInterfaces() {
138         return new Object[][]{
139             { "CB", methodType(void.class, int.class) }
140         };
141     }
142 
143     @Test(dataProvider = "simpleConstants")
144     public void testConstant(String name, Class<?> expectedType, Object expectedValue) throws Throwable {
145         Method getter = checkMethod(cls, name, expectedType);
146         assertEquals(getter.invoke(null), expectedValue);
147     }
148 
149     @Test(dataProvider = "stringConstants")
150     public void testStringConstant(String name, String expectedValue) throws Throwable {
151         Method getter = checkMethod(cls, name, MemorySegment.class);
152         MemorySegment actual = (MemorySegment) getter.invoke(null);
153         byte[] expected = expectedValue.getBytes(StandardCharsets.UTF_8);
154         assertEquals(actual.byteSize(), expected.length + 1);
155         for (int i = 0; i < expected.length; i++) {
156             assertEquals((byte) VH_bytes.get(actual, (long) i), expected[i]);
157         }
158     }
159 
160     @Test(dataProvider = "method")
161     public void testMethod(String name, MethodType expectedType, Object expectedReturn, Object[] args) throws Throwable {
162         Method mh_getter = checkMethod(cls, name + "$MH", MethodHandle.class);
163         MethodHandle mh = (MethodHandle) mh_getter.invoke(null);
164         assertEquals(mh.type(), expectedType);
165 
166         Object actualReturn = mh.invokeWithArguments(args);
167         assertEquals(actualReturn.getClass(), expectedReturn.getClass());
168         assertEquals(actualReturn, expectedReturn);
169 
170         Method func = checkMethod(cls, name, expectedType);
171         assertEquals(func.invoke(null, args), expectedReturn);
172     }
173 
174     @Test(dataProvider = "globals")
175     public void testGlobal(String name, Class<?> expectedType, MemoryLayout expectedLayout, Object expectedValue) throws Throwable {
176         Method layout_getter = checkMethod(cls, name + "$LAYOUT", MemoryLayout.class);
177         assertEquals(layout_getter.invoke(null), expectedLayout);
178 
179         Method addr_getter = checkMethod(cls, name + "$SEGMENT", MemorySegment.class);
180         MemorySegment segment = (MemorySegment)addr_getter.invoke(null);
181 
182         Method vh_getter = checkMethod(cls, name + "$VH", VarHandle.class);
183         VarHandle vh = (VarHandle) vh_getter.invoke(null);
184         assertEquals(vh.varType(), expectedType);
185         assertEquals(vh.get(segment), expectedValue);
186 
187         checkMethod(cls, name + "$get", expectedType);
188         checkMethod(cls, name + "$set", void.class, expectedType);
189     }
190 
191     @Test(dataProvider = "structMembers")
192     public void testStructMember(String structName, MemoryLayout memberLayout, Class<?> expectedType, Object testValue) throws Throwable {
193         String memberName = memberLayout.name().orElseThrow();
194 
195         Class<?> structCls = loader.loadClass("com.acme." + structName);
196         Method layout_getter = checkMethod(structCls, "$LAYOUT", MemoryLayout.class);
197         MemoryLayout structLayout = (MemoryLayout) layout_getter.invoke(null);
198         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
199             MemorySegment struct = MemorySegment.allocateNative(structLayout, scope);
200             Method vh_getter = checkMethod(structCls, memberName + "$VH", VarHandle.class);
201             VarHandle vh = (VarHandle) vh_getter.invoke(null);
202             assertEquals(vh.varType(), expectedType);
203 
204             Method getter = checkMethod(structCls, memberName + "$get", expectedType, MemorySegment.class);
205             Method setter = checkMethod(structCls, memberName + "$set", void.class, MemorySegment.class, expectedType);
206             MemorySegment addr = struct;
207             setter.invoke(null, addr, testValue);
208             assertEquals(getter.invoke(null, addr), testValue);
209         }
210     }
211 
212     @Test(dataProvider = "functionalInterfaces")
213     public void testFunctionalInterface(String name, MethodType type) {
214         Class<?> fiClass = loader.loadClass("com.acme." + name);
215         assertNotNull(fiClass);
216         checkMethod(fiClass, "apply", type);
217         checkMethod(fiClass, "allocate", NativeSymbol.class, fiClass, ResourceScope.class);
218     }
219 
220     @BeforeClass
221     public void setup() {
222         outputDir = getOutputFilePath("exmples_out");
223         Path inputHeader = getInputFilePath("examples.h");
224         run(
225             "-t", "com.acme",
226             "-d", outputDir,
227             "-l", "Examples",
228             "--",
229             inputHeader
230         ).checkSuccess();
231         loader = classLoader(outputDir);
232         cls = loader.loadClass("com.acme.examples_h");
233     }
234 
235     @AfterClass
236     public void tearDown() {
237         loader.close();
238         deleteDir(outputDir);
239     }
240 
241 }