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 }