2626
2727import static jdk .graal .compiler .core .common .spi .ForeignCallDescriptor .CallSideEffect .HAS_SIDE_EFFECT ;
2828
29+ import java .io .IOException ;
2930import java .lang .constant .DirectMethodHandleDesc ;
3031import java .lang .foreign .FunctionDescriptor ;
3132import java .lang .foreign .MemorySegment ;
3233import java .lang .foreign .MemorySegment .Scope ;
3334import java .lang .invoke .MethodHandle ;
3435import java .lang .invoke .MethodType ;
36+ import java .util .Deque ;
3537import java .util .HashMap ;
3638import java .util .Locale ;
3739import java .util .Map ;
3840import java .util .Set ;
41+ import java .util .concurrent .ConcurrentLinkedDeque ;
3942import java .util .function .BiConsumer ;
4043
4144import org .graalvm .collections .EconomicMap ;
7174import com .oracle .svm .core .util .VMError ;
7275
7376import jdk .graal .compiler .api .replacements .Fold ;
77+ import jdk .graal .compiler .util .json .JsonPrintable ;
78+ import jdk .graal .compiler .util .json .JsonWriter ;
7479import jdk .graal .compiler .word .Word ;
7580import jdk .internal .foreign .CABI ;
7681import jdk .internal .foreign .MemorySessionImpl ;
@@ -85,6 +90,7 @@ public static ForeignFunctionsRuntime singleton() {
8590 return ImageSingletons .lookup (ForeignFunctionsRuntime .class );
8691 }
8792
93+ private final AbiUtils abiUtils ;
8894 private final AbiUtils .TrampolineTemplate trampolineTemplate ;
8995
9096 private final EconomicMap <NativeEntryPointInfo , FunctionPointerHolder > downcallStubs = ImageHeapMap .create ("downcallStubs" );
@@ -93,6 +99,14 @@ public static ForeignFunctionsRuntime singleton() {
9399 private final EconomicSet <ResolvedJavaType > neverAccessesSharedArenaTypes = EconomicSet .create ();
94100 private final EconomicSet <ResolvedJavaMethod > neverAccessesSharedArenaMethods = EconomicSet .create ();
95101
102+ /**
103+ * A thread-safe stack of currently performed link requests (i.e. creating a downcall handle or
104+ * an upcall stub). This stack is used to generate a helpful error message if the link request
105+ * fails because of a missing stub. Since link requests may be created concurrently, we need to
106+ * use a thread-safe collection.
107+ */
108+ private final Deque <LinkRequest > currentLinkRequests = new ConcurrentLinkedDeque <>();
109+
96110 private final Map <Long , TrampolineSet > trampolines = new HashMap <>();
97111 private TrampolineSet currentTrampolineSet ;
98112
@@ -101,6 +115,7 @@ public static ForeignFunctionsRuntime singleton() {
101115
102116 @ Platforms (Platform .HOSTED_ONLY .class )
103117 public ForeignFunctionsRuntime (AbiUtils abiUtils ) {
118+ this .abiUtils = abiUtils ;
104119 this .trampolineTemplate = new TrampolineTemplate (new byte [abiUtils .trampolineSize ()]);
105120 }
106121
@@ -178,24 +193,18 @@ public void registerSafeArenaAccessorMethod(ResolvedJavaMethod method) {
178193 neverAccessesSharedArenaMethods .add (method );
179194 }
180195
181- /**
182- * We'd rather report the function descriptor than the native method type, but we don't have it
183- * available here. One could intercept this exception in
184- * {@link jdk.internal.foreign.abi.DowncallLinker#getBoundMethodHandle} and add information
185- * about the descriptor there.
186- */
187196 CFunctionPointer getDowncallStubPointer (NativeEntryPointInfo nep ) {
188197 FunctionPointerHolder holder = downcallStubs .get (nep );
189198 if (holder == null ) {
190- throw MissingForeignRegistrationUtils . reportDowncall (nep );
199+ throw reportMissingDowncall (nep );
191200 }
192201 return holder .functionPointer ;
193202 }
194203
195204 CFunctionPointer getUpcallStubPointer (JavaEntryPointInfo jep ) {
196205 FunctionPointerHolder holder = upcallStubs .get (jep );
197206 if (holder == null ) {
198- throw MissingForeignRegistrationUtils . reportUpcall (jep );
207+ throw reportMissingUpcall (jep );
199208 }
200209 return holder .functionPointer ;
201210 }
@@ -270,34 +279,91 @@ void freeTrampoline(long addr) {
270279 }
271280 }
272281
273- public static class MissingForeignRegistrationUtils extends MissingRegistrationUtils {
274- public static MissingForeignRegistrationError reportDowncall (NativeEntryPointInfo nep ) {
275- MissingForeignRegistrationError mfre = new MissingForeignRegistrationError (foreignRegistrationMessage ("downcall" , nep .methodType ()));
276- report (mfre );
277- return mfre ;
278- }
279-
280- public static MissingForeignRegistrationError reportUpcall (JavaEntryPointInfo jep ) {
281- MissingForeignRegistrationError mfre = new MissingForeignRegistrationError (foreignRegistrationMessage ("upcall" , jep .cMethodType ()));
282- report (mfre );
283- return mfre ;
282+ /**
283+ * Looks for the corresponding {@link #currentLinkRequests link request} by creating a
284+ * {@link NativeEntryPointInfo} for each currently existing link request and comparing to the
285+ * given one. The matching link request then contains the {@link FunctionDescriptor} and
286+ * {@link LinkerOptions} that are required to produce a helpful error message for the user.
287+ */
288+ private MissingForeignRegistrationError reportMissingDowncall (NativeEntryPointInfo nep ) {
289+ LinkRequest currentLinkRequest = null ;
290+ for (LinkRequest linkRequest : currentLinkRequests ) {
291+ NativeEntryPointInfo nativeEntryPointInfo = abiUtils .makeNativeEntrypoint (linkRequest .functionDescriptor , linkRequest .linkerOptions );
292+ if (nep .equals (nativeEntryPointInfo )) {
293+ currentLinkRequest = linkRequest ;
294+ break ;
295+ }
284296 }
297+ throw MissingForeignRegistrationUtils .report (false , currentLinkRequest , nep .methodType ());
298+ }
285299
286- private static String foreignRegistrationMessage (String failedAction , MethodType methodType ) {
287- return registrationMessage ("perform " + failedAction + " with leaf type" , methodType .toString (), "" , "" , "foreign" , "foreign" );
300+ /**
301+ * Similar to {@link #reportMissingDowncall} but for upcalls.
302+ */
303+ private MissingForeignRegistrationError reportMissingUpcall (JavaEntryPointInfo jep ) {
304+ LinkRequest currentLinkRequest = null ;
305+ for (LinkRequest linkRequest : currentLinkRequests ) {
306+ JavaEntryPointInfo javaEntryPointInfo = abiUtils .makeJavaEntryPoint (linkRequest .functionDescriptor , linkRequest .linkerOptions );
307+ if (jep .equals (javaEntryPointInfo )) {
308+ currentLinkRequest = linkRequest ;
309+ break ;
310+ }
288311 }
312+ throw MissingForeignRegistrationUtils .report (true , currentLinkRequest , jep .handleType ());
313+ }
289314
315+ public static class MissingForeignRegistrationUtils extends MissingRegistrationUtils {
290316 private static void report (MissingForeignRegistrationError exception ) {
291317 StackTraceElement responsibleClass = getResponsibleClass (exception , foreignEntryPoints );
292318 MissingRegistrationUtils .report (exception , responsibleClass );
293319 }
294320
321+ private static MissingForeignRegistrationError report (boolean upcall , LinkRequest linkRequest , MethodType methodType ) {
322+ String json = linkRequest != null ? elementToJSON (linkRequest ) : "" ;
323+ String failedAction = upcall ? "upcall" : "downcall" ;
324+ String message = registrationMessage ("perform " + failedAction + " with leaf type" , methodType .toString (), json , "" , "foreign" , "foreign-function-and-memory-api" );
325+ MissingForeignRegistrationError mfre = new MissingForeignRegistrationError (message );
326+ report (mfre );
327+ throw mfre ;
328+ }
329+
295330 private static final Map <String , Set <String >> foreignEntryPoints = Map .of (
296331 "jdk.internal.foreign.abi.AbstractLinker" , Set .of (
297332 "downcallHandle" ,
298333 "upcallStub" ));
299334 }
300335
336+ record LinkRequest (boolean upcall , FunctionDescriptor functionDescriptor , LinkerOptions linkerOptions ) implements AutoCloseable , JsonPrintable {
337+
338+ static LinkRequest create (boolean upcall , FunctionDescriptor functionDescriptor , LinkerOptions linkerOptions ) {
339+ LinkRequest linkRequest = new LinkRequest (upcall , functionDescriptor , linkerOptions );
340+ ForeignFunctionsRuntime .singleton ().currentLinkRequests .push (linkRequest );
341+ return linkRequest ;
342+ }
343+
344+ @ Override
345+ public boolean equals (Object obj ) {
346+ return this == obj ;
347+ }
348+
349+ @ Override
350+ public int hashCode () {
351+ return System .identityHashCode (this );
352+ }
353+
354+ @ Override
355+ public void close () {
356+ ForeignFunctionsRuntime .singleton ().currentLinkRequests .remove (this );
357+ }
358+
359+ @ Override
360+ public void printJson (JsonWriter writer ) throws IOException {
361+ writer .printValue (upcall ? "upcalls" : "downcalls" ).appendFieldSeparator ().appendArrayStart ();
362+ SubstrateForeignUtil .linkRequestToJsonPrintable (this ).printJson (writer );
363+ writer .appendArrayEnd ();
364+ }
365+ }
366+
301367 /**
302368 * Arguments follow the same structure as described in {@link NativeEntryPointInfo}, with an
303369 * additional {@link Target_jdk_internal_foreign_abi_NativeEntryPoint} (NEP) as the last
0 commit comments