blob: ba75660beeca46ea4d3ba4b7d18af3d6cf9705e6 [file] [log] [blame]
// 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);