37 import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker;
38 import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
39 import jdk.internal.foreign.abi.s390.linux.LinuxS390Linker;
40 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
41 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
42 import jdk.internal.vm.annotation.ForceInline;
43
44 import java.lang.foreign.AddressLayout;
45 import java.lang.foreign.Arena;
46 import java.lang.foreign.Linker;
47 import java.lang.foreign.FunctionDescriptor;
48 import java.lang.foreign.GroupLayout;
49 import java.lang.foreign.MemoryLayout;
50 import java.lang.foreign.MemorySegment;
51 import java.lang.foreign.MemorySegment.Scope;
52 import java.lang.foreign.SegmentAllocator;
53 import java.lang.foreign.ValueLayout;
54 import java.lang.invoke.MethodHandle;
55 import java.lang.invoke.MethodHandles;
56 import java.lang.invoke.MethodType;
57 import java.lang.invoke.VarHandle;
58 import java.lang.ref.Reference;
59 import java.nio.ByteOrder;
60 import java.nio.charset.StandardCharsets;
61 import java.util.Arrays;
62 import java.util.Map;
63 import java.util.Objects;
64 import java.util.stream.Collectors;
65 import java.util.stream.IntStream;
66
67 import static java.lang.foreign.ValueLayout.*;
68 import static java.lang.invoke.MethodHandles.*;
69 import static java.lang.invoke.MethodType.methodType;
70
71 public final class SharedUtils {
72
73 private SharedUtils() {
74 }
75
76 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
77 private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
78
79 private static final MethodHandle MH_ALLOC_BUFFER;
80 private static final MethodHandle MH_BUFFER_COPY;
81 private static final MethodHandle MH_REACHABILITY_FENCE;
82 public static final MethodHandle MH_CHECK_SYMBOL;
83 private static final MethodHandle MH_CHECK_CAPTURE_SEGMENT;
84
85 public static final AddressLayout C_POINTER = ADDRESS
86 .withTargetLayout(MemoryLayout.sequenceLayout(JAVA_BYTE));
87
88 public static final Arena DUMMY_ARENA = new Arena() {
89 @Override
90 public Scope scope() {
91 throw new UnsupportedOperationException();
92 }
93
94 @Override
95 public MemorySegment allocate(long byteSize) {
96 throw new UnsupportedOperationException();
97 }
98
99 @Override
100 public void close() {
101 // do nothing
102 }
103 };
104
105 static {
106 try {
107 MethodHandles.Lookup lookup = MethodHandles.lookup();
108 MH_ALLOC_BUFFER = lookup.findVirtual(SegmentAllocator.class, "allocate",
109 methodType(MemorySegment.class, MemoryLayout.class));
110 MH_BUFFER_COPY = lookup.findStatic(SharedUtils.class, "bufferCopy",
111 methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class));
112 MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence",
113 methodType(void.class, Object.class));
114 MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol",
115 methodType(void.class, MemorySegment.class));
116 MH_CHECK_CAPTURE_SEGMENT = lookup.findStatic(SharedUtils.class, "checkCaptureSegment",
117 methodType(MemorySegment.class, MemorySegment.class));
118 } catch (ReflectiveOperationException e) {
119 throw new BootstrapMethodError(e);
120 }
121 }
122
123 // this allocator should be used when no allocation is expected
124 public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> {
125 throw new IllegalStateException("Cannot get here");
126 };
127
128 public static long alignUp(long addr, long alignment) {
129 return ((addr - 1) | (alignment - 1)) + 1;
130 }
131
132 /**
133 * Takes a MethodHandle that takes an input buffer as a first argument (a MemorySegment), and returns nothing,
134 * and adapts it to return a MemorySegment, by allocating a MemorySegment for the input
135 * buffer, calling the target MethodHandle, and then returning the allocated MemorySegment.
136 *
137 * This allows viewing a MethodHandle that makes use of in memory return (IMR) as a MethodHandle that just returns
138 * a MemorySegment without requiring a pre-allocated buffer as an explicit input.
139 *
140 * @param handle the target handle to adapt
141 * @param cDesc the function descriptor of the native function (with actual return layout)
142 * @return the adapted handle
143 */
144 public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc, CallingSequence sequence) {
145 if (handle.type().returnType() != void.class)
146 throw new IllegalArgumentException("return expected to be void for in memory returns: " + handle.type());
147 int imrAddrIdx = sequence.numLeadingParams();
148 if (handle.type().parameterType(imrAddrIdx) != MemorySegment.class)
149 throw new IllegalArgumentException("MemorySegment expected as third param: " + handle.type());
150 if (cDesc.returnLayout().isEmpty())
151 throw new IllegalArgumentException("Return layout needed: " + cDesc);
234
235 throw new IllegalArgumentException("No layout for size: " + size + " isFloat=" + useFloat);
236 }
237
238 public static Linker getSystemLinker() {
239 return switch (CABI.current()) {
240 case WIN_64 -> Windowsx64Linker.getInstance();
241 case SYS_V -> SysVx64Linker.getInstance();
242 case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
243 case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
244 case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
245 case LINUX_PPC_64 -> LinuxPPC64Linker.getInstance();
246 case LINUX_PPC_64_LE -> LinuxPPC64leLinker.getInstance();
247 case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
248 case LINUX_S390 -> LinuxS390Linker.getInstance();
249 case FALLBACK -> FallbackLinker.getInstance();
250 case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker");
251 };
252 }
253
254 public static String toJavaStringInternal(MemorySegment segment, long start) {
255 int len = strlen(segment, start);
256 byte[] bytes = new byte[len];
257 MemorySegment.copy(segment, JAVA_BYTE, start, bytes, 0, len);
258 return new String(bytes, StandardCharsets.UTF_8);
259 }
260
261 private static int strlen(MemorySegment segment, long start) {
262 // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int)
263 for (int offset = 0; offset >= 0; offset++) {
264 byte curr = segment.get(JAVA_BYTE, start + offset);
265 if (curr == 0) {
266 return offset;
267 }
268 }
269 throw new IllegalArgumentException("String too large");
270 }
271
272 static Map<VMStorage, Integer> indexMap(Binding.Move[] moves) {
273 return IntStream.range(0, moves.length)
274 .boxed()
275 .collect(Collectors.toMap(i -> moves[i].storage(), i -> i));
276 }
277
278 static MethodHandle mergeArguments(MethodHandle mh, int sourceIndex, int destIndex) {
279 MethodType oldType = mh.type();
280 Class<?> sourceType = oldType.parameterType(sourceIndex);
281 Class<?> destType = oldType.parameterType(destIndex);
282 if (sourceType != destType) {
283 // TODO meet?
284 throw new IllegalArgumentException("Parameter types differ: " + sourceType + " != " + destType);
285 }
286 MethodType newType = oldType.dropParameterTypes(destIndex, destIndex + 1);
287 int[] reorder = new int[oldType.parameterCount()];
288 if (destIndex < sourceIndex) {
289 sourceIndex--;
290 }
291 for (int i = 0, index = 0; i < reorder.length; i++) {
417 return new Arena() {
418 final Arena arena = Arena.ofConfined();
419
420 @Override
421 public Scope scope() {
422 return arena.scope();
423 }
424
425 @Override
426 public void close() {
427 arena.close();
428 }
429
430 @Override
431 public MemorySegment allocate(long byteSize, long byteAlignment) {
432 throw new UnsupportedOperationException();
433 }
434 };
435 }
436
437 public static final class SimpleVaArg {
438 public final MemoryLayout layout;
439 public final Object value;
440
441 public SimpleVaArg(MemoryLayout layout, Object value) {
442 this.layout = layout;
443 this.value = value;
444 }
445
446 public VarHandle varHandle() {
447 return layout.varHandle();
448 }
449 }
450
451 static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
452 // use VH_LONG for integers to zero out the whole register in the process
453 if (type == long.class) {
454 ptr.set(JAVA_LONG_UNALIGNED, 0, (long) o);
455 } else if (type == int.class) {
456 ptr.set(JAVA_LONG_UNALIGNED, 0, (int) o);
457 } else if (type == short.class) {
458 ptr.set(JAVA_LONG_UNALIGNED, 0, (short) o);
459 } else if (type == char.class) {
460 ptr.set(JAVA_LONG_UNALIGNED, 0, (char) o);
461 } else if (type == byte.class) {
462 ptr.set(JAVA_LONG_UNALIGNED, 0, (byte) o);
463 } else if (type == float.class) {
464 ptr.set(JAVA_FLOAT_UNALIGNED, 0, (float) o);
465 } else if (type == double.class) {
466 ptr.set(JAVA_DOUBLE_UNALIGNED, 0, (double) o);
467 } else if (type == boolean.class) {
468 boolean b = (boolean)o;
469 ptr.set(JAVA_LONG_UNALIGNED, 0, b ? (long)1 : (long)0);
470 } else {
498 if (type == long.class) {
499 return ptr.get(JAVA_LONG_UNALIGNED, offset);
500 } else if (type == int.class) {
501 return ptr.get(JAVA_INT_UNALIGNED, offset);
502 } else if (type == short.class) {
503 return ptr.get(JAVA_SHORT_UNALIGNED, offset);
504 } else if (type == char.class) {
505 return ptr.get(JAVA_CHAR_UNALIGNED, offset);
506 } else if (type == byte.class) {
507 return ptr.get(JAVA_BYTE, offset);
508 } else if (type == float.class) {
509 return ptr.get(JAVA_FLOAT_UNALIGNED, offset);
510 } else if (type == double.class) {
511 return ptr.get(JAVA_DOUBLE_UNALIGNED, offset);
512 } else if (type == boolean.class) {
513 return ptr.get(JAVA_BOOLEAN, offset);
514 } else {
515 throw new IllegalArgumentException("Unsupported carrier: " + type);
516 }
517 }
518 }
|
37 import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker;
38 import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
39 import jdk.internal.foreign.abi.s390.linux.LinuxS390Linker;
40 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
41 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
42 import jdk.internal.vm.annotation.ForceInline;
43
44 import java.lang.foreign.AddressLayout;
45 import java.lang.foreign.Arena;
46 import java.lang.foreign.Linker;
47 import java.lang.foreign.FunctionDescriptor;
48 import java.lang.foreign.GroupLayout;
49 import java.lang.foreign.MemoryLayout;
50 import java.lang.foreign.MemorySegment;
51 import java.lang.foreign.MemorySegment.Scope;
52 import java.lang.foreign.SegmentAllocator;
53 import java.lang.foreign.ValueLayout;
54 import java.lang.invoke.MethodHandle;
55 import java.lang.invoke.MethodHandles;
56 import java.lang.invoke.MethodType;
57 import java.lang.ref.Reference;
58 import java.nio.ByteOrder;
59 import java.util.Arrays;
60 import java.util.Map;
61 import java.util.Objects;
62 import java.util.stream.Collectors;
63 import java.util.stream.IntStream;
64
65 import static java.lang.foreign.ValueLayout.*;
66 import static java.lang.invoke.MethodHandles.*;
67 import static java.lang.invoke.MethodType.methodType;
68
69 public final class SharedUtils {
70
71 private SharedUtils() {
72 }
73
74 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
75 private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
76
77 private static final MethodHandle MH_ALLOC_BUFFER;
78 private static final MethodHandle MH_BUFFER_COPY;
79 private static final MethodHandle MH_REACHABILITY_FENCE;
80 public static final MethodHandle MH_CHECK_SYMBOL;
81 private static final MethodHandle MH_CHECK_CAPTURE_SEGMENT;
82
83 public static final AddressLayout C_POINTER = ADDRESS
84 .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE));
85
86 public static final Arena DUMMY_ARENA = new Arena() {
87 @Override
88 public Scope scope() {
89 throw new UnsupportedOperationException();
90 }
91
92 @Override
93 public MemorySegment allocate(long byteSize, long byteAlignment) {
94 throw new UnsupportedOperationException();
95 }
96
97 @Override
98 public void close() {
99 // do nothing
100 }
101 };
102
103 static {
104 try {
105 MethodHandles.Lookup lookup = MethodHandles.lookup();
106 MH_ALLOC_BUFFER = lookup.findVirtual(SegmentAllocator.class, "allocate",
107 methodType(MemorySegment.class, MemoryLayout.class));
108 MH_BUFFER_COPY = lookup.findStatic(SharedUtils.class, "bufferCopy",
109 methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class));
110 MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence",
111 methodType(void.class, Object.class));
112 MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol",
113 methodType(void.class, MemorySegment.class));
114 MH_CHECK_CAPTURE_SEGMENT = lookup.findStatic(SharedUtils.class, "checkCaptureSegment",
115 methodType(MemorySegment.class, MemorySegment.class));
116 } catch (ReflectiveOperationException e) {
117 throw new BootstrapMethodError(e);
118 }
119 }
120
121 // this allocator should be used when no allocation is expected
122 public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> {
123 throw new IllegalStateException("Cannot get here");
124 };
125
126 public static long alignUp(long addr, long alignment) {
127 return ((addr - 1) | (alignment - 1)) + 1;
128 }
129
130 public static long remainsToAlignment(long addr, long alignment) {
131 return alignUp(addr, alignment) - addr;
132 }
133
134 /**
135 * Takes a MethodHandle that takes an input buffer as a first argument (a MemorySegment), and returns nothing,
136 * and adapts it to return a MemorySegment, by allocating a MemorySegment for the input
137 * buffer, calling the target MethodHandle, and then returning the allocated MemorySegment.
138 *
139 * This allows viewing a MethodHandle that makes use of in memory return (IMR) as a MethodHandle that just returns
140 * a MemorySegment without requiring a pre-allocated buffer as an explicit input.
141 *
142 * @param handle the target handle to adapt
143 * @param cDesc the function descriptor of the native function (with actual return layout)
144 * @return the adapted handle
145 */
146 public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc, CallingSequence sequence) {
147 if (handle.type().returnType() != void.class)
148 throw new IllegalArgumentException("return expected to be void for in memory returns: " + handle.type());
149 int imrAddrIdx = sequence.numLeadingParams();
150 if (handle.type().parameterType(imrAddrIdx) != MemorySegment.class)
151 throw new IllegalArgumentException("MemorySegment expected as third param: " + handle.type());
152 if (cDesc.returnLayout().isEmpty())
153 throw new IllegalArgumentException("Return layout needed: " + cDesc);
236
237 throw new IllegalArgumentException("No layout for size: " + size + " isFloat=" + useFloat);
238 }
239
240 public static Linker getSystemLinker() {
241 return switch (CABI.current()) {
242 case WIN_64 -> Windowsx64Linker.getInstance();
243 case SYS_V -> SysVx64Linker.getInstance();
244 case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
245 case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
246 case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
247 case LINUX_PPC_64 -> LinuxPPC64Linker.getInstance();
248 case LINUX_PPC_64_LE -> LinuxPPC64leLinker.getInstance();
249 case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
250 case LINUX_S390 -> LinuxS390Linker.getInstance();
251 case FALLBACK -> FallbackLinker.getInstance();
252 case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker");
253 };
254 }
255
256 static Map<VMStorage, Integer> indexMap(Binding.Move[] moves) {
257 return IntStream.range(0, moves.length)
258 .boxed()
259 .collect(Collectors.toMap(i -> moves[i].storage(), i -> i));
260 }
261
262 static MethodHandle mergeArguments(MethodHandle mh, int sourceIndex, int destIndex) {
263 MethodType oldType = mh.type();
264 Class<?> sourceType = oldType.parameterType(sourceIndex);
265 Class<?> destType = oldType.parameterType(destIndex);
266 if (sourceType != destType) {
267 // TODO meet?
268 throw new IllegalArgumentException("Parameter types differ: " + sourceType + " != " + destType);
269 }
270 MethodType newType = oldType.dropParameterTypes(destIndex, destIndex + 1);
271 int[] reorder = new int[oldType.parameterCount()];
272 if (destIndex < sourceIndex) {
273 sourceIndex--;
274 }
275 for (int i = 0, index = 0; i < reorder.length; i++) {
401 return new Arena() {
402 final Arena arena = Arena.ofConfined();
403
404 @Override
405 public Scope scope() {
406 return arena.scope();
407 }
408
409 @Override
410 public void close() {
411 arena.close();
412 }
413
414 @Override
415 public MemorySegment allocate(long byteSize, long byteAlignment) {
416 throw new UnsupportedOperationException();
417 }
418 };
419 }
420
421 static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
422 // use VH_LONG for integers to zero out the whole register in the process
423 if (type == long.class) {
424 ptr.set(JAVA_LONG_UNALIGNED, 0, (long) o);
425 } else if (type == int.class) {
426 ptr.set(JAVA_LONG_UNALIGNED, 0, (int) o);
427 } else if (type == short.class) {
428 ptr.set(JAVA_LONG_UNALIGNED, 0, (short) o);
429 } else if (type == char.class) {
430 ptr.set(JAVA_LONG_UNALIGNED, 0, (char) o);
431 } else if (type == byte.class) {
432 ptr.set(JAVA_LONG_UNALIGNED, 0, (byte) o);
433 } else if (type == float.class) {
434 ptr.set(JAVA_FLOAT_UNALIGNED, 0, (float) o);
435 } else if (type == double.class) {
436 ptr.set(JAVA_DOUBLE_UNALIGNED, 0, (double) o);
437 } else if (type == boolean.class) {
438 boolean b = (boolean)o;
439 ptr.set(JAVA_LONG_UNALIGNED, 0, b ? (long)1 : (long)0);
440 } else {
468 if (type == long.class) {
469 return ptr.get(JAVA_LONG_UNALIGNED, offset);
470 } else if (type == int.class) {
471 return ptr.get(JAVA_INT_UNALIGNED, offset);
472 } else if (type == short.class) {
473 return ptr.get(JAVA_SHORT_UNALIGNED, offset);
474 } else if (type == char.class) {
475 return ptr.get(JAVA_CHAR_UNALIGNED, offset);
476 } else if (type == byte.class) {
477 return ptr.get(JAVA_BYTE, offset);
478 } else if (type == float.class) {
479 return ptr.get(JAVA_FLOAT_UNALIGNED, offset);
480 } else if (type == double.class) {
481 return ptr.get(JAVA_DOUBLE_UNALIGNED, offset);
482 } else if (type == boolean.class) {
483 return ptr.get(JAVA_BOOLEAN, offset);
484 } else {
485 throw new IllegalArgumentException("Unsupported carrier: " + type);
486 }
487 }
488
489 public static Map<String, MemoryLayout> canonicalLayouts(ValueLayout longLayout, ValueLayout sizetLayout, ValueLayout wchartLayout) {
490 return Map.ofEntries(
491 // specified canonical layouts
492 Map.entry("bool", ValueLayout.JAVA_BOOLEAN),
493 Map.entry("char", ValueLayout.JAVA_BYTE),
494 Map.entry("short", ValueLayout.JAVA_SHORT),
495 Map.entry("int", ValueLayout.JAVA_INT),
496 Map.entry("float", ValueLayout.JAVA_FLOAT),
497 Map.entry("long", longLayout),
498 Map.entry("long long", ValueLayout.JAVA_LONG),
499 Map.entry("double", ValueLayout.JAVA_DOUBLE),
500 Map.entry("void*", ValueLayout.ADDRESS),
501 Map.entry("size_t", sizetLayout),
502 Map.entry("wchar_t", wchartLayout),
503 // unspecified size-dependent layouts
504 Map.entry("int8_t", ValueLayout.JAVA_BYTE),
505 Map.entry("int16_t", ValueLayout.JAVA_SHORT),
506 Map.entry("int32_t", ValueLayout.JAVA_INT),
507 Map.entry("int64_t", ValueLayout.JAVA_LONG),
508 // unspecified JNI layouts
509 Map.entry("jboolean", ValueLayout.JAVA_BOOLEAN),
510 Map.entry("jchar", ValueLayout.JAVA_CHAR),
511 Map.entry("jbyte", ValueLayout.JAVA_BYTE),
512 Map.entry("jshort", ValueLayout.JAVA_SHORT),
513 Map.entry("jint", ValueLayout.JAVA_INT),
514 Map.entry("jlong", ValueLayout.JAVA_LONG),
515 Map.entry("jfloat", ValueLayout.JAVA_FLOAT),
516 Map.entry("jdouble", ValueLayout.JAVA_DOUBLE)
517 );
518 }
519 }
|