Skip to content

[dart2wasm] Asynchronous exception catching isn't working properly #55347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
eyebrowsoffire opened this issue Apr 1, 2024 · 2 comments
Closed
Assignees
Labels
area-dart2wasm Issues for the dart2wasm compiler.

Comments

@eyebrowsoffire
Copy link
Contributor

There is some code in flutter framework that is causing unit test failures when compiled to wasm. The issue seems to lie in this file: https://github.com/flutter/flutter/blob/38b2c5bf410b9b2ab52812acf7bbd924bf9386e2/packages/flutter/lib/src/services/platform_channel.dart#L568

I have created a more minimal reproducible sample of code that demonstrates this issue:

class MyException implements Exception {
  MyException([this.message]);

  final String? message;

  @override
  String toString() => 'MyException($message)';
}

class MyOtherException implements Exception {
  MyOtherException([this.message]);

  final String? message;

  @override
  String toString() => 'MyOtherException($message)';
}

Future<String> asynchronouslyThrowException() async {
  throw MyException('throwing an error!');
}

Future<void> main() async {
  await _doIt(() => asynchronouslyThrowException());
}


Future<String?> _doIt(Future<dynamic> Function() handler) async {
  try {
    print('trying thing');
    return 'success: ${await handler()}';
  } on MyOtherException catch (e) {
    return e.message;
  } on MyException {
    print('returning null');
    return null;
  } catch (error) {
    return 'generic error';
  }
}

I don't know exactly what's going on yet, so it might be possible to distill it down further, but this does behave unexpectedly under dart2wasm. Running in the vm, we get the following output:

trying thing
returning null

But in dart2wasm, the MyException escapes out and is not caught by the on MyException block:

trying thing
assert_text.mjs:56 MyException(throwing an error!)
assert_text.mjs:56     at asynchronouslyThrowException inner (http://localhost:8000/assert_text.wasm:wasm-function[747]:0x1518f)
    at asynchronouslyThrowException (http://localhost:8000/assert_text.wasm:wasm-function[746]:0x15151)
    at main closure at file:///Users/jacksongardner/Desktop/scratch/assert_text.dart:25:15 (http://localhost:8000/assert_text.wasm:wasm-function[735]:0x14e82)
    at closure wrapper at file:///Users/jacksongardner/Desktop/scratch/assert_text.dart:25:15 trampoline (http://localhost:8000/assert_text.wasm:wasm-function[741]:0x14fa7)
    at _doIt inner (http://localhost:8000/assert_text.wasm:wasm-function[912]:0x17a12)
    at _doIt (http://localhost:8000/assert_text.wasm:wasm-function[742]:0x14fdc)
    at main inner (http://localhost:8000/assert_text.wasm:wasm-function[736]:0x14efc)
    at main (http://localhost:8000/assert_text.wasm:wasm-function[79]:0xc750)
assert_text.wasm:0xd023 Uncaught Exception {}
@a-siva a-siva added the area-dart2wasm Issues for the dart2wasm compiler. label Apr 1, 2024
@eyebrowsoffire
Copy link
Contributor Author

A slightly more distilled version:

class MyException implements Exception {
  MyException([this.message]);

  final String? message;

  @override
  String toString() => 'MyException($message)';
}

class MyOtherException implements Exception {
  MyOtherException([this.message]);

  final String? message;

  @override
  String toString() => 'MyOtherException($message)';
}

Future<String> asynchronouslyThrowException() async {
  throw MyException('throwing an error!');
}

Future<String?> main() async {
  try {
    print('trying thing');
    final future = asynchronouslyThrowException();
    print('got future');
    final result = await future;
    return 'success: $result';
  } on MyOtherException catch (e) {
    return e.message;
  } on MyException catch (e) {
    print('got $e, returning null');
    return null;
  } catch (error) {
    return 'generic error';
  }
}

It seems to be specific to having multiple on branches, and the order of the branches. It doesn't reproduce if I change the order of the on branches or if I just remove one of the on branches.

@osa1 osa1 self-assigned this Apr 4, 2024
osa1 added a commit to osa1/sdk that referenced this issue Apr 4, 2024
@osa1
Copy link
Member

osa1 commented Apr 4, 2024

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

copybara-service bot pushed a commit that referenced this issue May 29, 2024
This cherry-picks commits:

6de879e - [dart2wasm] Fix exception handling in async functions
7e237a1 - [dart2wasm] Small refactoring in async code generator
eabb2b3 - [dart2wasm] Catch JS exceptions in async functions
e44bc22 - [dart2wasm] Fix bug in restoration of `this` in async functions.
350954a - [dart2wasm] Fix `this` restoration code in sync* handling.
8ccb412 - [dart2wasm] Move type parameter bounds checks & parameter type check logic together with logic setting up variables
e7dde83 - [dart2wasm] Port VM fix for #52083 (sync*)
3863e78 - [dart2wasm] Move yield finder to a shared library
829261e - [dart2wasm] Move async compiler utilities to state_machine library
fab56db - [dart2wasm] Move common code generation routines to state_machine, fix sync*

Bugs: #55347, #55457, #51343, #51342
Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/368300
Cherry-pick-request: #55847
Change-Id: I0a4186533fbdf4c5727911295ad48696a90f715f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368300
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-dart2wasm Issues for the dart2wasm compiler.
Projects
None yet
Development

No branches or pull requests

3 participants