| Index: sdk/lib/_internal/compiler/js_lib/js_helper.dart
|
| diff --git a/sdk/lib/_internal/compiler/js_lib/js_helper.dart b/sdk/lib/_internal/compiler/js_lib/js_helper.dart
|
| index 97b1a9d9ef1a2dcf9162f8dd4af58573679c137d..38e94d539a22b0ead30c11d9969c0ff605bcd6a9 100644
|
| --- a/sdk/lib/_internal/compiler/js_lib/js_helper.dart
|
| +++ b/sdk/lib/_internal/compiler/js_lib/js_helper.dart
|
| @@ -25,7 +25,8 @@ import 'dart:_isolate_helper' show
|
| enterJsAsync,
|
| isWorker;
|
|
|
| -import 'dart:async' show Future, DeferredLoadException, Completer;
|
| +import 'dart:async'
|
| + show Future, DeferredLoadException, Completer, StreamController;
|
|
|
| import 'dart:_foreign_helper' show
|
| DART_CLOSURE_TO_JS,
|
| @@ -3456,3 +3457,173 @@ void badMain() {
|
| void mainHasTooManyParameters() {
|
| throw new MainError("'main' expects too many parameters.");
|
| }
|
| +
|
| +/// Runtime support for async-await transformation.
|
| +// If helperCallback is null we complete the completer with object.
|
| +// If helperCallback or errorCallback throws we complete the completer with
|
| +// the error.
|
| +thenHelper(object,
|
| + helperCallback,
|
| + Completer completer,
|
| + errorCallback) {
|
| + if (helperCallback == null) {
|
| + completer.complete(object);
|
| + return;
|
| + }
|
| + Future future = object is Future ? object : new Future.value(object);
|
| + future.then(new WrappedJsFunction(helperCallback, completer),
|
| + onError: new WrappedJsFunction(errorCallback, completer));
|
| + return completer.future;
|
| +}
|
| +
|
| +// Used to avoid creating two identical closure classes in [thenHelper].
|
| +class WrappedJsFunction {
|
| + final function;
|
| + final Completer completer;
|
| +
|
| + WrappedJsFunction(this.function, this.completer);
|
| +
|
| + call(result) {
|
| + try {
|
| + JS('', '#(#)', function, result);
|
| + } catch (e, st) {
|
| + completer.completeError(e, st);
|
| + }
|
| + }
|
| +}
|
| +
|
| +// If helperCallback is null we close the stream.
|
| +// If helperCallback or errorCallback throws we add the error to the stream
|
| +// If the object is a YIELD_SINGLE we add the object to the stream.
|
| +streamHelper(object,
|
| + helperCallback,
|
| + StreamController controller,
|
| + errorCallback) {
|
| + print("Called with $controller");
|
| + if (helperCallback == null) {
|
| + // This happens on return from the async* function.
|
| + controller.close();
|
| + return;
|
| + }
|
| +
|
| + if (object is IterationMarker) {
|
| + if (object.state == IterationMarker.YIELD_SINGLE) {
|
| + controller.add(object.value);
|
| + } else if (object.state == IterationMarker.YIELD_STAR) {
|
| + // TODO(sigurdm): Handle yield star from async*.
|
| + }
|
| + if (controller.isPaused) {
|
| + return;
|
| + } else if (controller.isCanceled) {
|
| + // TODO(sigurdm): Handle cancelled streams.
|
| + }
|
| + object = null;
|
| + }
|
| +
|
| + Future future = object is Future ? object : new Future.value(object);
|
| + future.then(new WrappedJsFunctionForStream(helperCallback, controller),
|
| + onError: errorCallback = null
|
| + ? null
|
| + : new WrappedJsFunctionForStream(errorCallback,
|
| + controller));
|
| + return controller.stream;
|
| +}
|
| +
|
| +
|
| +makeAsyncStarController(helperCallBack) {
|
| + StreamControllor c;
|
| + c = new StreamController(
|
| + onResume: () {
|
| + // TODO(sigurdm): The errorCallback should not be null.
|
| + streamHelper(null, helperCallBack, c, null);
|
| + });
|
| + return c;
|
| +}
|
| +
|
| +// Used to avoid creating two identical closure classes in [streamHelper].
|
| +class WrappedJsFunctionForStream {
|
| + final function;
|
| + final StreamController controller;
|
| +
|
| + WrappedJsFunctionForStream(this.function, this.controller);
|
| +
|
| + call(result) {
|
| + print(controller);
|
| + try {
|
| + JS('', "#(#)", function, result);
|
| + } catch (e, st) {
|
| + controller.addError(e, st);
|
| + }
|
| + }
|
| +}
|
| +
|
| +class IterationMarker {
|
| + static const YIELD_SINGLE = 0;
|
| + static const YIELD_STAR = 1;
|
| + static const ITERATION_ENDED = 2;
|
| +
|
| + final value;
|
| + final int state;
|
| +
|
| + IterationMarker._(this.state, this.value);
|
| +
|
| + static yieldStar(Iterable iterable) {
|
| + return new IterationMarker._(YIELD_STAR, iterable);
|
| + }
|
| +
|
| + static endOfIteration() {
|
| + return new IterationMarker._(ITERATION_ENDED, null);
|
| + }
|
| +
|
| + static yieldSingle(value) {
|
| + return new IterationMarker._(YIELD_SINGLE, value);
|
| + }
|
| +
|
| + toString() => "IterationMarker($state, $value)";
|
| +}
|
| +
|
| +class SyncStarIterator implements Iterator {
|
| + final helper;
|
| +
|
| + var _current = null;
|
| + bool stillRunning = true;
|
| + bool runningNested = false;
|
| +
|
| + get current => runningNested ? _current.current : _current;
|
| +
|
| + SyncStarIterator(helper)
|
| + : helper = ((arg) => JS('', '#(#)', helper, arg));
|
| +
|
| + bool moveNext() {
|
| + if (!stillRunning) return false;
|
| + if (runningNested) {
|
| + if (_current.moveNext()) {
|
| + return true;
|
| + } else {
|
| + runningNested = false;
|
| + }
|
| + }
|
| + _current = helper(null);
|
| + if (_current is IterationMarker) {
|
| + if (_current.state == IterationMarker.ITERATION_ENDED) {
|
| + _current = null;
|
| + stillRunning = false;
|
| + } else {
|
| + assert(_current.state == IterationMarker.YIELD_STAR);
|
| + _current = _current.value.iterator;
|
| + runningNested = true;
|
| + return moveNext();
|
| + }
|
| + }
|
| + return stillRunning;
|
| + }
|
| +}
|
| +
|
| +class SyncStarIterable extends IterableBase {
|
| + final outerHelper;
|
| +
|
| + SyncStarIterable(this.outerHelper);
|
| +
|
| + Iterator get iterator => new SyncStarIterator(JS('', '#()', outerHelper));
|
| +}
|
| +
|
|
|