[vm/ffi] FFI static type checks to handle invalid function types Fixes FFI compiler crash on cascading errors. Bug: https://github.com/flutter/flutter/issues/188175 TEST=tests/ffi/static_checks/regress_flutter_188175_test.dart Change-Id: Ifc454ba293c772eda839fcf25251bfab243ae25d Cq-Include-Trybots: dart/try:vm-aot-linux-debug-arm64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-debug-arm64-try,vm-aot-mac-debug-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-asan-mac-release-arm64-try,vm-asan-win-release-x64-try,vm-dyn-linux-debug-x64-try,vm-dyn-mac-debug-arm64-try,vm-ffi-dyn-mac-debug-simarm64_arm64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-tsan-mac-release-arm64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-ubsan-mac-release-arm64-try,vm-ubsan-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/515880 Auto-Submit: Daco Harkes <dacoharkes@google.com> Reviewed-by: Liam Appelbe <liama@google.com> Commit-Queue: Liam Appelbe <liama@google.com>
diff --git a/pkg/vm/lib/modular/transformations/ffi/common.dart b/pkg/vm/lib/modular/transformations/ffi/common.dart index f721348..a162665 100644 --- a/pkg/vm/lib/modular/transformations/ffi/common.dart +++ b/pkg/vm/lib/modular/transformations/ffi/common.dart
@@ -1946,6 +1946,10 @@ variance: Variance.covariant, )!; if (env.isSubtypeOf(dartType, correspondingDartType)) { + if (correspondingDartType is FunctionType && + dartType is! FunctionType) { + throw FfiStaticTypeError(); + } return correspondingDartType; } }
diff --git a/pkg/vm/lib/modular/transformations/ffi/use_sites.dart b/pkg/vm/lib/modular/transformations/ffi/use_sites.dart index 4b4063a..3d12bca 100644 --- a/pkg/vm/lib/modular/transformations/ffi/use_sites.dart +++ b/pkg/vm/lib/modular/transformations/ffi/use_sites.dart
@@ -651,6 +651,9 @@ isLeaf, reportErrorOn: node, ); + if (dartType is! FunctionType) { + throw FfiStaticTypeError(); + } return _replaceLookupFunction(node); } else if (target == asFunctionMethod) { if (_isMissingArguments(node)) { @@ -679,6 +682,9 @@ isLeaf, reportErrorOn: node, ); + if (dartType is! FunctionType) { + throw FfiStaticTypeError(); + } final DartType nativeSignature = nativeType.typeArguments[0]; return _replaceAsFunction( @@ -687,7 +693,7 @@ nativeType, ]), nativeSignature: nativeSignature, - dartSignature: dartType as FunctionType, + dartSignature: dartType, isLeaf: isLeaf, fileOffset: node.fileOffset, ); @@ -716,16 +722,17 @@ final DartType dartType = func.getStaticType(staticTypeContext!); ensureNativeTypeValid(nativeType, node); - final ffiFuncType = - ensureNativeTypeMatch( - FfiTypeCheckDirection.dartToNative, - nativeType, - dartType, - node, - ) - as FunctionType; + final ffiFuncType = ensureNativeTypeMatch( + FfiTypeCheckDirection.dartToNative, + nativeType, + dartType, + node, + ); + if (ffiFuncType is! FunctionType || dartType is! FunctionType) { + throw FfiStaticTypeError(); + } - final funcType = dartType as FunctionType; + final funcType = dartType; // Check return type. if (ffiFuncType.returnType != VoidType()) { @@ -1308,16 +1315,19 @@ } ensureNativeTypeValid(nativeType, node); - final ffiFuncType = - ensureNativeTypeMatch( - FfiTypeCheckDirection.dartToNative, - nativeType, - dartType, - node, - ) - as FunctionType; + final ffiFuncType = ensureNativeTypeMatch( + FfiTypeCheckDirection.dartToNative, + nativeType, + dartType, + node, + ); + if (ffiFuncType is! FunctionType || + dartType is! FunctionType || + node.arguments.types[0] is! FunctionType) { + throw FfiStaticTypeError(); + } - final funcType = dartType as FunctionType; + final funcType = dartType; // Check `exceptionalReturn`'s type. final Class expectedReturnClass =
diff --git a/tests/ffi/static_checks/regress_flutter_188175_test.dart b/tests/ffi/static_checks/regress_flutter_188175_test.dart new file mode 100644 index 0000000..e00f944 --- /dev/null +++ b/tests/ffi/static_checks/regress_flutter_188175_test.dart
@@ -0,0 +1,33 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Repro for https://github.com/flutter/flutter/issues/188175. + +import 'dart:ffi'; + +abstract class MyInterface { + void myMethod(); +} + +// dart format off +class MyClass implements MyInterface { +// ^^^^^^^ +// [analyzer] COMPILE_TIME_ERROR.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER +// [cfe] The non-abstract class 'MyClass' is missing implementations for these members: +} +// dart format on + +class Wrapper<T extends MyInterface> { + final T client; + Wrapper(this.client); + + void register() { + NativeCallable<Void Function()>.listener(client.myMethod); + } +} + +void main() { + final w = Wrapper<MyClass>(MyClass()); + w.register(); +}