Chromium Code Reviews| Index: runtime/lib/core_patch.dart |
| =================================================================== |
| --- runtime/lib/core_patch.dart (revision 44030) |
| +++ runtime/lib/core_patch.dart (working copy) |
| @@ -26,11 +26,102 @@ |
| String toString() => _enum_names[index]; |
| } |
| -typedef bool SyncGeneratorCallback(Iterator iterator); |
| +// _AsyncStarStreamController is used by the compiler to implement |
| +// async* generator functions. |
| +class _AsyncStarStreamController { |
| + StreamController controller; |
| + Function asyncStarBody; |
| + bool isAdding = false; |
| + bool isCancelled = false; |
| + bool onListenReceived = false; |
| + bool isClosed = false; |
| + |
| + Stream get stream => controller.stream; |
| + |
| + // Adds element to steam, returns true iff the caller should suspend |
|
Lasse Reichstein Nielsen
2015/02/26 14:54:52
suspend -> terminate/return from?
hausner
2015/02/26 21:54:07
Correct.
|
| + // execution of the generator. |
| + bool add(event) { |
| + if (!onListenReceived) _fatal("yield before stream is listened to!"); |
| + // If stream is cancelled, tell caller to exit the async generator. |
|
Lasse Reichstein Nielsen
2015/02/26 14:54:52
The spec says that you should pause first if the s
hausner
2015/02/26 21:54:07
I am leaving this as a TODO for later. The body cl
Lasse Reichstein Nielsen
2015/02/27 07:51:12
Looks fine. It basically what I did in an attempt
|
| + if (!controller.hasListener) { |
| + return true; |
| + } |
| + controller.add(event); |
| + if (!controller.isPaused) { |
| + scheduleMicrotask(asyncStarBody); |
| + } |
| + return false; |
| + } |
| + |
| + // Adds stream, tells caller to suspend execution of the generator |
| + // function. The generator will be scheduled again when all of the |
| + // elements of the added stream have been consumed. |
| + bool addStream(Stream stream) { |
| + assert(onListenReceived); |
| + // If stream is cancelled, tell caller to exit the async generator. |
| + if (!controller.hasListener) return true; |
| + isAdding = true; |
| + var whenDoneAdding = |
| + controller.addStream(stream as Stream, cancelOnError: false); |
| + whenDoneAdding.then((_) { |
| + isAdding = false; |
| + if (!controller.isPaused) { |
| + scheduleMicrotask(asyncStarBody); |
| + } |
| + }); |
| + return false; |
| + } |
| + |
| + addError(error, stackTrace) { |
| + if (!onListenReceived) _fatal("yield before stream is listened to!"); |
| + // If stream is cancelled, tell caller to exit the async generator. |
| + if (!controller.hasListener) return true; |
|
Lasse Reichstein Nielsen
2015/02/26 14:54:52
I think an uncaught exception reaching the stream
hausner
2015/02/26 21:54:08
Yes, this method is only called from the implicit
|
| + controller.addError(error, stackTrace); |
| + if (!controller.isPaused) { |
| + scheduleMicrotask(asyncStarBody); |
| + return false; |
| + } |
| + |
|
Lasse Reichstein Nielsen
2015/02/26 14:54:52
It returns null if the controller isn't paused - w
hausner
2015/02/26 21:54:07
Done.
|
| + } |
| + |
| + close() { |
| + isClosed = true; |
| + controller.close(); |
| + } |
| + |
| + _AsyncStarStreamController(this.asyncStarBody) { |
| + controller = new StreamController(onListen: this.onListen, |
| + onResume: this.onResume, |
| + onCancel: this.onCancel); |
| + } |
| + |
| + onListen() { |
| + assert(!onListenReceived); |
| + onListenReceived = true; |
| + scheduleMicrotask(asyncStarBody); |
| + } |
| + |
| + onResume() { |
| + if (!isAdding) { |
| + scheduleMicrotask(asyncStarBody); |
|
Lasse Reichstein Nielsen
2015/02/26 14:54:52
This is still error-prone if you get another resum
hausner
2015/02/26 21:54:07
Ok, done. I was thinking about this possibility ye
|
| + } |
| + } |
| + |
| + onCancel() { |
| + if (!isClosed) { |
|
Lasse Reichstein Nielsen
2015/02/26 14:54:52
You can use controller.isClosed. It's set to true
hausner
2015/02/26 21:54:07
Done.
|
| + scheduleMicrotask(asyncStarBody); |
| + } |
| + } |
| +} |
| + |
| + |
| // _SyncIterable and _syncIterator are used by the compiler to |
| // implement sync* generator functions. A sync* generator allocates |
| // and returns a new _SyncIterable object. |
| + |
| +typedef bool SyncGeneratorCallback(Iterator iterator); |
| + |
| class _SyncIterable extends IterableBase { |
| // moveNextFn is the closurized body of the generator function. |
| final SyncGeneratorCallback moveNextFn; |