Index: sdk/lib/_internal/js_runtime/lib/js_helper.dart |
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
index b62765f76a3fd5f3a12547b461ef64f94256f6d8..e08c8329709e6be2f952fc8b66ad19fe6be6859b 100644 |
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
@@ -4,8 +4,6 @@ |
library _js_helper; |
-import 'dart:_async_await_error_codes' as async_error_codes; |
- |
import 'dart:_js_embedded_names' show |
DEFERRED_LIBRARY_URIS, |
DEFERRED_LIBRARY_HASHES, |
@@ -2050,6 +2048,15 @@ class UnknownJsTypeError extends Error { |
String toString() => _message.isEmpty ? 'Error' : 'Error: $_message'; |
} |
+/// A wrapper around an exception, much like the one created by [wrapException] |
+/// but with a pre-given stack-trace. |
+class ExceptionAndStackTrace { |
+ dynamic dartException; |
+ StackTrace stackTrace; |
+ |
+ ExceptionAndStackTrace(this.dartException, this.stackTrace); |
+} |
+ |
/** |
* Called from catch blocks in generated code to extract the Dart |
* exception from the thrown value. The thrown value may have been |
@@ -3889,373 +3896,3 @@ void badMain() { |
void mainHasTooManyParameters() { |
throw new MainError("'main' expects too many parameters."); |
} |
- |
-/// A wrapper around an exception, much like the one created by [wrapException] |
-/// but with a pre-given stack-trace. |
-class ExceptionAndStackTrace { |
- dynamic dartException; |
- StackTrace stackTrace; |
- |
- ExceptionAndStackTrace(this.dartException, this.stackTrace); |
-} |
- |
-/// Runtime support for async-await transformation. |
-/// |
-/// This function is called by a transformed function on each await and return |
-/// in the untransformed function, and before starting. |
-/// |
-/// If [object] is not a future it will be wrapped in a `new Future.value`. |
-/// |
-/// If [asyncBody] is [async_error_codes.SUCCESS]/[async_error_codes.ERROR] it |
-/// indicates a return or throw from the async function, and |
-/// complete/completeError is called on [completer] with [object]. |
-/// |
-/// Otherwise [asyncBody] is set up to be called when the future is completed |
-/// with a code [async_error_codes.SUCCESS]/[async_error_codes.ERROR] depending |
-/// on the success of the future. |
-/// |
-/// Returns the future of the completer for convenience of the first call. |
-dynamic asyncHelper(dynamic object, |
- dynamic /* int | WrappedAsyncBody */ bodyFunctionOrErrorCode, |
- Completer completer) { |
- if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
- completer.complete(object); |
- return; |
- } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
- // The error is a js-error. |
- completer.completeError(unwrapException(object), |
- getTraceFromException(object)); |
- return; |
- } |
- Future future = object is Future ? object : new Future.value(object); |
- future.then( |
- (result) => bodyFunctionOrErrorCode(async_error_codes.SUCCESS, result), |
- onError: (dynamic error, StackTrace stackTrace) { |
- ExceptionAndStackTrace wrappedException = |
- new ExceptionAndStackTrace(error, stackTrace); |
- bodyFunctionOrErrorCode(async_error_codes.ERROR, wrappedException); |
- }); |
- return completer.future; |
-} |
- |
-typedef void WrappedAsyncBody(int errorCode, dynamic result); |
- |
-WrappedAsyncBody _wrapJsFunctionForAsync(dynamic /* js function */ function) { |
- var protected = JS('', """ |
- // Invokes [function] with [errorCode] and [result]. |
- // |
- // If (and as long as) the invocation throws, calls [function] again, |
- // with an error-code. |
- function(errorCode, result) { |
- while (true) { |
- try { |
- #(errorCode, result); |
- break; |
- } catch (error) { |
- result = error; |
- errorCode = #; |
- } |
- } |
- }""", function, async_error_codes.ERROR); |
- return Zone.current.bindBinaryCallback((int errorCode, dynamic result) { |
- JS('', '#(#, #)', protected, errorCode, result); |
- }); |
-} |
- |
-/// Implements the runtime support for async* functions. |
-/// |
-/// Called by the transformed function for each original return, await, yield, |
-/// yield* and before starting the function. |
-/// |
-/// When the async* function wants to return it calls this function with |
-/// [asyncBody] == [async_error_codes.SUCCESS], the asyncStarHelper takes this |
-/// as signal to close the stream. |
-/// |
-/// When the async* function wants to signal that an uncaught error was thrown, |
-/// it calls this function with [asyncBody] == [async_error_codes.ERROR], |
-/// the streamHelper takes this as signal to addError [object] to the |
-/// [controller] and close it. |
-/// |
-/// If the async* function wants to do a yield or yield*, it calls this function |
-/// with [object] being an [IterationMarker]. |
-/// |
-/// In the case of a yield or yield*, if the stream subscription has been |
-/// canceled, schedules [asyncBody] to be called with |
-/// [async_error_codes.STREAM_WAS_CANCELED]. |
-/// |
-/// If [object] is a single-yield [IterationMarker], adds the value of the |
-/// [IterationMarker] to the stream. If the stream subscription has been |
-/// paused, return early. Otherwise schedule the helper function to be |
-/// executed again. |
-/// |
-/// If [object] is a yield-star [IterationMarker], starts listening to the |
-/// yielded stream, and adds all events and errors to our own controller (taking |
-/// care if the subscription has been paused or canceled) - when the sub-stream |
-/// is done, schedules [asyncBody] again. |
-/// |
-/// If the async* function wants to do an await it calls this function with |
-/// [object] not and [IterationMarker]. |
-/// |
-/// If [object] is not a [Future], it is wrapped in a `Future.value`. |
-/// The [asyncBody] is called on completion of the future (see [asyncHelper]. |
-void asyncStarHelper(dynamic object, |
- dynamic /* int | WrappedAsyncBody */ bodyFunctionOrErrorCode, |
- AsyncStarStreamController controller) { |
- if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
- // This happens on return from the async* function. |
- if (controller.isCanceled) { |
- controller.cancelationCompleter.complete(); |
- } else { |
- controller.close(); |
- } |
- return; |
- } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
- // The error is a js-error. |
- if (controller.isCanceled) { |
- controller.cancelationCompleter.completeError( |
- unwrapException(object), |
- getTraceFromException(object)); |
- } else { |
- controller.addError(unwrapException(object), |
- getTraceFromException(object)); |
- controller.close(); |
- } |
- return; |
- } |
- |
- if (object is IterationMarker) { |
- if (controller.isCanceled) { |
- bodyFunctionOrErrorCode(async_error_codes.STREAM_WAS_CANCELED, null); |
- return; |
- } |
- if (object.state == IterationMarker.YIELD_SINGLE) { |
- controller.add(object.value); |
- |
- scheduleMicrotask(() { |
- if (controller.isPaused) { |
- // We only suspend the thread inside the microtask in order to allow |
- // listeners on the output stream to pause in response to the just |
- // output value, and have the stream immediately stop producing. |
- controller.isSuspended = true; |
- return; |
- } |
- bodyFunctionOrErrorCode(null, async_error_codes.SUCCESS); |
- }); |
- return; |
- } else if (object.state == IterationMarker.YIELD_STAR) { |
- Stream stream = object.value; |
- // Errors of [stream] are passed though to the main stream. (see |
- // [AsyncStreamController.addStream]). |
- // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |
- controller.addStream(stream).then((_) { |
- // No check for isPaused here because the spec 17.16.2 only |
- // demands checks *before* each element in [stream] not after the last |
- // one. On the other hand we check for isCanceled, as that check happens |
- // after insertion of each element. |
- int errorCode = controller.isCanceled |
- ? async_error_codes.STREAM_WAS_CANCELED |
- : async_error_codes.SUCCESS; |
- bodyFunctionOrErrorCode(errorCode, null); |
- }); |
- return; |
- } |
- } |
- |
- Future future = object is Future ? object : new Future.value(object); |
- future.then( |
- (result) => bodyFunctionOrErrorCode(async_error_codes.SUCCESS, result), |
- onError: (error, StackTrace stackTrace) { |
- ExceptionAndStackTrace wrappedException = |
- new ExceptionAndStackTrace(error, stackTrace); |
- bodyFunctionOrErrorCode(async_error_codes.ERROR, wrappedException); |
- }); |
-} |
- |
-Stream streamOfController(AsyncStarStreamController controller) { |
- return controller.stream; |
-} |
- |
-/// A wrapper around a [StreamController] that keeps track of the state of |
-/// the execution of an async* function. |
-/// It can be in 1 of 3 states: |
-/// |
-/// - running/scheduled |
-/// - suspended |
-/// - canceled |
-/// |
-/// If yielding while the subscription is paused it will become suspended. And |
-/// only resume after the subscription is resumed or canceled. |
-class AsyncStarStreamController { |
- StreamController controller; |
- Stream get stream => controller.stream; |
- |
- /// True when the async* function has yielded while being paused. |
- /// When true execution will only resume after a `onResume` or `onCancel` |
- /// event. |
- bool isSuspended = false; |
- |
- bool get isPaused => controller.isPaused; |
- |
- Completer cancelationCompleter = null; |
- |
- /// True after the StreamSubscription has been cancelled. |
- /// When this is true, errors thrown from the async* body should go to the |
- /// [cancelationCompleter] instead of adding them to [controller], and |
- /// returning from the async function should complete [cancelationCompleter]. |
- bool get isCanceled => cancelationCompleter != null; |
- |
- add(event) => controller.add(event); |
- |
- addStream(Stream stream) { |
- return controller.addStream(stream, cancelOnError: false); |
- } |
- |
- addError(error, stackTrace) => controller.addError(error, stackTrace); |
- |
- close() => controller.close(); |
- |
- AsyncStarStreamController(WrappedAsyncBody body) { |
- |
- _resumeBody() { |
- scheduleMicrotask(() { |
- body(async_error_codes.SUCCESS, null); |
- }); |
- } |
- |
- controller = new StreamController( |
- onListen: () { |
- _resumeBody(); |
- }, onResume: () { |
- // Only schedule again if the async* function actually is suspended. |
- // Resume directly instead of scheduling, so that the sequence |
- // `pause-resume-pause` will result in one extra event produced. |
- if (isSuspended) { |
- isSuspended = false; |
- _resumeBody(); |
- } |
- }, onCancel: () { |
- // If the async* is finished we ignore cancel events. |
- if (!controller.isClosed) { |
- cancelationCompleter = new Completer(); |
- if (isSuspended) { |
- // Resume the suspended async* function to run finalizers. |
- isSuspended = false; |
- scheduleMicrotask(() { |
- body(async_error_codes.STREAM_WAS_CANCELED, null); |
- }); |
- } |
- return cancelationCompleter.future; |
- } |
- }); |
- } |
-} |
- |
-makeAsyncStarController(body) { |
- return new AsyncStarStreamController(body); |
-} |
- |
-class IterationMarker { |
- static const YIELD_SINGLE = 0; |
- static const YIELD_STAR = 1; |
- static const ITERATION_ENDED = 2; |
- static const UNCAUGHT_ERROR = 3; |
- |
- final value; |
- final int state; |
- |
- IterationMarker._(this.state, this.value); |
- |
- static yieldStar(dynamic /* Iterable or Stream */ values) { |
- return new IterationMarker._(YIELD_STAR, values); |
- } |
- |
- static endOfIteration() { |
- return new IterationMarker._(ITERATION_ENDED, null); |
- } |
- |
- static yieldSingle(dynamic value) { |
- return new IterationMarker._(YIELD_SINGLE, value); |
- } |
- |
- static uncaughtError(dynamic error) { |
- return new IterationMarker._(UNCAUGHT_ERROR, error); |
- } |
- |
- toString() => "IterationMarker($state, $value)"; |
-} |
- |
-class SyncStarIterator implements Iterator { |
- final dynamic _body; |
- |
- // If [runningNested] this is the nested iterator, otherwise it is the |
- // current value. |
- dynamic _current = null; |
- bool _runningNested = false; |
- |
- get current => _runningNested ? _current.current : _current; |
- |
- SyncStarIterator(this._body); |
- |
- _runBody() { |
- return JS('', ''' |
- // Invokes [body] with [errorCode] and [result]. |
- // |
- // If (and as long as) the invocation throws, calls [function] again, |
- // with an error-code. |
- (function(body) { |
- var errorValue, errorCode = #; |
- while (true) { |
- try { |
- return body(errorCode, errorValue); |
- } catch (error) { |
- errorValue = error; |
- errorCode = # |
- } |
- } |
- })(#)''', async_error_codes.SUCCESS, async_error_codes.ERROR, _body); |
- } |
- |
- |
- bool moveNext() { |
- if (_runningNested) { |
- if (_current.moveNext()) { |
- return true; |
- } else { |
- _runningNested = false; |
- } |
- } |
- _current = _runBody(); |
- if (_current is IterationMarker) { |
- if (_current.state == IterationMarker.ITERATION_ENDED) { |
- _current = null; |
- // Rely on [_body] to repeatedly return `ITERATION_ENDED`. |
- return false; |
- } else if (_current.state == IterationMarker.UNCAUGHT_ERROR) { |
- // Rely on [_body] to repeatedly return `UNCAUGHT_ERROR`. |
- // This is a wrapped exception, so we use JavaScript throw to throw it. |
- JS('', 'throw #', _current.value); |
- } else { |
- assert(_current.state == IterationMarker.YIELD_STAR); |
- _current = _current.value.iterator; |
- _runningNested = true; |
- return moveNext(); |
- } |
- } |
- return true; |
- } |
-} |
- |
-/// An Iterable corresponding to a sync* method. |
-/// |
-/// Each invocation of a sync* method will return a new instance of this class. |
-class SyncStarIterable extends IterableBase { |
- // This is a function that will return a helper function that does the |
- // iteration of the sync*. |
- // |
- // Each invocation should give a body with fresh state. |
- final dynamic /* js function */ _outerHelper; |
- |
- SyncStarIterable(this._outerHelper); |
- |
- Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
-} |