dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.27k stars 1.58k forks source link

[dart2wasm] RuntimeError: unreachable #56534

Closed huanghui1998hhh closed 2 months ago

huanghui1998hhh commented 3 months ago

Here is a sample on flutter, the error occurs when tapping "Go back" button on SecondScreen

import 'package:flutter/material.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Builder(builder: (context) {
            return TextButton(
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const SecondScreen(),
                ),
              ),
              child: const Text('Go to Second Screen'),
            );
          }),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  const SecondScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Screen'),
      ),
      body: Center(
        child: TextButton(
          onPressed: Navigator.of(context).pop,
          child: const Text('Go back to First Screen'),
        ),
      ),
    );
  }
}

My dart version: Dart SDK version: 3.5.0 (stable) (Tue Jul 30 02:17:59 2024 -0700) on "windows_x64"

And here is the log main.dart.wasm:0x8167b Uncaught RuntimeError: unreachable at #dummy function (:8080/ref struct) -> (ref null #Top) (http://localhost:8080/main.dart.wasm) at _InkResponseState.handleTap (main.dart.wasm:0xebddc) at _InkResponseState.handleTap tear-off trampoline (main.dart.wasm:0xebde9) at GestureRecognizer.invokeCallback (main.dart.wasm:0xe7907) at TapGestureRecognizer.handleTapUp (main.dart.wasm:0xe783e) at BaseTapGestureRecognizer._checkUp (main.dart.wasm:0xe7779) at BaseTapGestureRecognizer.handlePrimaryPointer (main.dart.wasm:0xe8277) at PrimaryPointerGestureRecognizer.handleEvent (main.dart.wasm:0xe8149) at PrimaryPointerGestureRecognizer.handleEvent tear-off trampoline (main.dart.wasm:0xe81ad) at PointerRouter._dispatchEventToRoutes closure at file:///E:/fvm-flutter-cache/versions/stable/packages/flutter/lib/src/gestures/pointer_router.dart:141:26 (main.dart.wasm:0x9bf01) $#dummy function (ref struct) -> (ref null #Top) @ main.dart.wasm:0x8167b $_InkResponseState.handleTap @ main.dart.wasm:0xebddc $_InkResponseState.handleTap tear-off trampoline @ main.dart.wasm:0xebde9 $GestureRecognizer.invokeCallback @ main.dart.wasm:0xe7907 $TapGestureRecognizer.handleTapUp @ main.dart.wasm:0xe783e $BaseTapGestureRecognizer._checkUp @ main.dart.wasm:0xe7779 $BaseTapGestureRecognizer.handlePrimaryPointer @ main.dart.wasm:0xe8277 $PrimaryPointerGestureRecognizer.handleEvent @ main.dart.wasm:0xe8149 $PrimaryPointerGestureRecognizer.handleEvent tear-off trampoline @ main.dart.wasm:0xe81ad $PointerRouter._dispatchEventToRoutes closure at file:///E:/fvm-flutter-cache/versions/stable/packages/flutter/lib/src/gestures/pointer_router.dart:141:26 @ main.dart.wasm:0x9bf01 $closure wrapper at file:///E:/fvm-flutter-cache/versions/stable/packages/flutter/lib/src/gestures/pointer_router.dart:141:26 trampoline @ main.dart.wasm:0x9bf82 $__Map&_HashFieldBase&MapMixin&_HashBase&_OperatorEqualsAndHashCode&_LinkedHashMapMixin.forEach @ main.dart.wasm:0x840c8 $PointerRouter._dispatchEventToRoutes @ main.dart.wasm:0x9be92 $PointerRouter.route @ main.dart.wasm:0x9bdb4 $_WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent @ main.dart.wasm:0x116ff0 $_WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue @ main.dart.wasm:0x9ba5c $_WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket @ main.dart.wasm:0x1366c4 $_WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket tear-off trampoline @ main.dart.wasm:0x13673c $invoke1 @ main.dart.wasm:0xa344b $ClickDebouncer._sendToFramework @ main.dart.wasm:0x11474f $ClickDebouncer.onPointerData @ main.dart.wasm:0x13a9d6 $ClickDebouncer.onPointerData tear-off trampoline @ main.dart.wasm:0x13ab54 $_PointerAdapter.setup closure at org-dartlang-sdk:///lib/_engine/engine/pointer_binding.dart:1034:58 @ main.dart.wasm:0x138ca8 $closure wrapper at org-dartlang-sdk:///lib/_engine/engine/pointer_binding.dart:1034:58 trampoline @ main.dart.wasm:0x1395d1 $_PointerAdapter._addPointerEventListener closure at org-dartlang-sdk:///lib/_engine/engine/pointer_binding.dart:936:41 @ main.dart.wasm:0x13b47c $closure wrapper at org-dartlang-sdk:///lib/_engine/engine/pointer_binding.dart:936:41 trampoline @ main.dart.wasm:0x13b4a5 $_BaseAdapter.addEventListener closure loggedHandler at org-dartlang-sdk:///lib/_engine/engine/pointer_binding.dart:518:25 @ main.dart.wasm:0x13b7a0 $closure wrapper at org-dartlang-sdk:///lib/_engine/engine/pointer_binding.dart:518:25 trampoline @ main.dart.wasm:0x13b7ca $_1588 @ main.dart.wasm:0x7d1ec (anonymous) @ main.dart.mjs:219
dart-github-bot commented 3 months ago

Summary: The user is experiencing a "RuntimeError: unreachable" in their Flutter app when tapping the "Go back" button on the SecondScreen. This error occurs during the gesture recognition process, specifically when handling a tap event.

mkustermann commented 3 months ago

/cc @osa1 Could you have a look at this?

osa1 commented 2 months ago

I've been debugging this. The unreachable is actually an illegal cast that wasm-opt converts into an unreachable. Somehow we pass a Closure-0-1 (a closure that expects no type arguments, one positional argument, and no named arguments) to an _InkResponseStateWidget.onTap field which expects Closure-0-0.

Because Closure-0-1 is a subtype of Closure-0-0 (I don't remember why this is, is this necessary?) this doesn't cause Wasm validation issues but it fails in runtime when we try to use Closure-0-1 as Closure-0-0.

Relevant closure and vtable types:

(type $#Closure-0-0 (;833;) (sub $#ClosureBase (struct
  (field $field0 i32)
  (field $field1 (mut i32))
  (field $field2 (mut (ref struct)))
  (field $field3 (ref $#Vtable-0-0))
  (field $field4 (ref $_FunctionType)))))

(type $#Closure-0-1 (;834;) (sub $#Closure-0-0 (struct
  (field $field0 i32)
  (field $field1 (mut i32))
  (field $field2 (mut (ref struct)))
  (field $field3 (ref $#Vtable-0-1))
  (field $field4 (ref $_FunctionType)))))

(type $#Vtable-0-0 (;822;) (sub $#VtableBase (struct (field $field0 (ref $type818)) (field $field1 (ref $type819)))))

(type $#Vtable-0-1 (;826;) (sub $#Vtable-0-0 (struct
  (field $field0 (ref $type818))
  (field $field1 (ref $type819))
  (field $field2 (ref $type820)))))

_InkResponseStateWidget:

(type $_InkResponseStateWidget (;2371;) (sub final $StatefulWidget (struct
  (field $field0 i32)
  (field $field1 (mut i32))
  (field $key (ref null $Key))
  (field $child (ref null $Widget))
  (field $onTap (ref null $#Closure-0-0))           ;; <----- we are passing Closure-0-1 here
  (field $onTapDown (ref null $#Closure-0-1))
  (field $onTapUp (ref null $#Closure-0-1))
  (field $onTapCancel (ref null $#Closure-0-0))
  (field $onDoubleTap (ref null $#Closure-0-0))
  (field $onLongPress (ref null $#Closure-0-0))
  (field $onSecondaryTap (ref null $#Closure-0-0))
  (field $onSecondaryTapUp (ref null $#Closure-0-1))
  (field $onSecondaryTapDown (ref null $#Closure-0-1))
  (field $onHighlightChanged (ref null $#Closure-0-1))
  (field $onHover (ref null $#Closure-0-1))
  (field $mouseCursor (ref null $MouseCursor))
  (field $containedInkWell i32)
  (field $highlightShape (ref $BoxShape))
  (field $radius (ref null $BoxedDouble))
  (field $borderRadius (ref null $BorderRadius))
  (field $customBorder (ref null $OutlinedBorder))
  (field $focusColor (ref null $Color))
  (field $hoverColor (ref null $Color))
  (field $highlightColor (ref null $Color))
  (field $overlayColor (ref null $_WidgetStatePropertyWith))
  (field $splashColor (ref null $Color))
  (field $splashFactory (ref null $InteractiveInkFeatureFactory))
  (field $enableFeedback i32)
  (field $excludeFromSemantics i32)
  (field $onFocusChange (ref null $#Closure-0-1))
  (field $autofocus i32)
  (field $focusNode (ref null $FocusNode))
  (field $canRequestFocus i32)
  (field $parentState (ref null $_InkResponseState))
  (field $getRectCallback (ref null $#Closure-0-1))
  (field $statesController (ref null $WidgetStatesController))
  (field $hoverDuration (ref null $Duration)))))
osa1 commented 2 months ago

The problematic closure seems to be coming from this tear-off in user code:

          onPressed: Navigator.of(context).pop,

If I replace this with a closure:

          onPressed: () => Navigator.of(context).pop(),

Then the crash disappears.

The pop member has a type argument, so the tear-off is an instantiation closure: https://github.com/flutter/flutter/blob/2ed3dab361a00ed40c5d9dbca3f3383eb231153e/packages/flutter/lib/src/widgets/navigator.dart#L5375-L5376

Explicitly instantiating it with Navigator.of(context).pop<dynamic> does not fix the crash.

osa1 commented 2 months ago

Minimal repro:

class A {
  void test<T>([T? x]) {}
}

void main() {
  final a = A();
  void Function() x = a.test;
  x();
}

Output:

wasm-function[771]:0x15c33: RuntimeError: illegal cast
RuntimeError: illegal cast
    at #InstantiationTrampoline-1-0 (wasm://wasm/0008aca2:wasm-function[771]:0x15c33)
    at main (wasm://wasm/0008aca2:wasm-function[756]:0x15a79)
    at main tear-off trampoline (wasm://wasm/0008aca2:wasm-function[758]:0x15a8d)
    at _invokeMain (wasm://wasm/0008aca2:wasm-function[67]:0xb9ac)
    at Module.invoke (/usr/local/google/home/omersa/dart/sdk/test/test.mjs:323:26)
    at main (/usr/local/google/home/omersa/dart/sdk/sdk/pkg/dart2wasm/bin/run_wasm.js:407:21)
    at async action (/usr/local/google/home/omersa/dart/sdk/sdk/pkg/dart2wasm/bin/run_wasm.js:350:37)
    at async eventLoop (/usr/local/google/home/omersa/dart/sdk/sdk/pkg/dart2wasm/bin/run_wasm.js:327:9)
osa1 commented 2 months ago

Fix in https://dart-review.googlesource.com/c/sdk/+/381641.

huanghui1998hhh commented 2 months ago

Thanks for fix ! @osa1