Skip to content

Commit 6544c69

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Convert Objects to Dart_Handles in FFI calls
This includes support for calling Dart_PropagateError in native code when doing FFI calls, and catching uncaught exceptions with Dart_IsError when doing FFI callbacks. The support for Dart_PropagateError adds a catch entry to the FFI trampoline, which prevents inlining these trampolines in AOT. This regresses the FfiCall benchmarks by 1-2% in AOT. In addition, Dart_PropagateError requires maintaining a bit whether we entered native/VM code from generated code through FFI or not. That way we can do the proper transition on the exception path. When entering generated code, we store this bit on the stack, right after the entry frame. Design: http://go/dart-ffi-handles Issue: #36858 Issue: #41319 Change-Id: Idfd7ff69132fb29cc730931a4113d914d4437396 Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-nnbd-linux-debug-x64-try,analyzer-nnbd-linux-release-try,front-end-nnbd-linux-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/145591 Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent bf3166b commit 6544c69

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2134
-337
lines changed

pkg/analyzer/lib/src/dart/error/ffi_code.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class FfiCode extends AnalyzerErrorCode {
6161
message:
6262
"The method 'Pointer.fromFunction' must not have an exceptional return "
6363
"value (the second argument) when the return type of the function is "
64-
"either 'void' or 'Pointer'.",
64+
"either 'void', 'Handle' or 'Pointer'.",
6565
correction: "Try removing the exceptional return value.");
6666

6767
/**
@@ -102,7 +102,7 @@ class FfiCode extends AnalyzerErrorCode {
102102
message:
103103
"The method 'Pointer.fromFunction' must have an exceptional return "
104104
"value (the second argument) when the return type of the function is "
105-
"neither 'void' or 'Pointer'.",
105+
"neither 'void', 'Handle' or 'Pointer'.",
106106
correction: "Try adding an exceptional return value.");
107107

108108
/**

pkg/analyzer/lib/src/generated/ffi_verifier.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
190190
bool _isPointer(Element element) =>
191191
element.name == 'Pointer' && element.library.name == 'dart.ffi';
192192

193+
bool _isHandle(Element element) =>
194+
element.name == 'Handle' && element.library.name == 'dart.ffi';
195+
193196
bool _isNativeFunctionPointerExtension(Element element) =>
194197
element.name == 'NativeFunctionPointer' &&
195198
element.library.name == 'dart.ffi';
@@ -295,6 +298,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
295298
if (name == 'Void') {
296299
return _PrimitiveDartType.void_;
297300
}
301+
if (name == 'Handle') {
302+
return _PrimitiveDartType.handle;
303+
}
298304
}
299305
}
300306
return _PrimitiveDartType.none;
@@ -449,6 +455,11 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
449455
return dartType.isDartCoreDouble;
450456
} else if (nativeReturnType == _PrimitiveDartType.void_) {
451457
return dartType.isVoid;
458+
} else if (nativeReturnType == _PrimitiveDartType.handle) {
459+
InterfaceType objectType = typeSystem.objectStar;
460+
return checkCovariance
461+
? /* everything is subtype of objectStar */ true
462+
: typeSystem.isSubtypeOf2(objectType, dartType);
452463
} else if (dartType is InterfaceType && nativeType is InterfaceType) {
453464
return checkCovariance
454465
? typeSystem.isSubtypeOf2(dartType, nativeType)
@@ -520,7 +531,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
520531

521532
// TODO(brianwilkerson) Validate that `f` is a top-level function.
522533
final DartType R = (T as FunctionType).returnType;
523-
if ((FT as FunctionType).returnType.isVoid || _isPointer(R.element)) {
534+
if ((FT as FunctionType).returnType.isVoid ||
535+
_isPointer(R.element) ||
536+
_isHandle(R.element)) {
524537
if (argCount != 1) {
525538
_errorReporter.reportErrorForNode(
526539
FfiCode.INVALID_EXCEPTION_VALUE, node.argumentList.arguments[1]);
@@ -591,5 +604,6 @@ enum _PrimitiveDartType {
591604
double,
592605
int,
593606
void_,
607+
handle,
594608
none,
595609
}

pkg/vm/lib/transformations/ffi.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ enum NativeType {
3434
kFloat,
3535
kDouble,
3636
kVoid,
37-
kStruct
37+
kStruct,
38+
kHandle,
3839
}
3940

4041
const NativeType kNativeTypeIntStart = NativeType.kInt8;
@@ -59,7 +60,8 @@ const List<String> nativeTypeClassNames = [
5960
'Float',
6061
'Double',
6162
'Void',
62-
'Struct'
63+
'Struct',
64+
'Handle'
6365
];
6466

6567
const int UNKNOWN = 0;
@@ -85,6 +87,7 @@ const List<int> nativeTypeSizes = [
8587
8, // Double
8688
UNKNOWN, // Void
8789
UNKNOWN, // Struct
90+
WORD_SIZE, // Handle
8891
];
8992

9093
/// The struct layout in various ABIs.
@@ -181,6 +184,7 @@ class FfiTransformer extends Transformer {
181184
final DiagnosticReporter diagnosticReporter;
182185
final ReferenceFromIndex referenceFromIndex;
183186

187+
final Class objectClass;
184188
final Class intClass;
185189
final Class doubleClass;
186190
final Class listClass;
@@ -219,6 +223,7 @@ class FfiTransformer extends Transformer {
219223
FfiTransformer(this.index, this.coreTypes, this.hierarchy,
220224
this.diagnosticReporter, this.referenceFromIndex)
221225
: env = new TypeEnvironment(coreTypes, hierarchy),
226+
objectClass = coreTypes.objectClass,
222227
intClass = coreTypes.intClass,
223228
doubleClass = coreTypes.doubleClass,
224229
listClass = coreTypes.listClass,
@@ -288,9 +293,11 @@ class FfiTransformer extends Transformer {
288293
/// [Void] -> [void]
289294
/// [Pointer]<T> -> [Pointer]<T>
290295
/// T extends [Pointer] -> T
296+
/// [Handle] -> [Object]
291297
/// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
292298
/// where DartRepresentationOf(Tn) -> Sn
293-
DartType convertNativeTypeToDartType(DartType nativeType, bool allowStructs) {
299+
DartType convertNativeTypeToDartType(
300+
DartType nativeType, bool allowStructs, bool allowHandle) {
294301
if (nativeType is! InterfaceType) {
295302
return null;
296303
}
@@ -317,6 +324,9 @@ class FfiTransformer extends Transformer {
317324
if (nativeType_ == NativeType.kVoid) {
318325
return VoidType();
319326
}
327+
if (nativeType_ == NativeType.kHandle && allowHandle) {
328+
return InterfaceType(objectClass, Nullability.legacy);
329+
}
320330
if (nativeType_ != NativeType.kNativeFunction ||
321331
native.typeArguments[0] is! FunctionType) {
322332
return null;
@@ -329,11 +339,12 @@ class FfiTransformer extends Transformer {
329339
}
330340
if (fun.typeParameters.length != 0) return null;
331341
// TODO(36730): Structs cannot appear in native function signatures.
332-
final DartType returnType =
333-
convertNativeTypeToDartType(fun.returnType, /*allowStructs=*/ false);
342+
final DartType returnType = convertNativeTypeToDartType(
343+
fun.returnType, /*allowStructs=*/ false, /*allowHandle=*/ true);
334344
if (returnType == null) return null;
335345
final List<DartType> argumentTypes = fun.positionalParameters
336-
.map((t) => convertNativeTypeToDartType(t, /*allowStructs=*/ false))
346+
.map((t) => convertNativeTypeToDartType(
347+
t, /*allowStructs=*/ false, /*allowHandle=*/ true))
337348
.toList();
338349
if (argumentTypes.contains(null)) return null;
339350
return FunctionType(argumentTypes, returnType, Nullability.legacy);

pkg/vm/lib/transformations/ffi_definitions.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ class _FfiDefinitionTransformer extends FfiTransformer {
230230
nativeTypesClasses[nativeTypeAnnos.first.index],
231231
Nullability.legacy);
232232
// TODO(36730): Support structs inside structs.
233-
final DartType shouldBeDartType =
234-
convertNativeTypeToDartType(nativeType, /*allowStructs=*/ false);
233+
final DartType shouldBeDartType = convertNativeTypeToDartType(
234+
nativeType, /*allowStructs=*/ false, /*allowHandle=*/ false);
235235
if (shouldBeDartType == null ||
236236
!env.isSubtypeOf(type, shouldBeDartType,
237237
SubtypeCheckMode.ignoringNullabilities)) {

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ class _FfiUseSiteTransformer extends FfiTransformer {
202202
.classNode);
203203

204204
if (expectedReturn == NativeType.kVoid ||
205-
expectedReturn == NativeType.kPointer) {
205+
expectedReturn == NativeType.kPointer ||
206+
expectedReturn == NativeType.kHandle) {
206207
if (node.arguments.positional.length > 1) {
207208
diagnosticReporter.report(
208209
templateFfiExpectedNoExceptionalReturn.withArguments(
@@ -380,9 +381,9 @@ class _FfiUseSiteTransformer extends FfiTransformer {
380381

381382
void _ensureNativeTypeToDartType(
382383
DartType nativeType, DartType dartType, Expression node,
383-
{bool allowStructs: false}) {
384+
{bool allowStructs: false, bool allowHandle: false}) {
384385
final DartType correspondingDartType =
385-
convertNativeTypeToDartType(nativeType, allowStructs);
386+
convertNativeTypeToDartType(nativeType, allowStructs, allowHandle);
386387
if (dartType == correspondingDartType) return;
387388
if (env.isSubtypeOf(correspondingDartType, dartType,
388389
SubtypeCheckMode.ignoringNullabilities)) {
@@ -398,8 +399,9 @@ class _FfiUseSiteTransformer extends FfiTransformer {
398399
}
399400

400401
void _ensureNativeTypeValid(DartType nativeType, Expression node,
401-
{bool allowStructs: false}) {
402-
if (!_nativeTypeValid(nativeType, allowStructs: allowStructs)) {
402+
{bool allowStructs: false, bool allowHandle: false}) {
403+
if (!_nativeTypeValid(nativeType,
404+
allowStructs: allowStructs, allowHandle: allowHandle)) {
403405
diagnosticReporter.report(
404406
templateFfiTypeInvalid.withArguments(
405407
nativeType, currentLibrary.isNonNullableByDefault),
@@ -412,8 +414,10 @@ class _FfiUseSiteTransformer extends FfiTransformer {
412414

413415
/// The Dart type system does not enforce that NativeFunction return and
414416
/// parameter types are only NativeTypes, so we need to check this.
415-
bool _nativeTypeValid(DartType nativeType, {bool allowStructs: false}) {
416-
return convertNativeTypeToDartType(nativeType, allowStructs) != null;
417+
bool _nativeTypeValid(DartType nativeType,
418+
{bool allowStructs: false, allowHandle: false}) {
419+
return convertNativeTypeToDartType(nativeType, allowStructs, allowHandle) !=
420+
null;
417421
}
418422

419423
void _ensureIsStaticFunction(Expression node) {

runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,4 +790,126 @@ DART_EXPORT void ThreadPoolTest_BarrierSync(
790790
dart_enter_isolate(isolate);
791791
}
792792

793+
////////////////////////////////////////////////////////////////////////////////
794+
// Functions for handle tests.
795+
//
796+
// vmspecific_handle_test.dart
797+
798+
static void RunFinalizer(void* isolate_callback_data,
799+
Dart_WeakPersistentHandle handle,
800+
void* peer) {
801+
printf("Running finalizer for weak handle.\n");
802+
}
803+
804+
// Tests that passing handles through FFI calls works, and that the FFI call
805+
// sets up the VM state etc. correctly so that the handle API calls work.
806+
DART_EXPORT Dart_Handle PassObjectToC(Dart_Handle h) {
807+
// Can use "h" until this function returns.
808+
809+
// A persistent handle which outlives this call. Lifetime managed in C.
810+
auto persistent_handle = Dart_NewPersistentHandle(h);
811+
812+
Dart_Handle handle_2 = Dart_HandleFromPersistent(persistent_handle);
813+
Dart_DeletePersistentHandle(persistent_handle);
814+
if (Dart_IsError(handle_2)) {
815+
Dart_PropagateError(handle_2);
816+
}
817+
818+
Dart_Handle return_value;
819+
if (!Dart_IsNull(h)) {
820+
// A weak handle which outlives this call. Lifetime managed in C.
821+
auto weak_handle = Dart_NewWeakPersistentHandle(
822+
h, reinterpret_cast<void*>(0x1234), 64, RunFinalizer);
823+
return_value = Dart_HandleFromWeakPersistent(weak_handle);
824+
825+
// Deleting a weak handle is not required, it deletes itself on
826+
// finalization.
827+
// Deleting a weak handle cancels the finalizer.
828+
Dart_DeleteWeakPersistentHandle(weak_handle);
829+
} else {
830+
return_value = h;
831+
}
832+
833+
return return_value;
834+
}
835+
836+
DART_EXPORT void ClosureCallbackThroughHandle(void (*callback)(Dart_Handle),
837+
Dart_Handle closureHandle) {
838+
printf("ClosureCallbackThroughHandle %p %p\n", callback, closureHandle);
839+
callback(closureHandle);
840+
}
841+
842+
DART_EXPORT Dart_Handle ReturnHandleInCallback(Dart_Handle (*callback)()) {
843+
printf("ReturnHandleInCallback %p\n", callback);
844+
Dart_Handle handle = callback();
845+
if (Dart_IsError(handle)) {
846+
printf("callback() returned an error, propagating error\n");
847+
// Do C/C++ resource cleanup if needed, before propagating error.
848+
Dart_PropagateError(handle);
849+
}
850+
return handle;
851+
}
852+
853+
// Recurses til `i` reaches 0. Throws some Dart_Invoke in there as well.
854+
DART_EXPORT Dart_Handle HandleRecursion(Dart_Handle object,
855+
Dart_Handle (*callback)(int64_t),
856+
int64_t i) {
857+
printf("HandleRecursion %" Pd64 "\n", i);
858+
const bool do_invoke = i % 3 == 0;
859+
const bool do_gc = i % 7 == 3;
860+
if (do_gc) {
861+
Dart_ExecuteInternalCommand("gc-now", nullptr);
862+
}
863+
Dart_Handle result;
864+
if (do_invoke) {
865+
Dart_Handle method_name = Dart_NewStringFromCString("a");
866+
if (Dart_IsError(method_name)) {
867+
Dart_PropagateError(method_name);
868+
}
869+
Dart_Handle arg = Dart_NewInteger(i - 1);
870+
if (Dart_IsError(arg)) {
871+
Dart_PropagateError(arg);
872+
}
873+
printf("Dart_Invoke\n");
874+
result = Dart_Invoke(object, method_name, 1, &arg);
875+
} else {
876+
printf("callback\n");
877+
result = callback(i - 1);
878+
}
879+
if (do_gc) {
880+
Dart_ExecuteInternalCommand("gc-now", nullptr);
881+
}
882+
if (Dart_IsError(result)) {
883+
// Do C/C++ resource cleanup if needed, before propagating error.
884+
printf("Dart_PropagateError %" Pd64 "\n", i);
885+
Dart_PropagateError(result);
886+
}
887+
printf("return %" Pd64 "\n", i);
888+
return result;
889+
}
890+
891+
DART_EXPORT int64_t HandleReadFieldValue(Dart_Handle handle) {
892+
printf("HandleReadFieldValue\n");
893+
Dart_Handle field_name = Dart_NewStringFromCString("a");
894+
if (Dart_IsError(field_name)) {
895+
printf("Dart_PropagateError(field_name)\n");
896+
Dart_PropagateError(field_name);
897+
}
898+
Dart_Handle field_value = Dart_GetField(handle, field_name);
899+
if (Dart_IsError(field_value)) {
900+
printf("Dart_PropagateError(field_value)\n");
901+
Dart_PropagateError(field_value);
902+
}
903+
int64_t value;
904+
Dart_Handle err = Dart_IntegerToInt64(field_value, &value);
905+
if (Dart_IsError(err)) {
906+
Dart_PropagateError(err);
907+
}
908+
return value;
909+
}
910+
911+
DART_EXPORT Dart_Handle TrueHandle() {
912+
return Dart_True();
913+
}
914+
793915
} // namespace dart

runtime/docs/gc.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ With the mutator and marker running concurrently, the mutator could write a poin
6767

6868
The barrier is equivalent to
6969

70-
```
70+
```c++
7171
StorePoint(RawObject* source, RawObject** slot, RawObject* target) {
7272
*slot = target;
7373
if (target->IsSmi()) return;
@@ -84,7 +84,7 @@ StorePoint(RawObject* source, RawObject** slot, RawObject* target) {
8484
8585
But we combine the generational and incremental checks with a shift-and-mask.
8686
87-
```
87+
```c++
8888
enum HeaderBits {
8989
...
9090
kOldAndNotMarkedBit, // Incremental barrier target.

runtime/vm/class_id.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ namespace dart {
131131

132132
#define CLASS_LIST_FFI_TYPE_MARKER(V) \
133133
CLASS_LIST_FFI_NUMERIC(V) \
134-
V(Void)
134+
V(Void) \
135+
V(Handle)
135136

136137
#define CLASS_LIST_FFI(V) \
137138
V(Pointer) \

0 commit comments

Comments
 (0)