1 // Generated by jextract
   2 
   3 import jdk.incubator.foreign.FunctionDescriptor;
   4 import jdk.incubator.foreign.LibraryLookup;
   5 import jdk.incubator.foreign.MemoryAddress;
   6 import jdk.incubator.foreign.MemoryLayout;
   7 import jdk.incubator.foreign.MemorySegment;
   8 import jdk.incubator.foreign.SystemABI;
   9 
  10 import java.lang.invoke.MethodHandle;
  11 import java.lang.invoke.MethodHandles;
  12 import java.lang.invoke.MethodType;
  13 import java.nio.file.Files;
  14 import java.nio.file.Path;
  15 import java.nio.file.Paths;
  16 import java.util.Arrays;
  17 import java.util.Optional;
  18 
  19 import static jdk.incubator.foreign.MemoryLayouts.*;
  20 
  21 public class RuntimeHelper {
  22 
  23     private final static SystemABI ABI = SystemABI.getInstance();
  24 
  25     private final static ClassLoader LOADER = RuntimeHelper.class.getClassLoader();
  26 
  27     private final static MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup();
  28 
  29     static final LibraryLookup[] libraries(String[] libNames, String[] libPaths) {
  30         if (libNames.length == 0) {
  31             return new LibraryLookup[]{LibraryLookup.ofDefault()};
  32         } else {
  33             Path[] paths = Arrays.stream(libPaths).map(Paths::get).toArray(Path[]::new);
  34             return Arrays.stream(libNames).map(libName -> {
  35                 Optional<Path> absPath = findLibraryPath(paths, libName);
  36                 return absPath.isPresent() ?
  37                         LibraryLookup.ofPath(MH_LOOKUP, absPath.get().toString()) :
  38                         LibraryLookup.ofLibrary(MH_LOOKUP, libName);
  39             }).toArray(LibraryLookup[]::new);
  40         }
  41     }
  42 
  43     private static final Optional<Path> findLibraryPath(Path[] paths, String libName) {
  44         return Arrays.stream(paths).
  45                 map(p -> p.resolve(System.mapLibraryName(libName))).
  46                 filter(Files::isRegularFile).map(Path::toAbsolutePath).findFirst();
  47     }
  48 
  49     private static final Optional<MemoryAddress> lookup(LibraryLookup[] LIBRARIES, String sym) {
  50         for (LibraryLookup l : LIBRARIES) {
  51             try {
  52                 return Optional.of(l.lookup(sym));
  53             } catch (Throwable t) {
  54             }
  55         }
  56         try {
  57             return Optional.of(LibraryLookup.ofDefault().lookup(sym));
  58         } catch (Throwable t) {
  59             return Optional.empty();
  60         }
  61     }
  62 
  63     public static final MemoryAddress lookupGlobalVariable(LibraryLookup[] LIBRARIES, String name) {
  64         return lookup(LIBRARIES, name).orElse(null);
  65     }
  66 
  67     public static final MethodHandle downcallHandle(LibraryLookup[] LIBRARIES, String name, String desc, FunctionDescriptor fdesc) {
  68         return lookup(LIBRARIES, name).map(
  69                 addr -> {
  70                     MethodType mt = MethodType.fromMethodDescriptorString(desc, LOADER);
  71                     return fdesc.isVariadic() ?
  72                         VarargsInvoker.make(addr, mt, fdesc) :
  73                         ABI.downcallHandle(addr, mt, fdesc);
  74                 }).orElse(null);
  75     }
  76 
  77     public static final MemoryAddress upcallStub(MethodHandle handle, FunctionDescriptor fdesc) {
  78         return ABI.upcallStub(handle, fdesc);
  79     }
  80 
  81     public static final <Z> MemoryAddress upcallStub(Class<Z> fi, Z z, FunctionDescriptor fdesc, String mtypeDesc) {
  82         try {
  83             MethodHandle handle = MH_LOOKUP.findVirtual(fi, "apply",
  84                     MethodType.fromMethodDescriptorString(mtypeDesc, LOADER));
  85             handle = handle.bindTo(z);
  86             return upcallStub(handle, fdesc);
  87         } catch (Throwable ex) {
  88             throw new AssertionError(ex);
  89         }
  90     }
  91 
  92     private static class VarargsInvoker {
  93         private static final MethodHandle INVOKE_MH;
  94         private final MemoryAddress symbol;
  95         private final MethodType varargs;
  96         private final FunctionDescriptor function;
  97 
  98         private VarargsInvoker(MemoryAddress symbol, MethodType type, FunctionDescriptor function) {
  99             this.symbol = symbol;
 100             this.varargs = type;
 101             this.function = function;
 102         }
 103 
 104         static {
 105             try {
 106                 INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, Object[].class));
 107             } catch (ReflectiveOperationException e) {
 108                 throw new RuntimeException(e);
 109             }
 110         }
 111 
 112         static MethodHandle make(MemoryAddress symbol, MethodType type, FunctionDescriptor function) {
 113             VarargsInvoker invoker = new VarargsInvoker(symbol, type, function);
 114             return INVOKE_MH.bindTo(invoker).asCollector(Object[].class, type.parameterCount())
 115                     .asType(type);
 116         }
 117 
 118         private Object invoke(Object[] args) throws Throwable {
 119             // one trailing Object[]
 120             int nNamedArgs = function.argumentLayouts().size();
 121             assert(args.length == nNamedArgs + 1);
 122             // The last argument is the array of vararg collector
 123             Object[] unnamedArgs = (Object[]) args[args.length - 1];
 124 
 125             int argsCount = nNamedArgs + unnamedArgs.length;
 126             Class<?>[] argTypes = new Class<?>[argsCount];
 127             MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length];
 128 
 129             int pos = 0;
 130             for (pos = 0; pos < nNamedArgs; pos++) {
 131                 argTypes[pos] = varargs.parameterType(pos);
 132                 argLayouts[pos] = function.argumentLayouts().get(pos);
 133             }
 134 
 135             assert pos == nNamedArgs;
 136             for (Object o: unnamedArgs) {
 137                 argTypes[pos] = normalize(o.getClass());
 138                 argLayouts[pos] = variadicLayout(argTypes[pos]);
 139                 pos++;
 140             }
 141             assert pos == argsCount;
 142 
 143             MethodType mt = MethodType.methodType(varargs.returnType(), argTypes);
 144             FunctionDescriptor f = (function.returnLayout().isEmpty()) ?
 145                     FunctionDescriptor.ofVoid(false, argLayouts) :
 146                     FunctionDescriptor.of(function.returnLayout().get(), false, argLayouts);
 147             MethodHandle mh = SystemABI.getInstance().downcallHandle(symbol, mt, f);
 148             // flatten argument list so that it can be passed to an asSpreader MH
 149             Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length];
 150             System.arraycopy(args, 0, allArgs, 0, nNamedArgs);
 151             System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length);
 152 
 153             return mh.asSpreader(Object[].class, argsCount).invoke(allArgs);
 154         }
 155 
 156         private static Class<?> unboxIfNeeded(Class<?> clazz) {
 157             if (clazz == Boolean.class) {
 158                 return boolean.class;
 159             } else if (clazz == Void.class) {
 160                 return void.class;
 161             } else if (clazz == Byte.class) {
 162                 return byte.class;
 163             } else if (clazz == Character.class) {
 164                 return char.class;
 165             } else if (clazz == Short.class) {
 166                 return short.class;
 167             } else if (clazz == Integer.class) {
 168                 return int.class;
 169             } else if (clazz == Long.class) {
 170                 return long.class;
 171             } else if (clazz == Float.class) {
 172                 return float.class;
 173             } else if (clazz == Double.class) {
 174                 return double.class;
 175             } else {
 176                 return clazz;
 177             }
 178         }
 179 
 180         private Class<?> normalize(Class<?> c) {
 181             c = unboxIfNeeded(c);
 182             if (c.isPrimitive()) {
 183                 return c;
 184             }
 185             if (MemoryAddress.class.isAssignableFrom(c)) {
 186                 return MemoryAddress.class;
 187             }
 188             if (MemorySegment.class.isAssignableFrom(c)) {
 189                 return MemorySegment.class;
 190             }
 191             throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName());
 192         }
 193 
 194         private MemoryLayout variadicLayout(Class<?> c) {
 195             if (c == char.class || c == byte.class || c == short.class || c == int.class || c == long.class) {
 196                 //it is ok to approximate with a machine word here; numerics arguments in a prototype-less
 197                 //function call are always rounded up to a register size anyway.
 198                 return C_LONG;
 199             } else if (c == float.class || c == double.class) {
 200                 return C_DOUBLE;
 201             } else if (MemoryAddress.class.isAssignableFrom(c)) {
 202                 return C_POINTER;
 203             } else {
 204                 throw new IllegalArgumentException("Unhandled variadic argument class: " + c);
 205             }
 206         }
 207     }
 208 }