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