37 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
38 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
39 import jdk.internal.foreign.layout.AbstractLayout;
40 import jdk.internal.reflect.CallerSensitive;
41 import jdk.internal.reflect.Reflection;
42
43 import java.lang.foreign.AddressLayout;
44 import java.lang.foreign.GroupLayout;
45 import java.lang.foreign.MemoryLayout;
46 import java.lang.foreign.Arena;
47 import java.lang.foreign.FunctionDescriptor;
48 import java.lang.foreign.Linker;
49 import java.lang.foreign.MemorySegment;
50 import java.lang.foreign.PaddingLayout;
51 import java.lang.foreign.SequenceLayout;
52 import java.lang.foreign.StructLayout;
53 import java.lang.foreign.UnionLayout;
54 import java.lang.foreign.ValueLayout;
55 import java.lang.invoke.MethodHandle;
56 import java.lang.invoke.MethodType;
57 import java.util.List;
58 import java.nio.ByteOrder;
59 import java.util.Objects;
60 import java.util.Set;
61
62 public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
63 SysVx64Linker, WindowsAArch64Linker,
64 Windowsx64Linker,
65 LinuxPPC64Linker, LinuxPPC64leLinker,
66 LinuxRISCV64Linker, LinuxS390Linker,
67 FallbackLinker {
68
69 public interface UpcallStubFactory {
70 MemorySegment makeStub(MethodHandle target, Arena arena);
71 }
72
73 private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
74 private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
75 private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
76
77 @Override
78 @CallerSensitive
79 public final MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) {
80 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle");
81 SharedUtils.checkSymbol(symbol);
82 return downcallHandle0(function, options).bindTo(symbol);
83 }
84
85 @Override
86 @CallerSensitive
87 public final MethodHandle downcallHandle(FunctionDescriptor function, Option... options) {
88 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle");
89 return downcallHandle0(function, options);
90 }
91
92 private final MethodHandle downcallHandle0(FunctionDescriptor function, Option... options) {
93 Objects.requireNonNull(function);
94 Objects.requireNonNull(options);
95 checkLayouts(function);
96 function = stripNames(function);
97 LinkerOptions optionSet = LinkerOptions.forDowncall(function, options);
98 validateVariadicLayouts(function, optionSet);
99
100 return DOWNCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> {
101 FunctionDescriptor fd = linkRequest.descriptor();
102 MethodType type = fd.toMethodType();
103 MethodHandle handle = arrangeDowncall(type, fd, linkRequest.options());
104 handle = SharedUtils.maybeCheckCaptureSegment(handle, linkRequest.options());
105 handle = SharedUtils.maybeInsertAllocator(fd, handle);
106 return handle;
107 });
108 }
109
110 protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options);
111
112 @Override
196 }
197 }
198 checkGroupSize(sl, lastUnpaddedOffset);
199 } else if (layout instanceof UnionLayout ul) {
200 checkHasNaturalAlignment(layout);
201 long maxUnpaddedLayout = 0;
202 for (MemoryLayout member : ul.memberLayouts()) {
203 checkLayoutRecursive(member);
204 if (!(member instanceof PaddingLayout)) {
205 maxUnpaddedLayout = Long.max(maxUnpaddedLayout, member.byteSize());
206 }
207 }
208 checkGroupSize(ul, maxUnpaddedLayout);
209 } else if (layout instanceof SequenceLayout sl) {
210 checkHasNaturalAlignment(layout);
211 checkLayoutRecursive(sl.elementLayout());
212 }
213 }
214
215 // check for trailing padding
216 private static void checkGroupSize(GroupLayout gl, long maxUnpaddedOffset) {
217 long expectedSize = Utils.alignUp(maxUnpaddedOffset, gl.byteAlignment());
218 if (gl.byteSize() != expectedSize) {
219 throw new IllegalArgumentException("Layout '" + gl + "' has unexpected size: "
220 + gl.byteSize() + " != " + expectedSize);
221 }
222 }
223
224 // checks both that there is no excess padding between 'memberLayout' and
225 // the previous layout
226 private static void checkMemberOffset(StructLayout parent, MemoryLayout memberLayout,
227 long lastUnpaddedOffset, long offset) {
228 long expectedOffset = Utils.alignUp(lastUnpaddedOffset, memberLayout.byteAlignment());
229 if (expectedOffset != offset) {
230 throw new IllegalArgumentException("Member layout '" + memberLayout + "', of '" + parent + "'" +
231 " found at unexpected offset: " + offset + " != " + expectedOffset);
232 }
233 }
234
235 private static void checkSupported(ValueLayout valueLayout) {
236 valueLayout = valueLayout.withoutName();
237 if (valueLayout instanceof AddressLayout addressLayout) {
238 valueLayout = addressLayout.withoutTargetLayout();
239 }
240 if (!SUPPORTED_LAYOUTS.contains(valueLayout.withoutName())) {
241 throw new IllegalArgumentException("Unsupported layout: " + valueLayout);
242 }
243 }
244
245 private static void checkHasNaturalAlignment(MemoryLayout layout) {
246 if (!((AbstractLayout<?>) layout).hasNaturalAlignment()) {
247 throw new IllegalArgumentException("Layout alignment must be natural alignment: " + layout);
248 }
249 }
250
251 private static MemoryLayout stripNames(MemoryLayout ml) {
252 // we don't care about transferring alignment and byte order here
253 // since the linker already restricts those such that they will always be the same
254 return switch (ml) {
255 case StructLayout sl -> MemoryLayout.structLayout(stripNames(sl.memberLayouts()));
256 case UnionLayout ul -> MemoryLayout.unionLayout(stripNames(ul.memberLayouts()));
257 case SequenceLayout sl -> MemoryLayout.sequenceLayout(sl.elementCount(), stripNames(sl.elementLayout()));
258 case AddressLayout al -> al.targetLayout()
259 .map(tl -> al.withoutName().withTargetLayout(stripNames(tl)))
260 .orElseGet(al::withoutName);
261 default -> ml.withoutName(); // ValueLayout and PaddingLayout
262 };
263 }
264
265 private static MemoryLayout[] stripNames(List<MemoryLayout> layouts) {
266 return layouts.stream()
267 .map(AbstractLinker::stripNames)
268 .toArray(MemoryLayout[]::new);
269 }
270
271 private static FunctionDescriptor stripNames(FunctionDescriptor function) {
272 return function.returnLayout()
273 .map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts())))
274 .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts())));
275 }
276
277 private static final Set<MemoryLayout> SUPPORTED_LAYOUTS = Set.of(
278 ValueLayout.JAVA_BOOLEAN,
279 ValueLayout.JAVA_BYTE,
280 ValueLayout.JAVA_CHAR,
281 ValueLayout.JAVA_SHORT,
282 ValueLayout.JAVA_INT,
283 ValueLayout.JAVA_FLOAT,
284 ValueLayout.JAVA_LONG,
285 ValueLayout.JAVA_DOUBLE,
286 ValueLayout.ADDRESS
287 );
288 }
|
37 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
38 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
39 import jdk.internal.foreign.layout.AbstractLayout;
40 import jdk.internal.reflect.CallerSensitive;
41 import jdk.internal.reflect.Reflection;
42
43 import java.lang.foreign.AddressLayout;
44 import java.lang.foreign.GroupLayout;
45 import java.lang.foreign.MemoryLayout;
46 import java.lang.foreign.Arena;
47 import java.lang.foreign.FunctionDescriptor;
48 import java.lang.foreign.Linker;
49 import java.lang.foreign.MemorySegment;
50 import java.lang.foreign.PaddingLayout;
51 import java.lang.foreign.SequenceLayout;
52 import java.lang.foreign.StructLayout;
53 import java.lang.foreign.UnionLayout;
54 import java.lang.foreign.ValueLayout;
55 import java.lang.invoke.MethodHandle;
56 import java.lang.invoke.MethodType;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.nio.ByteOrder;
60 import java.util.Objects;
61 import java.util.Set;
62
63 public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
64 SysVx64Linker, WindowsAArch64Linker,
65 Windowsx64Linker,
66 LinuxPPC64Linker, LinuxPPC64leLinker,
67 LinuxRISCV64Linker, LinuxS390Linker,
68 FallbackLinker {
69
70 public interface UpcallStubFactory {
71 MemorySegment makeStub(MethodHandle target, Arena arena);
72 }
73
74 private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
75 private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
76 private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
77 private final Set<MemoryLayout> CANONICAL_LAYOUTS_CACHE = new HashSet<>(canonicalLayouts().values());
78
79 @Override
80 @CallerSensitive
81 public final MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) {
82 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle");
83 SharedUtils.checkSymbol(symbol);
84 return downcallHandle0(function, options).bindTo(symbol);
85 }
86
87 @Override
88 @CallerSensitive
89 public final MethodHandle downcallHandle(FunctionDescriptor function, Option... options) {
90 Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle");
91 return downcallHandle0(function, options);
92 }
93
94 private MethodHandle downcallHandle0(FunctionDescriptor function, Option... options) {
95 Objects.requireNonNull(function);
96 Objects.requireNonNull(options);
97 checkLayouts(function);
98 function = stripNames(function);
99 LinkerOptions optionSet = LinkerOptions.forDowncall(function, options);
100 validateVariadicLayouts(function, optionSet);
101
102 return DOWNCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> {
103 FunctionDescriptor fd = linkRequest.descriptor();
104 MethodType type = fd.toMethodType();
105 MethodHandle handle = arrangeDowncall(type, fd, linkRequest.options());
106 handle = SharedUtils.maybeCheckCaptureSegment(handle, linkRequest.options());
107 handle = SharedUtils.maybeInsertAllocator(fd, handle);
108 return handle;
109 });
110 }
111
112 protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options);
113
114 @Override
198 }
199 }
200 checkGroupSize(sl, lastUnpaddedOffset);
201 } else if (layout instanceof UnionLayout ul) {
202 checkHasNaturalAlignment(layout);
203 long maxUnpaddedLayout = 0;
204 for (MemoryLayout member : ul.memberLayouts()) {
205 checkLayoutRecursive(member);
206 if (!(member instanceof PaddingLayout)) {
207 maxUnpaddedLayout = Long.max(maxUnpaddedLayout, member.byteSize());
208 }
209 }
210 checkGroupSize(ul, maxUnpaddedLayout);
211 } else if (layout instanceof SequenceLayout sl) {
212 checkHasNaturalAlignment(layout);
213 checkLayoutRecursive(sl.elementLayout());
214 }
215 }
216
217 // check for trailing padding
218 private void checkGroupSize(GroupLayout gl, long maxUnpaddedOffset) {
219 long expectedSize = Utils.alignUp(maxUnpaddedOffset, gl.byteAlignment());
220 if (gl.byteSize() != expectedSize) {
221 throw new IllegalArgumentException("Layout '" + gl + "' has unexpected size: "
222 + gl.byteSize() + " != " + expectedSize);
223 }
224 }
225
226 // checks both that there is no excess padding between 'memberLayout' and
227 // the previous layout
228 private void checkMemberOffset(StructLayout parent, MemoryLayout memberLayout,
229 long lastUnpaddedOffset, long offset) {
230 long expectedOffset = Utils.alignUp(lastUnpaddedOffset, memberLayout.byteAlignment());
231 if (expectedOffset != offset) {
232 throw new IllegalArgumentException("Member layout '" + memberLayout + "', of '" + parent + "'" +
233 " found at unexpected offset: " + offset + " != " + expectedOffset);
234 }
235 }
236
237 private void checkSupported(ValueLayout valueLayout) {
238 valueLayout = valueLayout.withoutName();
239 if (valueLayout instanceof AddressLayout addressLayout) {
240 valueLayout = addressLayout.withoutTargetLayout();
241 }
242 if (!CANONICAL_LAYOUTS_CACHE.contains(valueLayout.withoutName())) {
243 throw new IllegalArgumentException("Unsupported layout: " + valueLayout);
244 }
245 }
246
247 private void checkHasNaturalAlignment(MemoryLayout layout) {
248 if (!((AbstractLayout<?>) layout).hasNaturalAlignment()) {
249 throw new IllegalArgumentException("Layout alignment must be natural alignment: " + layout);
250 }
251 }
252
253 private static MemoryLayout stripNames(MemoryLayout ml) {
254 // we don't care about transferring alignment and byte order here
255 // since the linker already restricts those such that they will always be the same
256 return switch (ml) {
257 case StructLayout sl -> MemoryLayout.structLayout(stripNames(sl.memberLayouts()));
258 case UnionLayout ul -> MemoryLayout.unionLayout(stripNames(ul.memberLayouts()));
259 case SequenceLayout sl -> MemoryLayout.sequenceLayout(sl.elementCount(), stripNames(sl.elementLayout()));
260 case AddressLayout al -> al.targetLayout()
261 .map(tl -> al.withoutName().withTargetLayout(stripNames(tl)))
262 .orElseGet(al::withoutName);
263 default -> ml.withoutName(); // ValueLayout and PaddingLayout
264 };
265 }
266
267 private static MemoryLayout[] stripNames(List<MemoryLayout> layouts) {
268 return layouts.stream()
269 .map(AbstractLinker::stripNames)
270 .toArray(MemoryLayout[]::new);
271 }
272
273 private static FunctionDescriptor stripNames(FunctionDescriptor function) {
274 return function.returnLayout()
275 .map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts())))
276 .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts())));
277 }
278 }
|