diff --git a/docs/reference-manual/native-image/ForeignInterface.md b/docs/reference-manual/native-image/ForeignInterface.md
index e4cbdd514943..98b09e73c975 100644
--- a/docs/reference-manual/native-image/ForeignInterface.md
+++ b/docs/reference-manual/native-image/ForeignInterface.md
@@ -30,14 +30,18 @@ These two kinds of calls are referred to as "downcalls" and "upcalls" respective
### Looking Up Native Functions
The FFM API provides the `SymbolLookup` interface to find functions in native libraries by name.
-`SymbolLookup.loaderLookup()` is currently the only supported kind of `SymbolLookup`.
+Native image supports all available symbol lookup methods, i.e., `SymbolLookup.loaderLookup()`, `SymbolLookup.libraryLookup()`, and `Linker.defaultLookup()`.
### Registering Foreign Calls
In order to perform calls to native code at run time, supporting code must be generated at image build time.
-Therefore, the `native-image` tool must be provided with descriptors that characterize the functions to which downcalls may be performed at run time.
+Therefore, the `native-image` tool must be provided with descriptors that characterize the functions with which downcalls or upcalls can be performed at runtime.
-These descriptors can be registered using a custom `Feature`, for example:
+For upcalls, it is recommended to register a specific static method as an upcall target by providing its declaring class and the method name.
+This allows `native-image` to create specialized upcall code that can be orders of magnitude faster than a upcall registered only by function descriptor.
+Whenever possible, this should be the preferred way to register upcalls.
+
+Descriptors and target methods can be registered using a custom `Feature`, for example:
```java
import static java.lang.foreign.ValueLayout.*;
@@ -50,6 +54,9 @@ class ForeignRegistrationFeature implements Feature {
RuntimeForeignAccess.registerForUpcall(FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT));
RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.of(ADDRESS, JAVA_INT, JAVA_INT), Linker.Option.firstVariadicArg(1));
RuntimeForeignAccess.registerForDowncall(FunctionDescriptor.ofVoid(JAVA_INT), Linker.Option.captureCallState("errno"));
+
+ MethodHandle target = MethodHandles.lookup().findStatic(UserClass.class, "aStaticMethod", MethodType.of(int.class, int.class, int.class));
+ RuntimeForeignAccess.registerForUpcall(target, FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT));
}
}
```
diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest
index ef23e446d560..41ebca3c11ac 100644
--- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest
+++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest
@@ -1088,6 +1088,7 @@ meth public !varargs static void initializeAtRunTime(java.lang.String[])
supr java.lang.Object
CLSS public final org.graalvm.nativeimage.hosted.RuntimeForeignAccess
+meth public !varargs static void registerForDirectUpcall(java.lang.invoke.MethodHandle,java.lang.Object,java.lang.Object[])
meth public !varargs static void registerForDowncall(java.lang.Object,java.lang.Object[])
meth public !varargs static void registerForUpcall(java.lang.Object,java.lang.Object[])
supr java.lang.Object
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java
index 9d3586017b23..2b5aa887f6ce 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java
@@ -40,6 +40,9 @@
*/
package org.graalvm.nativeimage.hosted;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
@@ -86,6 +89,34 @@ public static void registerForUpcall(Object desc, Object... options) {
ImageSingletons.lookup(RuntimeForeignAccessSupport.class).registerForUpcall(ConfigurationCondition.alwaysTrue(), desc, options);
}
+ /**
+ * Registers a specific static method (denoted by a method handle) as a fast upcall target. This
+ * will create a specialized upcall stub that will invoke only the specified method, which is
+ * much faster than using {@link #registerForUpcall(Object, Object...)}).
+ *
+ * The provided method handle must be a direct method handle. Those are most commonly created
+ * using {@link java.lang.invoke.MethodHandles.Lookup#findStatic(Class, String, MethodType)}.
+ * However, a strict requirement is that it must be possible to create a non-empty descriptor
+ * for the method handle using {@link MethodHandle#describeConstable()}. The denoted static
+ * method will also be registered for reflective access since run-time code will also create a
+ * method handle to denoted static method.
+ *
+ *
+ * Even though this method is weakly typed for compatibility reasons, runtime checks will be
+ * performed to ensure that the arguments have the expected type. It will be deprecated in favor
+ * of strongly typed variant as soon as possible.
+ *
+ *
+ * @param target A direct method handle denoting a static method.
+ * @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for upcalls.
+ * @param options An array of {@link java.lang.foreign.Linker.Option} used for the upcalls.
+ *
+ * @since 24.2
+ */
+ public static void registerForDirectUpcall(MethodHandle target, Object desc, Object... options) {
+ ImageSingletons.lookup(RuntimeForeignAccessSupport.class).registerForDirectUpcall(ConfigurationCondition.alwaysTrue(), target, desc, options);
+ }
+
private RuntimeForeignAccess() {
}
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeForeignAccessSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeForeignAccessSupport.java
index 96db98ab24be..dd0537c496fc 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeForeignAccessSupport.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeForeignAccessSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -40,8 +40,12 @@
*/
package org.graalvm.nativeimage.impl;
+import java.lang.invoke.MethodHandle;
+
public interface RuntimeForeignAccessSupport {
void registerForDowncall(ConfigurationCondition condition, Object desc, Object... options);
void registerForUpcall(ConfigurationCondition condition, Object desc, Object... options);
+
+ void registerForDirectUpcall(ConfigurationCondition condition, MethodHandle target, Object desc, Object... options);
}
diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md
index dc702c150655..1f747b21cf83 100644
--- a/substratevm/CHANGELOG.md
+++ b/substratevm/CHANGELOG.md
@@ -10,6 +10,7 @@ At runtime, premain runtime options are set along with main class' arguments in
The warning is planned to be replaced by an error in GraalVM for JDK 25.
* (GR-48384) Added a GDB Python script (`gdb-debughelpers.py`) to improve the Native Image debugging experience.
* (GR-49517) Add support for emitting Windows x64 unwind info. This enables stack walking in native tooling such as debuggers and profilers.
+* (GR-52576) Optimize FFM API upcalls for specifiable static upcall target methods.
* (GR-56599) Update native image debuginfo from DWARF4 to DWARF5 and store type information for debugging in DWARF type units.
* (GR-56601) Together with Red Hat, we added experimental support for `jcmd` on Linux and macOS. Add `--enable-monitoring=jcmd` to your build arguments to try it out.
* (GR-57384) Preserve the origin of a resource included in a native image. The information is included in the report produced by -H:+GenerateEmbeddedResourcesFile.
diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py
index 18623201ce2f..0b2b476975d8 100644
--- a/substratevm/mx.substratevm/suite.py
+++ b/substratevm/mx.substratevm/suite.py
@@ -719,13 +719,13 @@
],
"requiresConcealed": {
"java.base": [
- "jdk.internal.loader",
- "jdk.internal.reflect",
"jdk.internal.foreign",
"jdk.internal.foreign.abi",
"jdk.internal.foreign.abi.x64",
"jdk.internal.foreign.abi.x64.sysv",
"jdk.internal.foreign.abi.x64.windows",
+ "jdk.internal.loader",
+ "jdk.internal.reflect",
],
"jdk.internal.vm.ci" : [
"jdk.vm.ci.amd64",
@@ -758,6 +758,9 @@
"java.base": [
"jdk.internal.foreign",
"jdk.internal.foreign.abi",
+ "jdk.internal.foreign.abi.x64.windows",
+ "jdk.internal.foreign.abi.x64.sysv",
+ "jdk.internal.foreign.layout",
],
"jdk.internal.vm.ci" : [
"jdk.vm.ci.code",
diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java
index abf07ec02d15..fca87f363cd7 100644
--- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java
+++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java
@@ -26,9 +26,11 @@
import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.HAS_SIDE_EFFECT;
+import java.lang.constant.DirectMethodHandleDesc;
import java.lang.invoke.MethodHandle;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.BiConsumer;
import org.graalvm.collections.EconomicMap;
import org.graalvm.nativeimage.ImageSingletons;
@@ -61,11 +63,15 @@ public static ForeignFunctionsRuntime singleton() {
private final AbiUtils.TrampolineTemplate trampolineTemplate = AbiUtils.singleton().generateTrampolineTemplate();
private final EconomicMap downcallStubs = EconomicMap.create();
+ private final EconomicMap directUpcallStubs = EconomicMap.create();
private final EconomicMap upcallStubs = EconomicMap.create();
private final Map trampolines = new HashMap<>();
private TrampolineSet currentTrampolineSet;
+ // for testing: callback if direct upcall lookup succeeded
+ private BiConsumer usingSpecializedUpcallListener;
+
@Platforms(Platform.HOSTED_ONLY.class)
public ForeignFunctionsRuntime() {
}
@@ -82,6 +88,12 @@ public void addUpcallStubPointer(JavaEntryPointInfo jep, CFunctionPointer ptr) {
VMError.guarantee(upcallStubs.put(jep, new FunctionPointerHolder(ptr)) == null);
}
+ @Platforms(Platform.HOSTED_ONLY.class)
+ public void addDirectUpcallStubPointer(DirectMethodHandleDesc desc, CFunctionPointer ptr) {
+ VMError.guarantee(!directUpcallStubs.containsKey(desc), "Seems like multiple stubs were generated for " + desc);
+ VMError.guarantee(directUpcallStubs.put(desc, new FunctionPointerHolder(ptr)) == null);
+ }
+
/**
* We'd rather report the function descriptor than the native method type, but we don't have it
* available here. One could intercept this exception in
@@ -105,15 +117,61 @@ CFunctionPointer getUpcallStubPointer(JavaEntryPointInfo jep) {
}
Pointer registerForUpcall(MethodHandle methodHandle, JavaEntryPointInfo jep) {
+ /*
+ * Look up the upcall stub pointer first to avoid unnecessary allocation and synchronization
+ * if it doesn't exist.
+ */
+ CFunctionPointer upcallStubPointer = getUpcallStubPointer(jep);
synchronized (trampolines) {
if (currentTrampolineSet == null || !currentTrampolineSet.hasFreeTrampolines()) {
currentTrampolineSet = new TrampolineSet(trampolineTemplate);
trampolines.put(currentTrampolineSet.base().rawValue(), currentTrampolineSet);
}
- return currentTrampolineSet.assignTrampoline(methodHandle, getUpcallStubPointer(jep));
+ return currentTrampolineSet.assignTrampoline(methodHandle, upcallStubPointer);
}
}
+ /**
+ * Updates the stub address in the upcall trampoline with the address of a direct upcall stub.
+ * The trampoline is identified by the given native address and the direct upcall stub is
+ * identified by the method handle descriptor.
+ *
+ * @param trampolineAddress The address of the upcall trampoline.
+ * @param desc A direct method handle descriptor used to lookup the direct upcall stub.
+ */
+ void patchForDirectUpcall(long trampolineAddress, DirectMethodHandleDesc desc) {
+ FunctionPointerHolder functionPointerHolder = directUpcallStubs.get(desc);
+ if (functionPointerHolder == null) {
+ return;
+ }
+
+ Pointer trampolinePointer = WordFactory.pointer(trampolineAddress);
+ Pointer trampolineSetBase = TrampolineSet.getAllocationBase(trampolinePointer);
+ TrampolineSet trampolineSet = trampolines.get(trampolineSetBase.rawValue());
+ if (trampolineSet == null) {
+ return;
+ }
+ /*
+ * Synchronizing on 'trampolineSet' is not necessary at this point since we are still in the
+ * call context of 'Linker.upcallStub' and the allocated trampoline is owned by the
+ * allocating thread until it returns from the call. Also, the trampoline cannot be free'd
+ * between allocation and patching because the associated arena is still on the stack.
+ */
+ trampolineSet.patchTrampolineForDirectUpcall(trampolinePointer, functionPointerHolder.functionPointer);
+ /*
+ * If we reach this point, everything went fine and the trampoline was patched with the
+ * specialized upcall stub's address. For testing, now report that the lookup and patching
+ * succeeded.
+ */
+ if (usingSpecializedUpcallListener != null) {
+ usingSpecializedUpcallListener.accept(trampolineAddress, desc);
+ }
+ }
+
+ public void setUsingSpecializedUpcallListener(BiConsumer listener) {
+ usingSpecializedUpcallListener = listener;
+ }
+
void freeTrampoline(long addr) {
synchronized (trampolines) {
long base = TrampolineSet.getAllocationBase(WordFactory.pointer(addr)).rawValue();
diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java
index 6d60e1a41344..4871809310e8 100644
--- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java
+++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java
@@ -24,11 +24,25 @@
*/
package com.oracle.svm.core.foreign;
+import java.lang.constant.DirectMethodHandleDesc;
+import java.lang.constant.MethodHandleDesc;
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.MemorySegment;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.Optional;
+
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
+import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import jdk.internal.foreign.abi.AbstractLinker;
+import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory;
+import jdk.internal.foreign.abi.LinkerOptions;
+import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
+import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
@TargetClass(AbstractLinker.class)
public final class Target_jdk_internal_foreign_abi_AbstractLinker {
@@ -46,3 +60,55 @@ public final class Target_jdk_internal_foreign_abi_AbstractLinker {
@TargetClass(className = "jdk.internal.foreign.abi.SoftReferenceCache")
final class Target_jdk_internal_foreign_abi_SoftReferenceCache {
}
+
+/**
+ * A decorator for jdk.internal.foreign.abi.UpcallStubFactory which intercepts the call to method
+ * 'makeStub'. It will (1) call the original factory to create the upcall, and (2) then use the
+ * method handle's descriptor to lookup if a specialized (direct) upcall stub is available. If so,
+ * the trampoline will be updated with the specialized stub's address.
+ *
+ * @param delegate The original upcall stub factory as created by JDK's call arranger.
+ */
+record UpcallStubFactoryDecorator(UpcallStubFactory delegate) implements UpcallStubFactory {
+
+ @Override
+ public MemorySegment makeStub(MethodHandle target, Arena arena) {
+ MemorySegment segment = delegate.makeStub(target, arena);
+
+ /*
+ * We cannot do this in 'UpcallLinker.makeUpcallStub' because that one already gets a
+ * different method handle that will handle parameter/return value bindings. Further, method
+ * handles cannot be compared. If the provided method handle is a DirectMethodHandle, we use
+ * the MH descriptor to check if there is a registered direct upcall stub. Then, we will
+ * patch the already allocated trampoline with a different upcall stub pointer.
+ */
+ Optional methodHandleDesc = target.describeConstable();
+ if (methodHandleDesc.isPresent() && methodHandleDesc.get() instanceof DirectMethodHandleDesc desc) {
+ ForeignFunctionsRuntime.singleton().patchForDirectUpcall(segment.address(), desc);
+ }
+ return segment;
+ }
+}
+
+@TargetClass(value = SysVx64Linker.class, onlyWith = ForeignFunctionsEnabled.class)
+final class Target_jdk_internal_foreign_abi_x64_sysv_SysVx64Linker {
+
+ @Substitute
+ UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
+ return new UpcallStubFactoryDecorator(jdk.internal.foreign.abi.x64.sysv.CallArranger.arrangeUpcall(targetType, function, options));
+ }
+}
+
+@TargetClass(value = Windowsx64Linker.class, onlyWith = ForeignFunctionsEnabled.class)
+final class Target_jdk_internal_foreign_abi_x64_windows_Windowsx64Linker {
+
+ @Substitute
+ UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
+ return new UpcallStubFactoryDecorator(jdk.internal.foreign.abi.x64.windows.CallArranger.arrangeUpcall(targetType, function, options));
+ }
+}
+
+/*
+ * GR-58659, GR-58660: add substitutions for LinuxAArch64Linker and MacOsAArch64Linker here once we
+ * support them.
+ */
\ No newline at end of file
diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java
index 61974e0518b6..29ec06ea0cf3 100644
--- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java
+++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/TrampolineSet.java
@@ -26,6 +26,7 @@
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.List;
import org.graalvm.nativeimage.CurrentIsolate;
@@ -37,6 +38,7 @@
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.os.VirtualMemoryProvider;
+import com.oracle.svm.core.util.UnsignedUtils;
import com.oracle.svm.core.util.VMError;
import jdk.graal.compiler.core.common.NumUtil;
@@ -73,8 +75,22 @@ public static Pointer getAllocationBase(Pointer ptr) {
private final int trampolineCount = maxTrampolineCount();
private final PointerBase[] methodHandles = new PointerBase[trampolineCount];
private final CFunctionPointer[] stubs = new CFunctionPointer[trampolineCount];
+ private final BitSet patchedStubs;
private final Pointer trampolines;
+ private static BitSet initializedPatchedStubs(int nbits) {
+ BitSet patchedStubs = null;
+ assert (patchedStubs = new BitSet(nbits)).isEmpty();
+ return patchedStubs;
+ }
+
+ private boolean getAndSetPatchedStub(int id) {
+ assert patchedStubs != null;
+ boolean res = patchedStubs.get(id);
+ patchedStubs.set(id);
+ return res;
+ }
+
private PinnedObject pin(Object object) {
PinnedObject pinned = PinnedObject.create(object);
pins.add(pinned);
@@ -86,6 +102,7 @@ private PinnedObject pin(Object object) {
assert trampolineCount <= maxTrampolineCount();
trampolines = prepareTrampolines(pin(methodHandles), pin(stubs), template);
+ this.patchedStubs = initializedPatchedStubs(stubs.length);
}
Pointer base() {
@@ -103,10 +120,19 @@ Pointer assignTrampoline(MethodHandle methodHandle, CFunctionPointer upcallStubP
methodHandles[id] = pinned.addressOfObject();
stubs[id] = upcallStubPointer;
+ assert !patchedStubs.get(id);
return trampolines.add(id * AbiUtils.singleton().trampolineSize());
}
+ void patchTrampolineForDirectUpcall(Pointer trampolinePointer, CFunctionPointer directUpcallStubPointer) {
+ VMError.guarantee(trampolinePointer.aboveOrEqual(trampolines), "invalid trampoline pointer");
+ int id = UnsignedUtils.safeToInt(trampolinePointer.subtract(trampolines).unsignedDivide(AbiUtils.singleton().trampolineSize()));
+ VMError.guarantee(id >= 0 && id < stubs.length, "invalid trampoline id");
+ assert !getAndSetPatchedStub(id) : "attempt to patch trampoline twice";
+ stubs[id] = directUpcallStubPointer;
+ }
+
private Pointer prepareTrampolines(PinnedObject mhsArray, PinnedObject stubsArray, AbiUtils.TrampolineTemplate template) {
VirtualMemoryProvider memoryProvider = VirtualMemoryProvider.get();
UnsignedWord pageSize = allocationSize();
@@ -142,6 +168,9 @@ boolean tryFree() {
}
VirtualMemoryProvider.get().free(trampolines, allocationSize());
assigned = FREED;
+ if (patchedStubs != null) {
+ patchedStubs.clear();
+ }
return true;
}
}
diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/UpcallStubsHolder.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/UpcallStubsHolder.java
index 1a71459d279e..e76dfe455bfe 100644
--- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/UpcallStubsHolder.java
+++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/UpcallStubsHolder.java
@@ -54,10 +54,14 @@ public static ConstantPool getConstantPool(MetaAccessProvider metaAccess) {
*
*/
@Platforms(Platform.HOSTED_ONLY.class)
- public static String stubName(JavaEntryPointInfo jep, boolean highLevel) {
+ public static String stubName(JavaEntryPointInfo jep, boolean highLevel, boolean direct) {
MethodType type = jep.handleType();
- StringBuilder builder = new StringBuilder("upcall");
+ StringBuilder builder = new StringBuilder();
+ if (direct) {
+ builder.append("direct_");
+ }
+ builder.append("upcall");
builder.append(highLevel ? "High" : "Low");
builder.append("_");
for (var param : type.parameterArray()) {
diff --git a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsConfigurationParser.java b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsConfigurationParser.java
index cd3b2b260faa..877a86482c07 100644
--- a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsConfigurationParser.java
+++ b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsConfigurationParser.java
@@ -27,6 +27,9 @@
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.Linker.Option;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -41,6 +44,8 @@
import com.oracle.svm.core.configure.ConfigurationParser;
import com.oracle.svm.core.util.BasedOnJDKFile;
+import com.oracle.svm.core.util.UserError;
+import com.oracle.svm.hosted.ImageClassLoader;
import jdk.graal.compiler.util.json.JsonParserException;
@@ -52,38 +57,68 @@ public class ForeignFunctionsConfigurationParser extends ConfigurationParser {
private static final String DOWNCALL_OPTION_CRITICAL = "critical";
private static final String DOWNCALL_OPTION_ALLOW_HEAP_ACCESS = "allowHeapAccess";
+ private final ImageClassLoader imageClassLoader;
private final RuntimeForeignAccessSupport accessSupport;
- public ForeignFunctionsConfigurationParser(RuntimeForeignAccessSupport access) {
+ public ForeignFunctionsConfigurationParser(ImageClassLoader imageClassLoader, RuntimeForeignAccessSupport access) {
super(true);
+ this.imageClassLoader = imageClassLoader;
this.accessSupport = access;
}
@Override
public void parseAndRegister(Object json, URI origin) {
var topLevel = asMap(json, "first level of document must be a map");
- checkAttributes(topLevel, "foreign methods categories", List.of(), List.of("downcalls", "upcalls"));
+ checkAttributes(topLevel, "foreign methods categories", List.of(), List.of("downcalls", "upcalls", "directUpcalls"));
- var downcalls = asList(topLevel.get("downcalls", List.of()), "downcalls must be an array of method signatures");
+ var downcalls = asList(topLevel.get("downcalls", List.of()), "downcalls must be an array of function descriptor and linker options");
for (Object downcall : downcalls) {
parseAndRegisterForeignCall(downcall, this::parseDowncallOptions, (descriptor, options) -> accessSupport.registerForDowncall(ConfigurationCondition.alwaysTrue(), descriptor, options));
}
- var upcalls = asList(topLevel.get("upcalls", List.of()), "upcalls must be an array of method signatures");
+ var upcalls = asList(topLevel.get("upcalls", List.of()), "upcalls must be an array of function descriptor and linker options");
for (Object upcall : upcalls) {
parseAndRegisterForeignCall(upcall, this::parseUpcallOptions, (descriptor, options) -> accessSupport.registerForUpcall(ConfigurationCondition.alwaysTrue(), descriptor, options));
}
+
+ var directUpcalls = asList(topLevel.get("directUpcalls", List.of()), "direct upcalls must be an array of method references, function descriptors, and linker options");
+ for (Object upcall : directUpcalls) {
+ parseAndRegisterDirectUpcall(upcall);
+ }
}
private void parseAndRegisterForeignCall(Object call, Function, List