| // Copyright (c) 2022, 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. |
| |
| // Machinery for async and await. |
| // |
| // The implementation is based on two mechanisms in the JS Promise integration: |
| // |
| // The export wrapper: Allocates a new stack and calls the wrapped export on the |
| // new stack, passing a suspender object as an extra first argument that |
| // represents the new stack. |
| // |
| // The import wrapper: Takes a suspender object as an extra first argument and |
| // calls the wrapped import. If the wrapped import returns a `Promise`, the |
| // current stack is suspended, and the `Promise` is forwarded to the |
| // corresponding call of the export wrapper, where execution resumes on the |
| // original stack. When the `Promise` is resolved, execution resumes on the |
| // suspended stack, with the call to the import wrapper returning the value the |
| // `Promise` was resolved with. |
| // |
| // The call sequence when calling an async function is: |
| // |
| // Caller |
| // -> Outer (function specific, generated by `generateAsyncWrapper`) |
| // -> `_asyncHelper` |
| // -> `_callAsyncBridge` (imported JS function) |
| // -> `_asyncBridge` (via the Promise integration export wrapper) |
| // -> `_asyncBridge2` (intrinsic function) |
| // -> Stub (function specific, generated by `generateAsyncWrapper`) |
| // -> Inner (contains implementation, generated from async inner reference) |
| // |
| // The call sequence on await is: |
| // |
| // Function containing await |
| // -> `_awaitHelper` |
| // -> `_futurePromise` (via the Promise integration import wrapper) |
| // -> `new Promise` |
| // -> `Promise` constructor callback |
| // -> `_awaitCallback` |
| // -> `Future.then` |
| // `futurePromise` returns the newly created `Promise`, suspending the |
| // current execution. |
| // |
| // When the `Future` completes: |
| // |
| // `Future.then` callback |
| // -> `_callResolve` (imported JS function) |
| // -> `Promise` resolve function |
| // Resolving the `Promise` causes the suspended execution to resume. |
| |
| import 'dart:_internal' show patch, scheduleCallback, unsafeCastOpaque; |
| |
| import 'dart:wasm'; |
| |
| @pragma("wasm:entry-point") |
| Future<T> _asyncHelper<T>(WasmEqRef args) { |
| Completer<T> completer = Completer(); |
| _callAsyncBridge(args, completer); |
| return completer.future; |
| } |
| |
| @pragma("wasm:import", "dart2wasm.callAsyncBridge") |
| external void _callAsyncBridge(WasmEqRef args, Completer<Object?> completer); |
| |
| @pragma("wasm:export", "\$asyncBridge") |
| WasmAnyRef? _asyncBridge( |
| WasmExternRef? stack, WasmDataRef args, Completer<Object?> completer) { |
| try { |
| Object? result = _asyncBridge2(args, stack); |
| completer.complete(result); |
| } catch (e, s) { |
| completer.completeError(e, s); |
| } |
| } |
| |
| external Object? _asyncBridge2(WasmDataRef args, WasmExternRef? stack); |
| |
| class _FutureError { |
| final Object exception; |
| final StackTrace stackTrace; |
| |
| _FutureError(this.exception, this.stackTrace); |
| } |
| |
| @pragma("wasm:entry-point") |
| Object? _awaitHelper(Object? operand, WasmExternRef? stack) { |
| if (operand is! Future) return operand; |
| WasmExternRef futureRef = WasmAnyRef.fromObject(operand).externalize(); |
| Object? result = |
| unsafeCastOpaque(_futurePromise(stack, futureRef).internalize()); |
| if (result is _FutureError) { |
| // TODO(askesc): Combine stack traces |
| throw result.exception; |
| } |
| return result; |
| } |
| |
| @pragma("wasm:import", "dart2wasm.futurePromise") |
| external WasmExternRef? _futurePromise( |
| WasmExternRef? stack, WasmExternRef? future); |
| |
| @pragma("wasm:export", "\$awaitCallback") |
| void _awaitCallback(Future<Object?> future, WasmAnyRef resolve) { |
| future.then((value) { |
| _callResolve(resolve, value); |
| }, onError: (exception, stackTrace) { |
| _callResolve(resolve, _FutureError(exception, stackTrace)); |
| }); |
| } |
| |
| @pragma("wasm:import", "dart2wasm.callResolve") |
| external void _callResolve(WasmAnyRef resolve, Object? result); |