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 import java.lang.invoke.MethodHandles; 25 import java.lang.reflect.Method; 26 import java.nio.file.Path; 27 import java.util.function.BiConsumer; 28 29 import jdk.incubator.foreign.MemoryLayout; 30 import jdk.incubator.foreign.MemorySegment; 31 import jdk.incubator.foreign.ResourceScope; 32 import org.testng.annotations.Test; 33 34 import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; 35 import static org.testng.Assert.assertEquals; 36 import static org.testng.Assert.assertNotNull; 37 import static org.testng.Assert.fail; 38 39 /* 40 * @test 41 * @modules jdk.incubator.jextract 42 * @library /test/lib 43 * @build JextractToolRunner 44 * @bug 8244512 8252759 45 * @summary test nested structs and unions 46 * @run testng/othervm --enable-native-access=jdk.incubator.jextract TestNested 47 */ 48 public class TestNested extends JextractToolRunner { 49 @Test 50 public void testNestedStructs() { 51 Path nestedOutput = getOutputFilePath("nestedgen"); 52 Path nestedH = getInputFilePath("nested.h"); 53 run("-d", nestedOutput.toString(), nestedH.toString()).checkSuccess(); 54 try(Loader loader = classLoader(nestedOutput)) { 55 checkClass(loader, "Foo", 56 checkField("bar", MemorySegment.class, 0), 57 checkField("color", int.class, 8) 58 ); 59 checkClass(loader, "Foo$Bar", 60 checkField("x", int.class, 0), 61 checkField("y", int.class, 4) 62 ); 63 checkClass(loader, "U", 64 checkField("point", MemorySegment.class, 0), 65 checkField("rgb", int.class, 0), 66 checkField("i", int.class, 0) 67 ); 68 checkClass(loader, "U$Point", 69 checkField("x", short.class, 0), 70 checkField("y", short.class, 2) 71 ); 72 checkClass(loader, "MyStruct", 73 checkField("a", byte.class, 0), 74 75 checkField("b", int.class, 4, "$anon$0"), 76 checkField("c", int.class, 8, "$anon$0", "$anon$0"), 77 78 checkField("d", byte.class, 12, "$anon$0"), 79 checkField("f", MemorySegment.class, 13, "$anon$0"), 80 81 checkField("g", int.class, 16, "$anon$1"), 82 checkField("h", long.class, 16, "$anon$1"), 83 84 checkField("k", MemorySegment.class, 24) 85 ); 86 checkClass(loader, "MyStruct$MyStruct_Z", 87 checkField("e", byte.class, 0) 88 ); 89 checkClass(loader, "MyStruct$k", 90 checkField("i", int.class, 0), 91 checkField("j", int.class, 4) 92 ); 93 checkClass(loader, "MyUnion", 94 checkField("a", byte.class, 0), 95 96 checkField("b", int.class, 0, "$anon$0"), 97 checkField("c", int.class, 4, "$anon$0", "$anon$0"), 98 99 checkField("d", byte.class, 8, "$anon$0"), 100 checkField("f", MemorySegment.class, 9, "$anon$0"), 101 102 checkField("g", int.class, 0, "$anon$1"), 103 checkField("h", int.class, 4, "$anon$1"), 104 105 checkField("k", MemorySegment.class, 0) 106 ); 107 checkClass(loader, "MyUnion$MyUnion_Z", 108 checkField("e", byte.class, 0) 109 ); 110 checkClass(loader, "MyUnion$k", 111 checkField("i", int.class, 0), 112 checkField("j", long.class, 0) 113 ); 114 checkClass(loader, "X", 115 checkField("Z", MemorySegment.class, 0, "$anon$0") 116 ); 117 checkClass(loader, "X$Z", 118 checkField("y", int.class, 0) 119 ); 120 checkClass(loader, "X2", 121 checkField("y", int.class, 0, "$anon$0", "$anon$0") 122 ); 123 checkClass(loader, "NestedUnion", 124 checkField("x", int.class, 0), 125 checkField("y", int.class, 4, "$anon$0"), 126 checkField("z", int.class, 4, "$anon$0") 127 ); 128 } finally { 129 deleteDir(nestedOutput); 130 } 131 } 132 133 @SafeVarargs 134 private static void checkClass(Loader loader, String name, BiConsumer<Class<?>, MemoryLayout>... checks) { 135 Class<?> cls = loader.loadClass(name); 136 assertNotNull(cls); 137 MemoryLayout layout = findLayout(cls); 138 for (var check : checks) { 139 check.accept(cls, layout); 140 } 141 } 142 143 private static BiConsumer<Class<?>, MemoryLayout> checkField(String fieldName, Class<?> fieldType, 144 long expectedOffset, String... fieldPath) { 145 MemoryLayout.PathElement[] path = new MemoryLayout.PathElement[fieldPath.length + 1]; 146 int i = 0; 147 for (; i < fieldPath.length; i++) { 148 path[i] = groupElement(fieldPath[i]); 149 } 150 path[i] = groupElement(fieldName); 151 return (cls, layout) -> { 152 assertEquals(layout.byteOffset(path), expectedOffset); 153 checkAccessors(cls, layout, fieldName, fieldType, layout.select(path)); 154 }; 155 } 156 157 private static void checkAccessors(Class<?> cls, MemoryLayout layout, String fieldName, Class<?> type, 158 MemoryLayout fieldLayout) { 159 try { 160 if (type == MemorySegment.class) { 161 Method slicer = cls.getMethod(fieldName + "$slice", MemorySegment.class); 162 assertEquals(slicer.getReturnType(), MemorySegment.class); 163 try (ResourceScope scope = ResourceScope.newConfinedScope()) { 164 MemorySegment struct = MemorySegment.allocateNative(layout, scope); 165 MemorySegment slice = (MemorySegment) slicer.invoke(null, struct); 166 assertEquals(slice.byteSize(), fieldLayout.byteSize()); 167 } 168 } else { 169 Method getter = cls.getMethod(fieldName + "$get", MemorySegment.class); 170 assertEquals(getter.getReturnType(), type); 171 Method setter = cls.getMethod(fieldName + "$set", MemorySegment.class, type); 172 assertEquals(setter.getReturnType(), void.class); 173 174 Object zero = MethodHandles.zero(type).invoke(); 175 try (ResourceScope scope = ResourceScope.newConfinedScope()) { 176 MemorySegment struct = MemorySegment.allocateNative(layout, scope); 177 setter.invoke(null, struct, zero); 178 Object actual = getter.invoke(null, struct); 179 assertEquals(actual, zero); 180 } 181 } 182 } catch (Throwable t) { 183 fail("Unexpected exception", t); 184 } 185 } 186 }