20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package jdk.internal.foreign.abi.fallback;
26
27 import jdk.internal.foreign.Utils;
28
29 import java.lang.foreign.Arena;
30 import java.lang.foreign.GroupLayout;
31 import java.lang.foreign.MemoryLayout;
32 import java.lang.foreign.MemorySegment;
33 import java.lang.foreign.PaddingLayout;
34 import java.lang.foreign.SequenceLayout;
35 import java.lang.foreign.StructLayout;
36 import java.lang.foreign.UnionLayout;
37 import java.lang.foreign.ValueLayout;
38 import java.lang.invoke.VarHandle;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Objects;
44 import java.util.function.Predicate;
45
46 import static java.lang.foreign.ValueLayout.ADDRESS;
47 import static java.lang.foreign.ValueLayout.JAVA_INT;
48 import static java.lang.foreign.ValueLayout.JAVA_LONG;
49 import static java.lang.foreign.ValueLayout.JAVA_SHORT;
50
51 /**
52 * typedef struct _ffi_type
53 * {
54 * size_t size;
55 * unsigned short alignment;
56 * unsigned short type;
57 * struct _ffi_type **elements;
58 * } ffi_type;
59 */
60 class FFIType {
61 private static final ValueLayout SIZE_T = switch ((int) ADDRESS.byteSize()) {
62 case 8 -> JAVA_LONG;
63 case 4 -> JAVA_INT;
64 default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize());
65 };
66 private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT;
67 private static final StructLayout LAYOUT = Utils.computePaddedStructLayout(
68 SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements"));
69
70 private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type"));
71 private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements"));
72 private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle();
73
74 private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, Arena scope) {
75 MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize());
76 int i = 0;
77 for (; i < elements.size(); i++) {
78 MemoryLayout elementLayout = elements.get(i);
79 MemorySegment elementType = toFFIType(elementLayout, abi, scope);
80 elementsSeg.setAtIndex(ADDRESS, i, elementType);
81 }
82 // elements array is null-terminated
83 elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL);
84
85 MemorySegment ffiType = scope.allocate(LAYOUT);
86 VH_TYPE.set(ffiType, LibFallback.structTag());
87 VH_ELEMENTS.set(ffiType, elementsSeg);
88
89 return ffiType;
90 }
91
92 private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of(
93 boolean.class, LibFallback.uint8Type(),
94 byte.class, LibFallback.sint8Type(),
95 short.class, LibFallback.sint16Type(),
96 char.class, LibFallback.uint16Type(),
97 int.class, LibFallback.sint32Type(),
98 long.class, LibFallback.sint64Type(),
99 float.class, LibFallback.floatType(),
100 double.class, LibFallback.doubleType(),
101 MemorySegment.class, LibFallback.pointerType()
102 );
103
104 static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) {
105 if (layout instanceof GroupLayout grpl) {
106 if (grpl instanceof StructLayout strl) {
107 // libffi doesn't want our padding
115 assert grpl instanceof UnionLayout;
116 // JDK-8301800
117 throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl);
118 } else if (layout instanceof SequenceLayout sl) {
119 List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout());
120 return make(elements, abi, scope);
121 }
122 return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier()));
123 }
124
125 // verify layout against what libffi sets
126 private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType,
127 FFIABI abi) {
128 try (Arena verifyArena = Arena.ofConfined()) {
129 MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size());
130 LibFallback.getStructOffsets(structType, offsetsOut, abi);
131 long expectedOffset = 0;
132 int offsetIdx = 0;
133 for (MemoryLayout element : structLayout.memberLayouts()) {
134 if (!(element instanceof PaddingLayout)) {
135 long ffiOffset = (long) VH_SIZE_T_ARRAY.get(offsetsOut, offsetIdx++);
136 if (ffiOffset != expectedOffset) {
137 throw new IllegalArgumentException("Invalid group layout." +
138 " Offset of '" + element.name().orElse("<unnamed>")
139 + "': " + expectedOffset + " != " + ffiOffset);
140 }
141 }
142 expectedOffset += element.byteSize();
143 }
144 }
145 }
146 }
|
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package jdk.internal.foreign.abi.fallback;
26
27 import jdk.internal.foreign.Utils;
28
29 import java.lang.foreign.Arena;
30 import java.lang.foreign.GroupLayout;
31 import java.lang.foreign.MemoryLayout;
32 import java.lang.foreign.MemorySegment;
33 import java.lang.foreign.PaddingLayout;
34 import java.lang.foreign.SequenceLayout;
35 import java.lang.foreign.StructLayout;
36 import java.lang.foreign.UnionLayout;
37 import java.lang.foreign.ValueLayout;
38 import java.lang.invoke.VarHandle;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.function.Predicate;
44
45 import static java.lang.foreign.ValueLayout.ADDRESS;
46 import static java.lang.foreign.ValueLayout.JAVA_BYTE;
47 import static java.lang.foreign.ValueLayout.JAVA_INT;
48 import static java.lang.foreign.ValueLayout.JAVA_LONG;
49 import static java.lang.foreign.ValueLayout.JAVA_SHORT;
50
51 /**
52 * typedef struct _ffi_type
53 * {
54 * size_t size;
55 * unsigned short alignment;
56 * unsigned short type;
57 * struct _ffi_type **elements;
58 * } ffi_type;
59 */
60 class FFIType {
61 static final ValueLayout SIZE_T = layoutFor((int)ADDRESS.byteSize());
62 private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT;
63 private static final StructLayout LAYOUT = Utils.computePaddedStructLayout(
64 SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements"));
65
66 private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type"));
67 private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements"));
68 private static final VarHandle VH_SIZE_T = SIZE_T.varHandle();
69
70 private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, Arena scope) {
71 MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize());
72 int i = 0;
73 for (; i < elements.size(); i++) {
74 MemoryLayout elementLayout = elements.get(i);
75 MemorySegment elementType = toFFIType(elementLayout, abi, scope);
76 elementsSeg.setAtIndex(ADDRESS, i, elementType);
77 }
78 // elements array is null-terminated
79 elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL);
80
81 MemorySegment ffiType = scope.allocate(LAYOUT);
82 VH_TYPE.set(ffiType, 0L, LibFallback.structTag());
83 VH_ELEMENTS.set(ffiType, 0L, elementsSeg);
84
85 return ffiType;
86 }
87
88 private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of(
89 boolean.class, LibFallback.uint8Type(),
90 byte.class, LibFallback.sint8Type(),
91 short.class, LibFallback.sint16Type(),
92 char.class, LibFallback.uint16Type(),
93 int.class, LibFallback.sint32Type(),
94 long.class, LibFallback.sint64Type(),
95 float.class, LibFallback.floatType(),
96 double.class, LibFallback.doubleType(),
97 MemorySegment.class, LibFallback.pointerType()
98 );
99
100 static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) {
101 if (layout instanceof GroupLayout grpl) {
102 if (grpl instanceof StructLayout strl) {
103 // libffi doesn't want our padding
111 assert grpl instanceof UnionLayout;
112 // JDK-8301800
113 throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl);
114 } else if (layout instanceof SequenceLayout sl) {
115 List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout());
116 return make(elements, abi, scope);
117 }
118 return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier()));
119 }
120
121 // verify layout against what libffi sets
122 private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType,
123 FFIABI abi) {
124 try (Arena verifyArena = Arena.ofConfined()) {
125 MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size());
126 LibFallback.getStructOffsets(structType, offsetsOut, abi);
127 long expectedOffset = 0;
128 int offsetIdx = 0;
129 for (MemoryLayout element : structLayout.memberLayouts()) {
130 if (!(element instanceof PaddingLayout)) {
131 long ffiOffset = sizeTAtIndex(offsetsOut, offsetIdx++);
132 if (ffiOffset != expectedOffset) {
133 throw new IllegalArgumentException("Invalid group layout." +
134 " Offset of '" + element.name().orElse("<unnamed>")
135 + "': " + expectedOffset + " != " + ffiOffset);
136 }
137 }
138 expectedOffset += element.byteSize();
139 }
140 }
141 }
142
143 static ValueLayout layoutFor(int byteSize) {
144 return switch (byteSize) {
145 case 1 -> JAVA_BYTE;
146 case 2 -> JAVA_SHORT;
147 case 4 -> JAVA_INT;
148 case 8 -> JAVA_LONG;
149 default -> throw new IllegalStateException("Unsupported size: " + byteSize);
150 };
151 }
152
153 private static long sizeTAtIndex(MemorySegment segment, int index) {
154 long offset = SIZE_T.scale(0, index);
155 if (VH_SIZE_T.varType() == long.class) {
156 return (long) VH_SIZE_T.get(segment, offset);
157 } else {
158 return (int) VH_SIZE_T.get(segment, offset); // 'erase' to long
159 }
160 }
161 }
|