[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();
+}