Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library _js_helper; | 5 library _js_helper; |
| 6 | 6 |
| 7 import 'dart:_js_embedded_names' show | 7 import 'dart:_js_embedded_names' show |
| 8 GET_TYPE_FROM_NAME, | 8 GET_TYPE_FROM_NAME, |
| 9 GET_ISOLATE_TAG, | 9 GET_ISOLATE_TAG, |
| 10 INTERCEPTED_NAMES, | 10 INTERCEPTED_NAMES, |
| 11 INTERCEPTORS_BY_TAG, | 11 INTERCEPTORS_BY_TAG, |
| 12 LEAF_TAGS, | 12 LEAF_TAGS, |
| 13 METADATA, | 13 METADATA, |
| 14 DEFERRED_LIBRARY_URIS, | 14 DEFERRED_LIBRARY_URIS, |
| 15 DEFERRED_LIBRARY_HASHES, | 15 DEFERRED_LIBRARY_HASHES, |
| 16 INITIALIZE_LOADED_HUNK, | 16 INITIALIZE_LOADED_HUNK, |
| 17 IS_HUNK_LOADED, | 17 IS_HUNK_LOADED, |
| 18 IS_HUNK_INITIALIZED, | 18 IS_HUNK_INITIALIZED, |
| 19 NATIVE_SUPERCLASS_TAG_NAME; | 19 NATIVE_SUPERCLASS_TAG_NAME; |
| 20 | 20 |
| 21 import 'dart:collection'; | 21 import 'dart:collection'; |
| 22 import 'dart:_isolate_helper' show | 22 import 'dart:_isolate_helper' show |
| 23 IsolateNatives, | 23 IsolateNatives, |
| 24 leaveJsAsync, | 24 leaveJsAsync, |
| 25 enterJsAsync, | 25 enterJsAsync, |
| 26 isWorker; | 26 isWorker; |
| 27 | 27 |
| 28 import 'dart:async' show Future, DeferredLoadException, Completer; | 28 import 'dart:async' |
|
floitsch
2015/02/05 20:17:23
maybe not your code, but should be easy:
move "sho
sigurdm
2015/02/06 14:26:34
Done.
| |
| 29 show Future, | |
| 30 DeferredLoadException, | |
| 31 Completer, | |
| 32 StreamController, | |
| 33 Stream, | |
| 34 StreamSubscription, | |
| 35 scheduleMicrotask; | |
| 29 | 36 |
| 30 import 'dart:_foreign_helper' show | 37 import 'dart:_foreign_helper' show |
| 31 DART_CLOSURE_TO_JS, | 38 DART_CLOSURE_TO_JS, |
| 32 JS, | 39 JS, |
| 33 JS_CALL_IN_ISOLATE, | 40 JS_CALL_IN_ISOLATE, |
| 34 JS_CONST, | 41 JS_CONST, |
| 35 JS_CURRENT_ISOLATE, | 42 JS_CURRENT_ISOLATE, |
| 36 JS_CURRENT_ISOLATE_CONTEXT, | 43 JS_CURRENT_ISOLATE_CONTEXT, |
| 37 JS_DART_OBJECT_CONSTRUCTOR, | 44 JS_DART_OBJECT_CONSTRUCTOR, |
| 38 JS_EFFECT, | 45 JS_EFFECT, |
| (...skipping 3416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3455 throw new MainError("No top-level function named 'main'."); | 3462 throw new MainError("No top-level function named 'main'."); |
| 3456 } | 3463 } |
| 3457 | 3464 |
| 3458 void badMain() { | 3465 void badMain() { |
| 3459 throw new MainError("'main' is not a function."); | 3466 throw new MainError("'main' is not a function."); |
| 3460 } | 3467 } |
| 3461 | 3468 |
| 3462 void mainHasTooManyParameters() { | 3469 void mainHasTooManyParameters() { |
| 3463 throw new MainError("'main' expects too many parameters."); | 3470 throw new MainError("'main' expects too many parameters."); |
| 3464 } | 3471 } |
| 3472 | |
| 3473 /// Runtime support for async-await transformation. | |
| 3474 /// | |
| 3475 /// This function is called by a transformed function on each await and return | |
| 3476 /// in the untransformed function, and before starting. | |
| 3477 /// | |
| 3478 /// If [object] is not a future it will be wrapped in a `new Future.value`. | |
| 3479 /// | |
| 3480 /// If [helperCallback] is null it indicates a return from the async function, | |
| 3481 /// and we complete the completer with object. | |
| 3482 /// | |
| 3483 /// Otherwise [helperCallback] is set up to be called when the future is | |
| 3484 /// successfull and [errorCallback] if it is completed with an error. | |
| 3485 /// | |
| 3486 /// If helperCallback or errorCallback throws we complete the completer with the | |
| 3487 /// error. | |
| 3488 /// | |
| 3489 /// Returns the future of the completer for convenience of the first call. | |
| 3490 dynamic thenHelper(dynamic object, | |
| 3491 dynamic /* js function */ helperCallback, | |
| 3492 Completer completer, | |
| 3493 dynamic /* js function */ errorCallback) { | |
| 3494 if (helperCallback == null) { | |
| 3495 completer.complete(object); | |
| 3496 return; | |
| 3497 } | |
| 3498 Future future = object is Future ? object : new Future.value(object); | |
| 3499 future.then(_wrapJsFunctionForThenHelper(helperCallback, completer), | |
| 3500 onError: (errorCallback == null) | |
| 3501 ? null | |
| 3502 : _wrapJsFunctionForThenHelper(errorCallback, completer)); | |
| 3503 return completer.future; | |
| 3504 } | |
| 3505 | |
| 3506 Function _wrapJsFunctionForThenHelper(dynamic /* js function */ function, | |
| 3507 Completer completer) { | |
| 3508 return (result) { | |
| 3509 try { | |
| 3510 JS('', '#(#)', function, result); | |
| 3511 } catch (e, st) { | |
| 3512 completer.completeError(e, st); | |
| 3513 } | |
| 3514 }; | |
| 3515 } | |
| 3516 | |
| 3517 | |
| 3518 /// Implements the runtime support for async* functions. | |
| 3519 /// | |
| 3520 /// Called by the transformed function for each original return, await, yield, | |
| 3521 /// yield* and before starting the function. | |
| 3522 /// | |
| 3523 /// When the async* function wants to return it call this with | |
|
floitsch
2015/02/05 20:17:23
calls this functions. The streamHelper takes this
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3524 /// [helperCallback] == null, and the stream is closed. | |
| 3525 /// | |
| 3526 /// If the async* function wants to do a yield or yield* it will call this with | |
|
floitsch
2015/02/05 20:17:23
it calls this function
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3527 /// [object] being an [IterationMarker]. In this case [errorCallback] has a | |
| 3528 /// special meaning; it is a callback that will run all enclosing finalizers. | |
| 3529 /// | |
| 3530 /// In the case of a yield or yield*, if the stream subscription has been | |
| 3531 /// canceled [errorCallback] is scheduled. | |
| 3532 /// | |
| 3533 /// If [object] is a single-yield [IterationMarker], add the value of the | |
|
floitsch
2015/02/05 20:17:23
adds
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3534 /// [IterationMarker] to the stream, next: if the stream subscription has been | |
|
floitsch
2015/02/05 20:17:23
to the stream. If the stream...
(unless I'm misun
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3535 /// paused, return early. Otherwise schedule the helper function to be | |
| 3536 /// executed again. | |
| 3537 /// | |
| 3538 /// If [object] is a yield-star [IterationMarker], start listening to the | |
|
floitsch
2015/02/05 20:17:23
starts ... and adds ... schedules
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3539 /// yielded stream, and add all events and errors to our own controller (taking | |
| 3540 /// care if the subscription has been paused or canceled) - when the sub-stream | |
| 3541 /// is done, schedule [helperCallback] again. | |
| 3542 /// | |
| 3543 /// If the async* function wants to do an await it will call this with [object] | |
|
floitsch
2015/02/05 20:17:23
calls this function with [object] not an [Iteratio
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3544 /// not being an [IterationMarker]. | |
| 3545 /// | |
| 3546 /// If [object] is not a [Future], it is wrapped in a `Future.value`. | |
| 3547 /// Then set up [helperCallback] to be called on successfull completion of the | |
|
floitsch
2015/02/05 20:17:23
The [helperCallback] is called on successful compl
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3548 /// future. | |
| 3549 /// | |
| 3550 /// If [helperCallback] or [errorCallback] throws the error is added to the | |
| 3551 /// stream. | |
| 3552 dynamic streamHelper(dynamic object, | |
| 3553 dynamic /* js function */ helperCallback, | |
| 3554 AsyncStarStreamController controller, | |
| 3555 dynamic /* js function */ errorCallback) { | |
| 3556 if (helperCallback == null) { | |
| 3557 // This happens on return from the async* function. | |
| 3558 controller.close(); | |
| 3559 return null; | |
| 3560 } | |
| 3561 | |
| 3562 if (object is IterationMarker) { | |
| 3563 if (controller.stopRunning) { | |
| 3564 _wrapJsFunctionForStream(errorCallback, controller)(); | |
| 3565 return null; | |
| 3566 } | |
| 3567 if (object.state == IterationMarker.YIELD_SINGLE) { | |
| 3568 controller.add(object.value); | |
| 3569 if (controller.isPaused) { | |
| 3570 return null; | |
| 3571 } | |
| 3572 new Future.value(null).then( | |
|
floitsch
2015/02/05 20:17:23
Why?
Do we have to wait a microtask before we can
sigurdm
2015/02/06 14:26:35
I thought that it was necessary to suspend executi
| |
| 3573 _wrapJsFunctionForStream(helperCallback, controller)); | |
| 3574 return; | |
| 3575 } else if (object.state == IterationMarker.YIELD_STAR) { | |
| 3576 Stream stream = object.value; | |
| 3577 controller.isAdding = true; | |
| 3578 controller.addStream(stream).then((_) { | |
|
floitsch
2015/02/05 20:17:23
You might need to listen to the error too. I'm not
sigurdm
2015/02/06 14:26:34
As you saw below, I explicitly ask errors to be pa
| |
| 3579 controller.isAdding = false; | |
| 3580 _wrapJsFunctionForStream(helperCallback, controller)(null); | |
| 3581 }); | |
| 3582 return null; | |
| 3583 } | |
| 3584 } | |
| 3585 | |
| 3586 Future future = object is Future ? object : new Future.value(object); | |
| 3587 future.then(_wrapJsFunctionForStream(helperCallback, controller), | |
| 3588 onError: errorCallback == null | |
| 3589 ? null | |
| 3590 : _wrapJsFunctionForStream(errorCallback, controller)); | |
| 3591 return controller.stream; | |
| 3592 } | |
| 3593 | |
| 3594 /// A wrapper around a [StreamController] that remembers if that controller | |
| 3595 /// got a cancel. | |
| 3596 /// | |
| 3597 /// Also has a subSubscription that when not null will provide events for the | |
| 3598 /// stream, and will be paused and resumed along with this controller. | |
| 3599 class AsyncStarStreamController { | |
| 3600 StreamController controller; | |
| 3601 Stream get stream => controller.stream; | |
| 3602 bool stopRunning = false; | |
| 3603 bool isAdding = false; | |
| 3604 bool get isPaused => controller.isPaused; | |
| 3605 add(event) => controller.add(event); | |
| 3606 addStream(Stream stream) { | |
| 3607 return controller.addStream(stream, cancelOnError: false); | |
|
floitsch
2015/02/05 20:17:23
I see. So I guess the future cannot have an error.
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3608 } | |
| 3609 addError(error, stackTrace) => controller.addError(error, stackTrace); | |
| 3610 close() => controller.close(); | |
| 3611 | |
| 3612 AsyncStarStreamController(helperCallback) { | |
| 3613 controller = new StreamController( | |
| 3614 onResume: () { | |
| 3615 if (!isAdding) { | |
|
floitsch
2015/02/05 20:17:23
Check with Lasse, if 'isPaused' on the controller
floitsch
2015/02/05 20:17:23
Add comment.
sigurdm
2015/02/06 14:26:34
Done.
sigurdm
2015/02/06 14:26:34
According to him, it is just as good.
| |
| 3616 streamHelper(null, helperCallback, this, null); | |
| 3617 } | |
| 3618 }, onCancel: () { | |
| 3619 stopRunning = true; | |
|
floitsch
2015/02/05 20:17:23
Please make sure (test), that an incoming stream i
sigurdm
2015/02/06 14:26:34
Modified asyncstar_yieldstar_test to do that.
| |
| 3620 }); | |
| 3621 } | |
| 3622 } | |
| 3623 | |
| 3624 makeAsyncStarController(helperCallback) { | |
| 3625 return new AsyncStarStreamController(helperCallback); | |
| 3626 } | |
| 3627 | |
| 3628 Function _wrapJsFunctionForStream(dynamic /* js function */ function, | |
| 3629 AsyncStarStreamController controller) { | |
| 3630 return (result) { | |
| 3631 try { | |
| 3632 JS('', '#(#)', function, result); | |
| 3633 } catch (e, st) { | |
| 3634 controller.addError(e, st); | |
| 3635 } | |
| 3636 }; | |
| 3637 } | |
| 3638 | |
| 3639 | |
| 3640 class IterationMarker { | |
| 3641 static const YIELD_SINGLE = 0; | |
| 3642 static const YIELD_STAR = 1; | |
| 3643 static const ITERATION_ENDED = 2; | |
| 3644 | |
| 3645 final value; | |
| 3646 final int state; | |
| 3647 | |
| 3648 IterationMarker._(this.state, this.value); | |
| 3649 | |
| 3650 static yieldStar(dynamic /* Iterable or Stream */ yielded) { | |
|
floitsch
2015/02/05 20:17:23
maybe just "values" ?
sigurdm
2015/02/06 14:26:34
Done.
| |
| 3651 return new IterationMarker._(YIELD_STAR, yielded); | |
| 3652 } | |
| 3653 | |
| 3654 static endOfIteration() { | |
| 3655 return new IterationMarker._(ITERATION_ENDED, null); | |
| 3656 } | |
| 3657 | |
| 3658 static yieldSingle(dynamic value) { | |
| 3659 return new IterationMarker._(YIELD_SINGLE, value); | |
| 3660 } | |
| 3661 | |
| 3662 toString() => "IterationMarker($state, $value)"; | |
| 3663 } | |
| 3664 | |
| 3665 class SyncStarIterator implements Iterator { | |
| 3666 final Function _helper; | |
| 3667 | |
| 3668 // If [runningNested] this is the nested iterator, otherwise it is the | |
| 3669 // current value. | |
| 3670 dynamic _current = null; | |
| 3671 bool _runningNested = false; | |
| 3672 | |
| 3673 get current => _runningNested ? _current.current : _current; | |
| 3674 | |
| 3675 SyncStarIterator(helper) | |
| 3676 : _helper = ((arg) => JS('', '#(#)', helper, arg)); | |
| 3677 | |
| 3678 bool moveNext() { | |
| 3679 if (_runningNested) { | |
| 3680 if (_current.moveNext()) { | |
| 3681 return true; | |
| 3682 } else { | |
| 3683 _runningNested = false; | |
| 3684 } | |
| 3685 } | |
| 3686 _current = _helper(null); | |
| 3687 if (_current is IterationMarker) { | |
| 3688 if (_current.state == IterationMarker.ITERATION_ENDED) { | |
| 3689 _current = null; | |
| 3690 // Rely on [_helper] to repeatedly return `ITERATION_ENDED`. | |
| 3691 return false; | |
| 3692 } else { | |
| 3693 assert(_current.state == IterationMarker.YIELD_STAR); | |
| 3694 _current = _current.value.iterator; | |
| 3695 _runningNested = true; | |
| 3696 return moveNext(); | |
| 3697 } | |
| 3698 } | |
| 3699 return true; | |
| 3700 } | |
| 3701 } | |
| 3702 | |
| 3703 /// An Iterable corresponding to a sync* method. | |
| 3704 /// | |
| 3705 /// Each invocation of a sync* method will return a new instance of this class. | |
| 3706 class SyncStarIterable extends IterableBase { | |
| 3707 // This is a function that will return a helper function that does the | |
| 3708 // iteration of the sync*. | |
| 3709 // | |
| 3710 // Each invocation should give a helper with fresh state. | |
| 3711 final dynamic /* js function */ _outerHelper; | |
| 3712 | |
| 3713 SyncStarIterable(this._outerHelper); | |
| 3714 | |
| 3715 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | |
| 3716 } | |
| 3717 | |
| OLD | NEW |