@@ -73,7 +73,11 @@ abstract class FunctionExecuteNode extends Node {
73
73
protected Object cachedSignature (NativePointer receiver , @ SuppressWarnings ("unused" ) LibFFISignature signature , Object [] args ,
74
74
@ Cached ("signature" ) @ SuppressWarnings ("unused" ) LibFFISignature cachedSignature ,
75
75
@ Cached ("createCachedSignatureCall(cachedSignature)" ) DirectCallNode execute ) {
76
- return execute .call (receiver .asPointer (), args );
76
+ try {
77
+ return execute .call (receiver .asPointer (), args );
78
+ } finally {
79
+ assert keepAlive (args );
80
+ }
77
81
}
78
82
79
83
static class SignatureExecuteNode extends RootNode {
@@ -170,7 +174,11 @@ protected Object cachedArgCount(NativePointer receiver, LibFFISignature signatur
170
174
throw ArityException .create (argIdx , args .length );
171
175
}
172
176
173
- return slowPathCall .call (receiver , signature , buffer );
177
+ try {
178
+ return slowPathCall .call (receiver , signature , buffer );
179
+ } finally {
180
+ assert keepAlive (args );
181
+ }
174
182
}
175
183
176
184
DirectCallNode createSlowPathCall () {
@@ -222,7 +230,11 @@ static Object genericExecute(NativePointer receiver, LibFFISignature signature,
222
230
throw ArityException .create (argIdx , args .length );
223
231
}
224
232
225
- return IndirectCallNode .getUncached ().call (language .getSlowPathCall (), receiver , signature , buffer );
233
+ try {
234
+ return IndirectCallNode .getUncached ().call (language .getSlowPathCall (), receiver , signature , buffer );
235
+ } finally {
236
+ assert keepAlive (args );
237
+ }
226
238
}
227
239
228
240
static class SlowPathExecuteNode extends RootNode {
@@ -248,4 +260,14 @@ static Object slowPathExecute(NFIContext ctx, LibFFISignature signature, long fu
248
260
return signature .execute (ctx , functionPointer , buffer );
249
261
}
250
262
}
263
+
264
+ /**
265
+ * Helper method to keep the argument array alive. The method itself does nothing, but it keeps
266
+ * the value alive in a FrameState. That way, the GC can not free the objects as long as they
267
+ * might still be in use by the native code (maybe indirectly via an embedded native pointer),
268
+ * but the escape analysis can still virtualize the objects if the allocation is visible.
269
+ */
270
+ private static boolean keepAlive (@ SuppressWarnings ("unused" ) Object args ) {
271
+ return true ;
272
+ }
251
273
}
0 commit comments