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)); |
+} |
+ |