1 /*
  2  * Copyright (c) 2020, 2021, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 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;
 26 
 27 import jdk.incubator.foreign.Addressable;
 28 import jdk.incubator.foreign.FunctionDescriptor;
 29 import jdk.incubator.foreign.GroupLayout;
 30 import jdk.incubator.foreign.MemoryAccess;
 31 import jdk.incubator.foreign.MemoryAddress;
 32 import jdk.incubator.foreign.MemoryHandles;
 33 import jdk.incubator.foreign.MemoryLayout;
 34 import jdk.incubator.foreign.MemorySegment;
 35 import jdk.incubator.foreign.ResourceScope;
 36 import jdk.incubator.foreign.SegmentAllocator;
 37 import jdk.incubator.foreign.SequenceLayout;
 38 import jdk.incubator.foreign.CLinker;
 39 import jdk.incubator.foreign.ValueLayout;
 40 import jdk.internal.access.JavaLangAccess;
 41 import jdk.internal.access.SharedSecrets;
 42 import jdk.internal.foreign.CABI;
 43 import jdk.internal.foreign.MemoryAddressImpl;
 44 import jdk.internal.foreign.Utils;
 45 import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
 46 import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
 47 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
 48 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
 49 
 50 import java.lang.invoke.MethodHandle;
 51 import java.lang.invoke.MethodHandles;
 52 import java.lang.invoke.MethodType;
 53 import java.lang.invoke.VarHandle;
 54 import java.lang.ref.Reference;
 55 import java.nio.charset.Charset;
 56 import java.nio.charset.StandardCharsets;
 57 import java.util.Arrays;
 58 import java.util.List;
 59 import java.util.Map;
 60 import java.util.Objects;
 61 import java.util.function.Consumer;
 62 import java.util.stream.Collectors;
 63 import java.util.stream.IntStream;
 64 
 65 import static java.lang.invoke.MethodHandles.collectArguments;
 66 import static java.lang.invoke.MethodHandles.constant;
 67 import static java.lang.invoke.MethodHandles.dropArguments;
 68 import static java.lang.invoke.MethodHandles.dropReturn;
 69 import static java.lang.invoke.MethodHandles.empty;
 70 import static java.lang.invoke.MethodHandles.filterArguments;
 71 import static java.lang.invoke.MethodHandles.identity;
 72 import static java.lang.invoke.MethodHandles.insertArguments;
 73 import static java.lang.invoke.MethodHandles.permuteArguments;
 74 import static java.lang.invoke.MethodHandles.tryFinally;
 75 import static java.lang.invoke.MethodType.methodType;
 76 import static jdk.incubator.foreign.CLinker.*;
 77 
 78 public class SharedUtils {
 79 
 80     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 81 
 82     private static final MethodHandle MH_ALLOC_BUFFER;
 83     private static final MethodHandle MH_BASEADDRESS;
 84     private static final MethodHandle MH_BUFFER_COPY;
 85     private static final MethodHandle MH_MAKE_CONTEXT_NO_ALLOCATOR;
 86     private static final MethodHandle MH_MAKE_CONTEXT_BOUNDED_ALLOCATOR;
 87     private static final MethodHandle MH_CLOSE_CONTEXT;
 88     private static final MethodHandle MH_REACHBILITY_FENCE;
 89     private static final MethodHandle MH_HANDLE_UNCAUGHT_EXCEPTION;
 90 
 91     static {
 92         try {
 93             MethodHandles.Lookup lookup = MethodHandles.lookup();
 94             MH_ALLOC_BUFFER = lookup.findVirtual(SegmentAllocator.class, "allocate",
 95                     methodType(MemorySegment.class, MemoryLayout.class));
 96             MH_BASEADDRESS = lookup.findVirtual(MemorySegment.class, "address",
 97                     methodType(MemoryAddress.class));
 98             MH_BUFFER_COPY = lookup.findStatic(SharedUtils.class, "bufferCopy",
 99                     methodType(MemoryAddress.class, MemoryAddress.class, MemorySegment.class));
100             MH_MAKE_CONTEXT_NO_ALLOCATOR = lookup.findStatic(Binding.Context.class, "ofScope",
101                     methodType(Binding.Context.class));
102             MH_MAKE_CONTEXT_BOUNDED_ALLOCATOR = lookup.findStatic(Binding.Context.class, "ofBoundedAllocator",
103                     methodType(Binding.Context.class, long.class));
104             MH_CLOSE_CONTEXT = lookup.findVirtual(Binding.Context.class, "close",
105                     methodType(void.class));
106             MH_REACHBILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence",
107                     methodType(void.class, Object.class));
108             MH_HANDLE_UNCAUGHT_EXCEPTION = lookup.findStatic(SharedUtils.class, "handleUncaughtException",
109                     methodType(void.class, Throwable.class));
110         } catch (ReflectiveOperationException e) {
111             throw new BootstrapMethodError(e);
112         }
113     }
114 
115     // this allocator should be used when no allocation is expected
116     public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> { throw new IllegalStateException("Cannot get here"); };
117 
118     /**
119      * Align the specified type from a given address
120      * @return The address the data should be at based on alignment requirement
121      */
122     public static long align(MemoryLayout t, boolean isVar, long addr) {
123         return alignUp(addr, alignment(t, isVar));
124     }
125 
126     public static long alignUp(long addr, long alignment) {
127         return ((addr - 1) | (alignment - 1)) + 1;
128     }
129 
130     /**
131      * The alignment requirement for a given type
132      * @param isVar indicate if the type is a standalone variable. This change how
133      * array is aligned. for example.
134      */
135     public static long alignment(MemoryLayout t, boolean isVar) {
136         if (t instanceof ValueLayout) {
137             return alignmentOfScalar((ValueLayout) t);
138         } else if (t instanceof SequenceLayout) {
139             // when array is used alone
140             return alignmentOfArray((SequenceLayout) t, isVar);
141         } else if (t instanceof GroupLayout) {
142             return alignmentOfContainer((GroupLayout) t);
143         } else if (t.isPadding()) {
144             return 1;
145         } else {
146             throw new IllegalArgumentException("Invalid type: " + t);
147         }
148     }
149 
150     private static long alignmentOfScalar(ValueLayout st) {
151         return st.byteSize();
152     }
153 
154     private static long alignmentOfArray(SequenceLayout ar, boolean isVar) {
155         if (ar.elementCount().orElseThrow() == 0) {
156             // VLA or incomplete
157             return 16;
158         } else if ((ar.byteSize()) >= 16 && isVar) {
159             return 16;
160         } else {
161             // align as element type
162             MemoryLayout elementType = ar.elementLayout();
163             return alignment(elementType, false);
164         }
165     }
166 
167     private static long alignmentOfContainer(GroupLayout ct) {
168         // Most strict member
169         return ct.memberLayouts().stream().mapToLong(t -> alignment(t, false)).max().orElse(1);
170     }
171 
172     /**
173      * Takes a MethodHandle that takes an input buffer as a first argument (a MemoryAddress), and returns nothing,
174      * and adapts it to return a MemorySegment, by allocating a MemorySegment for the input
175      * buffer, calling the target MethodHandle, and then returning the allocated MemorySegment.
176      *
177      * This allows viewing a MethodHandle that makes use of in memory return (IMR) as a MethodHandle that just returns
178      * a MemorySegment without requiring a pre-allocated buffer as an explicit input.
179      *
180      * @param handle the target handle to adapt
181      * @param cDesc the function descriptor of the native function (with actual return layout)
182      * @return the adapted handle
183      */
184     public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc) {
185         if (handle.type().returnType() != void.class)
186             throw new IllegalArgumentException("return expected to be void for in memory returns: " + handle.type());
187         if (handle.type().parameterType(2) != MemoryAddress.class)
188             throw new IllegalArgumentException("MemoryAddress expected as third param: " + handle.type());
189         if (cDesc.returnLayout().isEmpty())
190             throw new IllegalArgumentException("Return layout needed: " + cDesc);
191 
192         MethodHandle ret = identity(MemorySegment.class); // (MemorySegment) MemorySegment
193         handle = collectArguments(ret, 1, handle); // (MemorySegment, Addressable, SegmentAllocator, MemoryAddress, ...) MemorySegment
194         handle = collectArguments(handle, 3, MH_BASEADDRESS); // (MemorySegment, Addressable, SegmentAllocator, MemorySegment, ...) MemorySegment
195         handle = mergeArguments(handle, 0, 3);  // (MemorySegment, Addressable, SegmentAllocator, ...) MemorySegment
196         handle = collectArguments(handle, 0, insertArguments(MH_ALLOC_BUFFER, 1, cDesc.returnLayout().get())); // (SegmentAllocator, Addressable, SegmentAllocator, ...) MemoryAddress
197         handle = mergeArguments(handle, 0, 2);  // (SegmentAllocator, Addressable, ...) MemoryAddress
198         handle = swapArguments(handle, 0, 1); // (Addressable, SegmentAllocator, ...) MemoryAddress
199         return handle;
200     }
201 
202     /**
203      * Takes a MethodHandle that returns a MemorySegment, and adapts it to take an input buffer as a first argument
204      * (a MemoryAddress), and upon invocation, copies the contents of the returned MemorySegment into the input buffer
205      * passed as the first argument.
206      *
207      * @param target the target handle to adapt
208      * @return the adapted handle
209      */
210     public static MethodHandle adaptUpcallForIMR(MethodHandle target, boolean dropReturn) {
211         if (target.type().returnType() != MemorySegment.class)
212             throw new IllegalArgumentException("Must return MemorySegment for IMR");
213 
214         target = collectArguments(MH_BUFFER_COPY, 1, target); // (MemoryAddress, ...) MemoryAddress
215 
216         if (dropReturn) { // no handling for return value, need to drop it
217             target = dropReturn(target);
218         }
219 
220         return target;
221     }
222 
223     private static MemoryAddress bufferCopy(MemoryAddress dest, MemorySegment buffer) {
224         MemoryAddressImpl.ofLongUnchecked(dest.toRawLongValue(), buffer.byteSize()).copyFrom(buffer);
225         return dest;
226     }
227 
228     public static void checkCompatibleType(Class<?> carrier, MemoryLayout layout, long addressSize) {
229         if (carrier.isPrimitive()) {
230             Utils.checkPrimitiveCarrierCompat(carrier, layout);
231         } else if (carrier == MemoryAddress.class) {
232             Utils.checkLayoutType(layout, ValueLayout.class);
233             if (layout.bitSize() != addressSize)
234                 throw new IllegalArgumentException("Address size mismatch: " + addressSize + " != " + layout.bitSize());
235         } else if (carrier == MemorySegment.class) {
236             Utils.checkLayoutType(layout, GroupLayout.class);
237         } else {
238             throw new IllegalArgumentException("Unsupported carrier: " + carrier);
239         }
240     }
241 
242     public static void checkFunctionTypes(MethodType mt, FunctionDescriptor cDesc, long addressSize) {
243         if (mt.returnType() == void.class != cDesc.returnLayout().isEmpty())
244             throw new IllegalArgumentException("Return type mismatch: " + mt + " != " + cDesc);
245         List<MemoryLayout> argLayouts = cDesc.argumentLayouts();
246         if (mt.parameterCount() != argLayouts.size())
247             throw new IllegalArgumentException("Arity mismatch: " + mt + " != " + cDesc);
248 
249         int paramCount = mt.parameterCount();
250         for (int i = 0; i < paramCount; i++) {
251             checkCompatibleType(mt.parameterType(i), argLayouts.get(i), addressSize);
252         }
253         cDesc.returnLayout().ifPresent(rl -> checkCompatibleType(mt.returnType(), rl, addressSize));
254     }
255 
256     public static Class<?> primitiveCarrierForSize(long size, boolean useFloat) {
257         if (useFloat) {
258             if (size == 4) {
259                 return float.class;
260             } else if (size == 8) {
261                 return double.class;
262             }
263         } else {
264             if (size == 1) {
265                 return byte.class;
266             } else if (size == 2) {
267                 return short.class;
268             } else if (size <= 4) {
269                 return int.class;
270             } else if (size <= 8) {
271                 return long.class;
272             }
273         }
274 
275         throw new IllegalArgumentException("No type for size: " + size + " isFloat=" + useFloat);
276     }
277 
278     public static CLinker getSystemLinker() {
279         return switch (CABI.current()) {
280             case Win64 -> Windowsx64Linker.getInstance();
281             case SysV -> SysVx64Linker.getInstance();
282             case LinuxAArch64 -> LinuxAArch64Linker.getInstance();
283             case MacOsAArch64 -> MacOsAArch64Linker.getInstance();
284         };
285     }
286 
287     public static String toJavaStringInternal(MemorySegment segment, long start) {
288         int len = strlen(segment, start);
289         byte[] bytes = new byte[len];
290         MemorySegment.ofArray(bytes)
291                 .copyFrom(segment.asSlice(start, len));
292         return new String(bytes, StandardCharsets.UTF_8);
293     }
294 
295     private static int strlen(MemorySegment segment, long start) {
296         // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int)
297         for (int offset = 0; offset >= 0; offset++) {
298             byte curr = MemoryAccess.getByteAtOffset(segment, start + offset);
299             if (curr == 0) {
300                 return offset;
301             }
302         }
303         throw new IllegalArgumentException("String too large");
304     }
305 
306     static long bufferCopySize(CallingSequence callingSequence) {
307         // FIXME: > 16 bytes alignment might need extra space since the
308         // starting address of the allocator might be un-aligned.
309         long size = 0;
310         for (int i = 0; i < callingSequence.argumentCount(); i++) {
311             List<Binding> bindings = callingSequence.argumentBindings(i);
312             for (Binding b : bindings) {
313                 if (b instanceof Binding.Copy) {
314                     Binding.Copy c = (Binding.Copy) b;
315                     size = Utils.alignUp(size, c.alignment());
316                     size += c.size();
317                 } else if (b instanceof Binding.Allocate) {
318                     Binding.Allocate c = (Binding.Allocate) b;
319                     size = Utils.alignUp(size, c.alignment());
320                     size += c.size();
321                 }
322             }
323         }
324         return size;
325     }
326 
327     static Map<VMStorage, Integer> indexMap(Binding.Move[] moves) {
328         return IntStream.range(0, moves.length)
329                         .boxed()
330                         .collect(Collectors.toMap(i -> moves[i].storage(), i -> i));
331     }
332 
333     static MethodHandle mergeArguments(MethodHandle mh, int sourceIndex, int destIndex) {
334         MethodType oldType = mh.type();
335         Class<?> sourceType = oldType.parameterType(sourceIndex);
336         Class<?> destType = oldType.parameterType(destIndex);
337         if (sourceType != destType) {
338             // TODO meet?
339             throw new IllegalArgumentException("Parameter types differ: " + sourceType + " != " + destType);
340         }
341         MethodType newType = oldType.dropParameterTypes(destIndex, destIndex + 1);
342         int[] reorder = new int[oldType.parameterCount()];
343         assert destIndex > sourceIndex;
344         for (int i = 0, index = 0; i < reorder.length; i++) {
345             if (i != destIndex) {
346                 reorder[i] = index++;
347             } else {
348                 reorder[i] = sourceIndex;
349             }
350         }
351         return permuteArguments(mh, newType, reorder);
352     }
353 
354 
355     static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) {
356         MethodType mtype = mh.type();
357         int[] perms = new int[mtype.parameterCount()];
358         MethodType swappedType = MethodType.methodType(mtype.returnType());
359         for (int i = 0 ; i < perms.length ; i++) {
360             int dst = i;
361             if (i == firstArg) dst = secondArg;
362             if (i == secondArg) dst = firstArg;
363             perms[i] = dst;
364             swappedType = swappedType.appendParameterTypes(mtype.parameterType(dst));
365         }
366         return permuteArguments(mh, swappedType, perms);
367     }
368 
369     private static MethodHandle reachabilityFenceHandle(Class<?> type) {
370         return MH_REACHBILITY_FENCE.asType(MethodType.methodType(void.class, type));
371     }
372 
373     static void handleUncaughtException(Throwable t) {
374         if (t != null) {
375             t.printStackTrace();
376             JLA.exit(1);
377         }
378     }
379 
380     static MethodHandle wrapWithAllocator(MethodHandle specializedHandle,
381                                           int allocatorPos, long bufferCopySize,
382                                           boolean upcall) {
383         // insert try-finally to close the NativeScope used for Binding.Copy
384         MethodHandle closer;
385         int insertPos;
386         if (specializedHandle.type().returnType() == void.class) {
387             if (!upcall) {
388                 closer = empty(methodType(void.class, Throwable.class)); // (Throwable) -> void
389             } else {
390                 closer = MH_HANDLE_UNCAUGHT_EXCEPTION;
391             }
392             insertPos = 1;
393         } else {
394             closer = identity(specializedHandle.type().returnType()); // (V) -> V
395             closer = dropArguments(closer, 0, Throwable.class); // (Throwable, V) -> V
396             insertPos = 2;
397         }
398 
399         // downcalls get the leading Addressable/SegmentAllocator param as well
400         if (!upcall) {
401             closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(Addressable.class));
402             closer = dropArguments(closer, insertPos++, SegmentAllocator.class); // (Throwable, V?, Addressable, SegmentAllocator) -> V/void
403         }
404 
405         closer = collectArguments(closer, insertPos++, MH_CLOSE_CONTEXT); // (Throwable, V?, Addressable?, BindingContext) -> V/void
406 
407         if (!upcall) {
408             // now for each Addressable parameter, add a reachability fence
409             MethodType specType = specializedHandle.type();
410             // skip 3 for address, segment allocator, and binding context
411             for (int i = 3; i < specType.parameterCount(); i++) {
412                 Class<?> param = specType.parameterType(i);
413                 if (Addressable.class.isAssignableFrom(param)) {
414                     closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(param));
415                 } else {
416                     closer = dropArguments(closer, insertPos++, param);
417                 }
418             }
419         }
420 
421         MethodHandle contextFactory;
422 
423         if (bufferCopySize > 0) {
424             contextFactory = MethodHandles.insertArguments(MH_MAKE_CONTEXT_BOUNDED_ALLOCATOR, 0, bufferCopySize);
425         } else if (upcall) {
426             contextFactory = MH_MAKE_CONTEXT_NO_ALLOCATOR;
427         } else {
428             // this path is probably never used now, since ProgrammableInvoker never calls this routine with bufferCopySize == 0
429             contextFactory = constant(Binding.Context.class, Binding.Context.DUMMY);
430         }
431 
432         specializedHandle = tryFinally(specializedHandle, closer);
433         specializedHandle = collectArguments(specializedHandle, allocatorPos, contextFactory);
434         return specializedHandle;
435     }
436 
437     // lazy init MH_ALLOC and MH_FREE handles
438     private static class AllocHolder {
439 
440         private static final CLinker SYS_LINKER = getSystemLinker();
441 
442         static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("malloc").get(),
443                         MethodType.methodType(MemoryAddress.class, long.class),
444                 FunctionDescriptor.of(C_POINTER, C_LONG_LONG));
445 
446         static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("free").get(),
447                         MethodType.methodType(void.class, MemoryAddress.class),
448                 FunctionDescriptor.ofVoid(C_POINTER));
449     }
450 
451     public static MemoryAddress checkSymbol(Addressable symbol) {
452         return checkAddressable(symbol, "Symbol is NULL");
453     }
454 
455     public static MemoryAddress checkAddress(MemoryAddress address) {
456         return checkAddressable(address, "Address is NULL");
457     }
458 
459     private static MemoryAddress checkAddressable(Addressable symbol, String msg) {
460         Objects.requireNonNull(symbol);
461         MemoryAddress symbolAddr = symbol.address();
462         if (symbolAddr.equals(MemoryAddress.NULL))
463             throw new IllegalArgumentException("Symbol is NULL: " + symbolAddr);
464         return symbolAddr;
465     }
466 
467     public static MemoryAddress allocateMemoryInternal(long size) {
468         try {
469             return (MemoryAddress) AllocHolder.MH_MALLOC.invokeExact(size);
470         } catch (Throwable th) {
471             throw new RuntimeException(th);
472         }
473     }
474 
475     public static void freeMemoryInternal(MemoryAddress addr) {
476         try {
477             AllocHolder.MH_FREE.invokeExact(addr);
478         } catch (Throwable th) {
479             throw new RuntimeException(th);
480         }
481     }
482 
483     public static VaList newVaList(Consumer<VaList.Builder> actions, ResourceScope scope) {
484         return switch (CABI.current()) {
485             case Win64 -> Windowsx64Linker.newVaList(actions, scope);
486             case SysV -> SysVx64Linker.newVaList(actions, scope);
487             case LinuxAArch64 -> LinuxAArch64Linker.newVaList(actions, scope);
488             case MacOsAArch64 -> MacOsAArch64Linker.newVaList(actions, scope);
489         };
490     }
491 
492     public static VarHandle vhPrimitiveOrAddress(Class<?> carrier, MemoryLayout layout) {
493         return carrier == MemoryAddress.class
494             ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false)))
495             : layout.varHandle(carrier);
496     }
497 
498     public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) {
499         return switch (CABI.current()) {
500             case Win64 -> Windowsx64Linker.newVaListOfAddress(ma, scope);
501             case SysV -> SysVx64Linker.newVaListOfAddress(ma, scope);
502             case LinuxAArch64 -> LinuxAArch64Linker.newVaListOfAddress(ma, scope);
503             case MacOsAArch64 -> MacOsAArch64Linker.newVaListOfAddress(ma, scope);
504         };
505     }
506 
507     public static VaList emptyVaList() {
508         return switch (CABI.current()) {
509             case Win64 -> Windowsx64Linker.emptyVaList();
510             case SysV -> SysVx64Linker.emptyVaList();
511             case LinuxAArch64 -> LinuxAArch64Linker.emptyVaList();
512             case MacOsAArch64 -> MacOsAArch64Linker.emptyVaList();
513         };
514     }
515 
516     public static MethodType convertVaListCarriers(MethodType mt, Class<?> carrier) {
517         Class<?>[] params = new Class<?>[mt.parameterCount()];
518         for (int i = 0; i < params.length; i++) {
519             Class<?> pType = mt.parameterType(i);
520             params[i] = ((pType == VaList.class) ? carrier : pType);
521         }
522         return methodType(mt.returnType(), params);
523     }
524 
525     public static MethodHandle unboxVaLists(MethodType type, MethodHandle handle, MethodHandle unboxer) {
526         for (int i = 0; i < type.parameterCount(); i++) {
527             if (type.parameterType(i) == VaList.class) {
528                handle = filterArguments(handle, i + 1, unboxer); // +1 for leading address
529             }
530         }
531         return handle;
532     }
533 
534     public static MethodHandle boxVaLists(MethodHandle handle, MethodHandle boxer) {
535         MethodType type = handle.type();
536         for (int i = 0; i < type.parameterCount(); i++) {
537             if (type.parameterType(i) == VaList.class) {
538                handle = filterArguments(handle, i, boxer);
539             }
540         }
541         return handle;
542     }
543 
544     static void checkType(Class<?> actualType, Class<?> expectedType) {
545         if (expectedType != actualType) {
546             throw new IllegalArgumentException(
547                     String.format("Invalid operand type: %s. %s expected", actualType, expectedType));
548         }
549     }
550 
551     public static boolean isTrivial(FunctionDescriptor cDesc) {
552         return cDesc.attribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME)
553                 .map(Boolean.class::cast)
554                 .orElse(false);
555     }
556 
557     public static class SimpleVaArg {
558         public final Class<?> carrier;
559         public final MemoryLayout layout;
560         public final Object value;
561 
562         public SimpleVaArg(Class<?> carrier, MemoryLayout layout, Object value) {
563             this.carrier = carrier;
564             this.layout = layout;
565             this.value = value;
566         }
567 
568         public VarHandle varHandle() {
569             return carrier == MemoryAddress.class
570                 ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false)))
571                 : layout.varHandle(carrier);
572         }
573     }
574 
575     public static non-sealed class EmptyVaList implements VaList {
576 
577         private final MemoryAddress address;
578 
579         public EmptyVaList(MemoryAddress address) {
580             this.address = address;
581         }
582 
583         private static UnsupportedOperationException uoe() {
584             return new UnsupportedOperationException("Empty VaList");
585         }
586 
587         @Override
588         public int vargAsInt(MemoryLayout layout) {
589             throw uoe();
590         }
591 
592         @Override
593         public long vargAsLong(MemoryLayout layout) {
594             throw uoe();
595         }
596 
597         @Override
598         public double vargAsDouble(MemoryLayout layout) {
599             throw uoe();
600         }
601 
602         @Override
603         public MemoryAddress vargAsAddress(MemoryLayout layout) {
604             throw uoe();
605         }
606 
607         @Override
608         public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
609             throw uoe();
610         }
611 
612         @Override
613         public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
614             throw uoe();
615         }
616 
617         @Override
618         public void skip(MemoryLayout... layouts) {
619             throw uoe();
620         }
621 
622         @Override
623         public ResourceScope scope() {
624             return ResourceScope.globalScope();
625         }
626 
627         @Override
628         public VaList copy() {
629             return this;
630         }
631 
632         @Override
633         public MemoryAddress address() {
634             return address;
635         }
636     }
637 
638     static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
639         // use VH_LONG for integers to zero out the whole register in the process
640         if (type == long.class) {
641             MemoryAccess.setLong(ptr, (long) o);
642         } else if (type == int.class) {
643             MemoryAccess.setLong(ptr, (int) o);
644         } else if (type == short.class) {
645             MemoryAccess.setLong(ptr, (short) o);
646         } else if (type == char.class) {
647             MemoryAccess.setLong(ptr, (char) o);
648         } else if (type == byte.class) {
649             MemoryAccess.setLong(ptr, (byte) o);
650         } else if (type == float.class) {
651             MemoryAccess.setFloat(ptr, (float) o);
652         } else if (type == double.class) {
653             MemoryAccess.setDouble(ptr, (double) o);
654         } else {
655             throw new IllegalArgumentException("Unsupported carrier: " + type);
656         }
657     }
658 
659     static void write(MemorySegment ptr, Class<?> type, Object o) {
660         if (type == long.class) {
661             MemoryAccess.setLong(ptr, (long) o);
662         } else if (type == int.class) {
663             MemoryAccess.setInt(ptr, (int) o);
664         } else if (type == short.class) {
665             MemoryAccess.setShort(ptr, (short) o);
666         } else if (type == char.class) {
667             MemoryAccess.setChar(ptr, (char) o);
668         } else if (type == byte.class) {
669             MemoryAccess.setByte(ptr, (byte) o);
670         } else if (type == float.class) {
671             MemoryAccess.setFloat(ptr, (float) o);
672         } else if (type == double.class) {
673             MemoryAccess.setDouble(ptr, (double) o);
674         } else {
675             throw new IllegalArgumentException("Unsupported carrier: " + type);
676         }
677     }
678 
679     static Object read(MemorySegment ptr, Class<?> type) {
680         if (type == long.class) {
681             return MemoryAccess.getLong(ptr);
682         } else if (type == int.class) {
683             return MemoryAccess.getInt(ptr);
684         } else if (type == short.class) {
685             return MemoryAccess.getShort(ptr);
686         } else if (type == char.class) {
687             return MemoryAccess.getChar(ptr);
688         } else if (type == byte.class) {
689             return MemoryAccess.getByte(ptr);
690         } else if (type == float.class) {
691             return MemoryAccess.getFloat(ptr);
692         } else if (type == double.class) {
693             return MemoryAccess.getDouble(ptr);
694         } else {
695             throw new IllegalArgumentException("Unsupported carrier: " + type);
696         }
697     }
698 }