1 /*
  2  * Copyright (c) 2023, 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 id=specialized
 26  * @enablePreview
 27  * @library ../
 28  * @requires jdk.foreign.linker != "UNSUPPORTED"
 29  * @requires (!(os.name == "Mac OS X" & os.arch == "aarch64") | jdk.foreign.linker != "FALLBACK")
 30  * @modules java.base/jdk.internal.foreign
 31  * @run testng/othervm
 32  *   --enable-native-access=ALL-UNNAMED
 33  *   -Djdk.internal.foreign.DowncallLinker.USE_SPEC=true
 34  *   -Djdk.internal.foreign.UpcallLinker.USE_SPEC=true
 35  *   TestArrayStructs
 36  */
 37 
 38 /*
 39  * @test id=interpreted
 40  * @enablePreview
 41  * @library ../
 42  * @requires jdk.foreign.linker != "UNSUPPORTED"
 43  * @requires (!(os.name == "Mac OS X" & os.arch == "aarch64") | jdk.foreign.linker != "FALLBACK")
 44  * @modules java.base/jdk.internal.foreign
 45  * @run testng/othervm
 46  *   --enable-native-access=ALL-UNNAMED
 47  *   -Djdk.internal.foreign.DowncallLinker.USE_SPEC=false
 48  *   -Djdk.internal.foreign.UpcallLinker.USE_SPEC=false
 49  *   TestArrayStructs
 50  */
 51 
 52 import org.testng.annotations.DataProvider;
 53 import org.testng.annotations.Test;
 54 
 55 import java.lang.foreign.Arena;
 56 import java.lang.foreign.FunctionDescriptor;
 57 import java.lang.foreign.MemoryLayout;
 58 import java.lang.foreign.MemorySegment;
 59 import java.lang.foreign.StructLayout;
 60 import java.lang.invoke.MethodHandle;
 61 import java.util.ArrayList;
 62 import java.util.Collections;
 63 import java.util.List;
 64 import java.util.concurrent.atomic.AtomicReference;
 65 import java.util.function.Consumer;
 66 import java.util.stream.Stream;
 67 
 68 import static java.lang.foreign.MemoryLayout.sequenceLayout;
 69 import static java.lang.foreign.MemoryLayout.structLayout;
 70 
 71 public class TestArrayStructs extends NativeTestHelper {
 72     static {
 73         System.loadLibrary("ArrayStructs");
 74     }
 75 
 76     // Test if structs of various different sizes, including non-powers of two, work correctly
 77     @Test(dataProvider = "arrayStructs")
 78     public void testArrayStruct(String functionName, FunctionDescriptor baseDesc, int numPrefixArgs, int numElements) throws Throwable {
 79         FunctionDescriptor downcallDesc = baseDesc.insertArgumentLayouts(0, C_POINTER); // CB
 80         MemoryLayout[] elementLayouts = Collections.nCopies(numElements, C_CHAR).toArray(MemoryLayout[]::new);
 81         FunctionDescriptor upcallDesc = baseDesc.appendArgumentLayouts(elementLayouts);
 82         try (Arena arena = Arena.ofConfined()) {
 83             TestValue[] testArgs = genTestArgs(baseDesc, arena);
 84 
 85             MethodHandle downcallHandle = downcallHandle(functionName, downcallDesc);
 86             Object[] args = new Object[downcallDesc.argumentLayouts().size() + 1]; // +1 for return allocator
 87             AtomicReference<Object[]> returnBox = new AtomicReference<>();
 88             int returnIdx = numPrefixArgs;
 89             int argIdx = 0;
 90             args[argIdx++] = arena;
 91             args[argIdx++] = makeArgSaverCB(upcallDesc, arena, returnBox, returnIdx);
 92             for (TestValue testArg : testArgs) {
 93                 args[argIdx++] = testArg.value();
 94             }
 95 
 96             MemorySegment returned = (MemorySegment) downcallHandle.invokeWithArguments(args);
 97             Consumer<Object> structCheck = testArgs[returnIdx].check();
 98 
 99             structCheck.accept(returned);
100 
101             Object[] capturedArgs = returnBox.get();
102             int capturedArgIdx;
103             for (capturedArgIdx = numPrefixArgs; capturedArgIdx < testArgs.length; capturedArgIdx++) {
104                 testArgs[capturedArgIdx].check().accept(capturedArgs[capturedArgIdx]);
105             }
106 
107             byte[] elements = new byte[numElements];
108             for (int elIdx = 0; elIdx < numElements; elIdx++, capturedArgIdx++) {
109                 elements[elIdx] = (byte) capturedArgs[capturedArgIdx];
110             }
111 
112             structCheck.accept(MemorySegment.ofArray(elements)); // reuse the check for the struct
113         }
114     }
115 
116     @DataProvider
117     public static Object[][] arrayStructs() {
118         List<Object[]> cases = new ArrayList<>();
119         for (int i = 0; i < layouts.size(); i++) {
120             StructLayout layout = layouts.get(i);
121             int numElements = i + 1;
122             cases.add(new Object[]{"F" + numElements, FunctionDescriptor.of(layout, layout), 0, numElements});
123         }
124         for (int i = 0; i < layouts.size(); i++) {
125             StructLayout layout = layouts.get(i);
126             MemoryLayout[] argLayouts = Stream.concat(PREFIX_LAYOUTS.stream(), Stream.of(layout)).toArray(MemoryLayout[]::new);
127             int numElements = i + 1;
128             cases.add(new Object[]{"F" + numElements + "_stack", FunctionDescriptor.of(layout, argLayouts), PREFIX_LAYOUTS.size(), numElements});
129         }
130 
131         return cases.toArray(Object[][]::new);
132     }
133 
134     static final List<MemoryLayout> PREFIX_LAYOUTS = List.of(
135         C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
136         C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
137 
138     static final List<StructLayout> layouts = List.of(
139         structLayout(sequenceLayout(1, C_CHAR).withName("f0")).withName("S1"),
140         structLayout(sequenceLayout(2, C_CHAR).withName("f0")).withName("S2"),
141         structLayout(sequenceLayout(3, C_CHAR).withName("f0")).withName("S3"),
142         structLayout(sequenceLayout(4, C_CHAR).withName("f0")).withName("S4"),
143         structLayout(sequenceLayout(5, C_CHAR).withName("f0")).withName("S5"),
144         structLayout(sequenceLayout(6, C_CHAR).withName("f0")).withName("S6"),
145         structLayout(sequenceLayout(7, C_CHAR).withName("f0")).withName("S7"),
146         structLayout(sequenceLayout(8, C_CHAR).withName("f0")).withName("S8"),
147         structLayout(sequenceLayout(9, C_CHAR).withName("f0")).withName("S9"),
148         structLayout(sequenceLayout(10, C_CHAR).withName("f0")).withName("S10"),
149         structLayout(sequenceLayout(11, C_CHAR).withName("f0")).withName("S11"),
150         structLayout(sequenceLayout(12, C_CHAR).withName("f0")).withName("S12"),
151         structLayout(sequenceLayout(13, C_CHAR).withName("f0")).withName("S13"),
152         structLayout(sequenceLayout(14, C_CHAR).withName("f0")).withName("S14"),
153         structLayout(sequenceLayout(15, C_CHAR).withName("f0")).withName("S15"),
154         structLayout(sequenceLayout(16, C_CHAR).withName("f0")).withName("S16"));
155 }