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:_async_await_error_codes' as async_error_codes; | |
8 | |
7 import 'dart:_js_embedded_names' show | 9 import 'dart:_js_embedded_names' show |
8 GET_TYPE_FROM_NAME, | 10 GET_TYPE_FROM_NAME, |
9 GET_ISOLATE_TAG, | 11 GET_ISOLATE_TAG, |
10 INTERCEPTED_NAMES, | 12 INTERCEPTED_NAMES, |
11 INTERCEPTORS_BY_TAG, | 13 INTERCEPTORS_BY_TAG, |
12 LEAF_TAGS, | 14 LEAF_TAGS, |
13 METADATA, | 15 METADATA, |
14 DEFERRED_LIBRARY_URIS, | 16 DEFERRED_LIBRARY_URIS, |
15 DEFERRED_LIBRARY_HASHES, | 17 DEFERRED_LIBRARY_HASHES, |
16 INITIALIZE_LOADED_HUNK, | 18 INITIALIZE_LOADED_HUNK, |
17 IS_HUNK_LOADED, | 19 IS_HUNK_LOADED, |
18 IS_HUNK_INITIALIZED, | 20 IS_HUNK_INITIALIZED, |
19 NATIVE_SUPERCLASS_TAG_NAME; | 21 NATIVE_SUPERCLASS_TAG_NAME; |
20 | 22 |
21 import 'dart:collection'; | 23 import 'dart:collection'; |
22 import 'dart:_isolate_helper' show | 24 import 'dart:_isolate_helper' show |
23 IsolateNatives, | 25 IsolateNatives, |
24 enterJsAsync, | 26 enterJsAsync, |
25 isWorker, | 27 isWorker, |
26 leaveJsAsync; | 28 leaveJsAsync; |
27 | 29 |
28 import 'dart:async' show | 30 import 'dart:async' show |
29 Future, | 31 Future, |
30 DeferredLoadException, | 32 DeferredLoadException, |
31 Completer, | 33 Completer, |
32 StreamController, | 34 StreamController, |
33 Stream, | 35 Stream, |
34 StreamSubscription, | 36 StreamSubscription, |
35 scheduleMicrotask; | 37 scheduleMicrotask; |
36 | 38 |
37 import 'dart:_foreign_helper' show | 39 import 'dart:_foreign_helper' show |
38 DART_CLOSURE_TO_JS, | 40 DART_CLOSURE_TO_JS, |
39 JS, | 41 JS, |
40 JS_CALL_IN_ISOLATE, | 42 JS_CALL_IN_ISOLATE, |
41 JS_CONST, | 43 JS_CONST, |
42 JS_CURRENT_ISOLATE, | 44 JS_CURRENT_ISOLATE, |
43 JS_CURRENT_ISOLATE_CONTEXT, | 45 JS_CURRENT_ISOLATE_CONTEXT, |
44 JS_DART_OBJECT_CONSTRUCTOR, | 46 JS_DART_OBJECT_CONSTRUCTOR, |
45 JS_EFFECT, | 47 JS_EFFECT, |
(...skipping 3424 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3470 throw new MainError("'main' expects too many parameters."); | 3472 throw new MainError("'main' expects too many parameters."); |
3471 } | 3473 } |
3472 | 3474 |
3473 /// Runtime support for async-await transformation. | 3475 /// Runtime support for async-await transformation. |
3474 /// | 3476 /// |
3475 /// This function is called by a transformed function on each await and return | 3477 /// This function is called by a transformed function on each await and return |
3476 /// in the untransformed function, and before starting. | 3478 /// in the untransformed function, and before starting. |
3477 /// | 3479 /// |
3478 /// If [object] is not a future it will be wrapped in a `new Future.value`. | 3480 /// If [object] is not a future it will be wrapped in a `new Future.value`. |
3479 /// | 3481 /// |
3480 /// If [helperCallback] is null it indicates a return from the async function, | 3482 /// If [asyncBody] is [async_error_codes.SUCCESS]/[async_error_codes.ERROR] it |
3481 /// and we complete the completer with object. | 3483 /// indicates a return or throw from the async function, and |
3484 /// complete/completeError is called on [completer] with [object]. | |
3482 /// | 3485 /// |
3483 /// Otherwise [helperCallback] is set up to be called when the future is | 3486 /// Otherwise [asyncBody] is set up to be called when the future is completed |
3484 /// successfull and [errorCallback] if it is completed with an error. | 3487 /// with a code [async_error_codes.SUCCESS]/[async_error_codes.ERROR] depending |
3485 /// | 3488 /// on the success of the future. |
3486 /// If helperCallback or errorCallback throws we complete the completer with the | |
3487 /// error. | |
3488 /// | 3489 /// |
3489 /// Returns the future of the completer for convenience of the first call. | 3490 /// Returns the future of the completer for convenience of the first call. |
3490 dynamic thenHelper(dynamic object, | 3491 dynamic asyncHelper(dynamic object, |
3491 dynamic /* js function */ helperCallback, | 3492 dynamic /* js function */ asyncBody, |
3492 Completer completer, | 3493 Completer completer) { |
3493 dynamic /* js function */ errorCallback) { | 3494 if (asyncBody == async_error_codes.SUCCESS) { |
3494 if (helperCallback == null) { | |
3495 completer.complete(object); | 3495 completer.complete(object); |
3496 return; | 3496 return; |
3497 } else if (asyncBody == async_error_codes.ERROR) { | |
3498 // The error will be a js-error. | |
floitsch
2015/02/13 15:30:07
is a
sigurdm
2015/02/17 08:43:10
Done.
| |
3499 completer.completeError(unwrapException(object), | |
3500 getTraceFromException(object)); | |
3501 return; | |
3497 } | 3502 } |
3498 Future future = object is Future ? object : new Future.value(object); | 3503 Future future = object is Future ? object : new Future.value(object); |
3499 future.then(_wrapJsFunctionForThenHelper(helperCallback, completer), | 3504 future.then(_wrapJsFunctionForAsync(asyncBody, async_error_codes.SUCCESS), |
3500 onError: (errorCallback == null) | 3505 onError: _wrapJsFunctionForAsync(asyncBody, async_error_codes.ERROR)); |
3501 ? null | |
3502 : _wrapJsFunctionForThenHelper(errorCallback, completer)); | |
3503 return completer.future; | 3506 return completer.future; |
3504 } | 3507 } |
3505 | 3508 |
3506 Function _wrapJsFunctionForThenHelper(dynamic /* js function */ function, | 3509 Function _wrapJsFunctionForAsync(dynamic /* js function */ function, |
3507 Completer completer) { | 3510 int errorCode) { |
3508 return (result) { | 3511 return (result) { |
3509 try { | 3512 JS('', '#(#, #)', function, errorCode, result); |
3510 JS('', '#(#)', function, result); | |
3511 } catch (e, st) { | |
3512 completer.completeError(e, st); | |
3513 } | |
3514 }; | 3513 }; |
3515 } | 3514 } |
3516 | 3515 |
3517 | |
3518 /// Implements the runtime support for async* functions. | 3516 /// Implements the runtime support for async* functions. |
3519 /// | 3517 /// |
3520 /// Called by the transformed function for each original return, await, yield, | 3518 /// Called by the transformed function for each original return, await, yield, |
3521 /// yield* and before starting the function. | 3519 /// yield* and before starting the function. |
3522 /// | 3520 /// |
3523 /// When the async* function wants to return it calls this function. with | 3521 /// When the async* function wants to return it calls this function with |
3524 /// [helperCallback] == null, the streamHelper takes this as signal to close the | 3522 /// [asyncBody] == [async_error_codes.SUCCESS], the asyncStarHelper takes this |
3525 /// stream. | 3523 /// as signal to close the stream. |
3526 /// | 3524 /// |
3527 /// If the async* function wants to do a yield or yield* it calls this function | 3525 /// When the async* function wants to signal that an uncaught error was thrown, |
3528 /// with [object] being an [IterationMarker]. In this case [errorCallback] has a | 3526 /// it calls this function with [asyncBody] == [async_error_codes.ERROR], |
3529 /// special meaning; it is a callback that will run all enclosing finalizers. | 3527 /// the streamHelper takes this as signal to addError [object] to the |
3528 /// [controller] and close it. | |
3529 /// | |
3530 /// If the async* function wants to do a yield or yield*, it calls this function | |
3531 /// with [object] being an [IterationMarker]. | |
3530 /// | 3532 /// |
3531 /// In the case of a yield or yield*, if the stream subscription has been | 3533 /// In the case of a yield or yield*, if the stream subscription has been |
3532 /// canceled [errorCallback] is scheduled. | 3534 /// canceled, schedules [asyncBody] to be called with |
3535 /// [async_error_codes.STREAM_WAS_CANCELED]. | |
3533 /// | 3536 /// |
3534 /// If [object] is a single-yield [IterationMarker], adds the value of the | 3537 /// If [object] is a single-yield [IterationMarker], adds the value of the |
3535 /// [IterationMarker] to the stream. If the stream subscription has been | 3538 /// [IterationMarker] to the stream. If the stream subscription has been |
3536 /// paused, return early. Otherwise schedule the helper function to be | 3539 /// paused, return early. Otherwise schedule the helper function to be |
3537 /// executed again. | 3540 /// executed again. |
3538 /// | 3541 /// |
3539 /// If [object] is a yield-star [IterationMarker], starts listening to the | 3542 /// If [object] is a yield-star [IterationMarker], starts listening to the |
3540 /// yielded stream, and adds all events and errors to our own controller (taking | 3543 /// yielded stream, and adds all events and errors to our own controller (taking |
3541 /// care if the subscription has been paused or canceled) - when the sub-stream | 3544 /// care if the subscription has been paused or canceled) - when the sub-stream |
3542 /// is done, schedules [helperCallback] again. | 3545 /// is done, schedules [asyncBody] again. |
3543 /// | 3546 /// |
3544 /// If the async* function wants to do an await it calls this function with | 3547 /// If the async* function wants to do an await it calls this function with |
3545 /// [object] not and [IterationMarker]. | 3548 /// [object] not and [IterationMarker]. |
3546 /// | 3549 /// |
3547 /// If [object] is not a [Future], it is wrapped in a `Future.value`. | 3550 /// If [object] is not a [Future], it is wrapped in a `Future.value`. |
3548 /// The [helperCallback] is called on successfull completion of the | 3551 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. |
3549 /// future. | 3552 void asyncStarHelper(dynamic object, |
3550 /// | 3553 dynamic /* int | js function */ asyncBody, |
floitsch
2015/02/13 15:30:07
bodyFunctionOrErrorCode ?
It's long, but not used
sigurdm
2015/02/17 08:43:10
Good point. I will use identical.
| |
3551 /// If [helperCallback] or [errorCallback] throws the error is added to the | 3554 AsyncStarStreamController controller) { |
3552 /// stream. | 3555 if (asyncBody == async_error_codes.SUCCESS) { |
floitsch
2015/02/13 15:30:07
nit.
use identical, or invert the order.
The async
sigurdm
2015/02/17 08:43:10
Done.
| |
3553 void streamHelper(dynamic object, | |
3554 dynamic /* js function */ helperCallback, | |
3555 AsyncStarStreamController controller, | |
3556 dynamic /* js function */ errorCallback) { | |
3557 if (helperCallback == null) { | |
3558 // This happens on return from the async* function. | 3556 // This happens on return from the async* function. |
3559 controller.close(); | 3557 controller.close(); |
3560 return; | 3558 return; |
3559 } else if (asyncBody == async_error_codes.ERROR) { | |
floitsch
2015/02/13 15:30:07
ditto.
sigurdm
2015/02/17 08:43:10
Done.
| |
3560 // The error will be a js-error. | |
floitsch
2015/02/13 15:30:07
is
sigurdm
2015/02/17 08:43:10
Done.
| |
3561 controller.addError(unwrapException(object), | |
3562 getTraceFromException(object)); | |
3563 controller.close(); | |
3564 return; | |
3561 } | 3565 } |
3562 | 3566 |
3563 if (object is IterationMarker) { | 3567 if (object is IterationMarker) { |
3564 if (controller.stopRunning) { | 3568 if (controller.stopRunning) { |
3565 _wrapJsFunctionForStream(errorCallback, controller)(); | 3569 _wrapJsFunctionForAsync(asyncBody, |
3570 async_error_codes.STREAM_WAS_CANCELED)(null); | |
3566 return; | 3571 return; |
3567 } | 3572 } |
3568 if (object.state == IterationMarker.YIELD_SINGLE) { | 3573 if (object.state == IterationMarker.YIELD_SINGLE) { |
3569 controller.add(object.value); | 3574 controller.add(object.value); |
3570 // If the controller is paused we stop producing more values. | 3575 // If the controller is paused we stop producing more values. |
3571 if (controller.isPaused) { | 3576 if (controller.isPaused) { |
3572 return; | 3577 return; |
3573 } | 3578 } |
3574 // TODO(sigurdm): We should not suspend here according to the spec. | 3579 // TODO(sigurdm): We should not suspend here according to the spec. |
3575 scheduleMicrotask(() { | 3580 scheduleMicrotask(() { |
3576 _wrapJsFunctionForStream(helperCallback, controller)(null); | 3581 _wrapJsFunctionForAsync(asyncBody, async_error_codes.SUCCESS) |
3582 (null); | |
3577 }); | 3583 }); |
3578 return; | 3584 return; |
3579 } else if (object.state == IterationMarker.YIELD_STAR) { | 3585 } else if (object.state == IterationMarker.YIELD_STAR) { |
3580 Stream stream = object.value; | 3586 Stream stream = object.value; |
3581 controller.isAdding = true; | 3587 controller.isAdding = true; |
3582 // Errors of [stream] are passed though to the main stream. (see | 3588 // Errors of [stream] are passed though to the main stream. (see |
3583 // [AsyncStreamController.addStream]. | 3589 // [AsyncStreamController.addStream]. |
3584 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. | 3590 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |
3585 controller.addStream(stream).then((_) { | 3591 controller.addStream(stream).then((_) { |
3586 controller.isAdding = false; | 3592 controller.isAdding = false; |
3587 _wrapJsFunctionForStream(helperCallback, controller)(null); | 3593 _wrapJsFunctionForAsync(asyncBody, async_error_codes.SUCCESS)(null); |
3588 }); | 3594 }); |
3589 return; | 3595 return; |
3590 } | 3596 } |
3591 } | 3597 } |
3592 | 3598 |
3593 Future future = object is Future ? object : new Future.value(object); | 3599 Future future = object is Future ? object : new Future.value(object); |
3594 future.then(_wrapJsFunctionForStream(helperCallback, controller), | 3600 future.then(_wrapJsFunctionForAsync(asyncBody, async_error_codes.SUCCESS), |
3595 onError: errorCallback == null | 3601 onError: _wrapJsFunctionForAsync(asyncBody, |
3596 ? null | 3602 async_error_codes.ERROR)); |
3597 : _wrapJsFunctionForStream(errorCallback, controller)); | |
3598 } | 3603 } |
3599 | 3604 |
3600 Stream streamOfController(AsyncStarStreamController controller) { | 3605 Stream streamOfController(AsyncStarStreamController controller) { |
3601 return controller.stream; | 3606 return controller.stream; |
3602 } | 3607 } |
3603 | 3608 |
3604 /// A wrapper around a [StreamController] that remembers if that controller | 3609 /// A wrapper around a [StreamController] that remembers if that controller |
3605 /// got a cancel. | 3610 /// got a cancel. |
3606 /// | 3611 /// |
3607 /// Also has a subSubscription that when not null will provide events for the | 3612 /// Also has a subSubscription that when not null will provide events for the |
3608 /// stream, and will be paused and resumed along with this controller. | 3613 /// stream, and will be paused and resumed along with this controller. |
3609 class AsyncStarStreamController { | 3614 class AsyncStarStreamController { |
3610 StreamController controller; | 3615 StreamController controller; |
3611 Stream get stream => controller.stream; | 3616 Stream get stream => controller.stream; |
3612 bool stopRunning = false; | 3617 bool stopRunning = false; |
3613 bool isAdding = false; | 3618 bool isAdding = false; |
3614 bool get isPaused => controller.isPaused; | 3619 bool get isPaused => controller.isPaused; |
3615 add(event) => controller.add(event); | 3620 add(event) => controller.add(event); |
3616 addStream(Stream stream) { | 3621 addStream(Stream stream) { |
3617 return controller.addStream(stream, cancelOnError: false); | 3622 return controller.addStream(stream, cancelOnError: false); |
3618 } | 3623 } |
3619 addError(error, stackTrace) => controller.addError(error, stackTrace); | 3624 addError(error, stackTrace) => controller.addError(error, stackTrace); |
3620 close() => controller.close(); | 3625 close() => controller.close(); |
3621 | 3626 |
3622 AsyncStarStreamController(helperCallback) { | 3627 AsyncStarStreamController(helperCallback) { |
3623 controller = new StreamController( | 3628 controller = new StreamController( |
3624 onListen: () { | 3629 onListen: () { |
3625 scheduleMicrotask(() => JS('', '#(null)', helperCallback)); | 3630 scheduleMicrotask(() { |
3631 JS('', '#(#, null)',helperCallback, async_error_codes.SUCCESS); | |
floitsch
2015/02/13 15:30:07
missing space.
sigurdm
2015/02/17 08:43:10
Done.
| |
3632 }); | |
3626 }, | 3633 }, |
3627 onResume: () { | 3634 onResume: () { |
3628 if (!isAdding) { | 3635 if (!isAdding) { |
3629 streamHelper(null, helperCallback, this, null); | 3636 asyncStarHelper(null, helperCallback, this); |
3630 } | 3637 } |
3631 }, onCancel: () { | 3638 }, onCancel: () { |
3632 stopRunning = true; | 3639 stopRunning = true; |
3633 }); | 3640 }); |
3634 } | 3641 } |
3635 } | 3642 } |
3636 | 3643 |
3637 makeAsyncStarController(helperCallback) { | 3644 makeAsyncStarController(helperCallback) { |
3638 return new AsyncStarStreamController(helperCallback); | 3645 return new AsyncStarStreamController(helperCallback); |
3639 } | 3646 } |
3640 | 3647 |
3641 Function _wrapJsFunctionForStream(dynamic /* js function */ function, | |
3642 AsyncStarStreamController controller) { | |
3643 return (result) { | |
3644 try { | |
3645 JS('', '#(#)', function, result); | |
3646 } catch (e, st) { | |
3647 controller.addError(e, st); | |
3648 } | |
3649 }; | |
3650 } | |
3651 | |
3652 | |
3653 class IterationMarker { | 3648 class IterationMarker { |
3654 static const YIELD_SINGLE = 0; | 3649 static const YIELD_SINGLE = 0; |
3655 static const YIELD_STAR = 1; | 3650 static const YIELD_STAR = 1; |
3656 static const ITERATION_ENDED = 2; | 3651 static const ITERATION_ENDED = 2; |
3657 | 3652 |
3658 final value; | 3653 final value; |
3659 final int state; | 3654 final int state; |
3660 | 3655 |
3661 IterationMarker._(this.state, this.value); | 3656 IterationMarker._(this.state, this.value); |
3662 | 3657 |
3663 static yieldStar(dynamic /* Iterable or Stream */ values) { | 3658 static yieldStar(dynamic /* Iterable or Stream */ values) { |
3664 return new IterationMarker._(YIELD_STAR, values); | 3659 return new IterationMarker._(YIELD_STAR, values); |
3665 } | 3660 } |
3666 | 3661 |
3667 static endOfIteration() { | 3662 static endOfIteration() { |
3668 return new IterationMarker._(ITERATION_ENDED, null); | 3663 return new IterationMarker._(ITERATION_ENDED, null); |
3669 } | 3664 } |
3670 | 3665 |
3671 static yieldSingle(dynamic value) { | 3666 static yieldSingle(dynamic value) { |
3672 return new IterationMarker._(YIELD_SINGLE, value); | 3667 return new IterationMarker._(YIELD_SINGLE, value); |
3673 } | 3668 } |
3674 | 3669 |
3675 toString() => "IterationMarker($state, $value)"; | 3670 toString() => "IterationMarker($state, $value)"; |
3676 } | 3671 } |
3677 | 3672 |
3678 class SyncStarIterator implements Iterator { | 3673 class SyncStarIterator implements Iterator { |
3679 final Function _helper; | 3674 final Function _body; |
3680 | 3675 |
3681 // If [runningNested] this is the nested iterator, otherwise it is the | 3676 // If [runningNested] this is the nested iterator, otherwise it is the |
3682 // current value. | 3677 // current value. |
3683 dynamic _current = null; | 3678 dynamic _current = null; |
3684 bool _runningNested = false; | 3679 bool _runningNested = false; |
3685 | 3680 |
3686 get current => _runningNested ? _current.current : _current; | 3681 get current => _runningNested ? _current.current : _current; |
3687 | 3682 |
3688 SyncStarIterator(helper) | 3683 SyncStarIterator(body) |
3689 : _helper = ((arg) => JS('', '#(#)', helper, arg)); | 3684 : _body = (() => JS('', '#()', body)); |
3690 | 3685 |
3691 bool moveNext() { | 3686 bool moveNext() { |
3692 if (_runningNested) { | 3687 if (_runningNested) { |
3693 if (_current.moveNext()) { | 3688 if (_current.moveNext()) { |
3694 return true; | 3689 return true; |
3695 } else { | 3690 } else { |
3696 _runningNested = false; | 3691 _runningNested = false; |
3697 } | 3692 } |
3698 } | 3693 } |
3699 _current = _helper(null); | 3694 _current = _body(); |
3700 if (_current is IterationMarker) { | 3695 if (_current is IterationMarker) { |
3701 if (_current.state == IterationMarker.ITERATION_ENDED) { | 3696 if (_current.state == IterationMarker.ITERATION_ENDED) { |
3702 _current = null; | 3697 _current = null; |
3703 // Rely on [_helper] to repeatedly return `ITERATION_ENDED`. | 3698 // Rely on [_helper] to repeatedly return `ITERATION_ENDED`. |
3704 return false; | 3699 return false; |
3705 } else { | 3700 } else { |
3706 assert(_current.state == IterationMarker.YIELD_STAR); | 3701 assert(_current.state == IterationMarker.YIELD_STAR); |
3707 _current = _current.value.iterator; | 3702 _current = _current.value.iterator; |
3708 _runningNested = true; | 3703 _runningNested = true; |
3709 return moveNext(); | 3704 return moveNext(); |
3710 } | 3705 } |
3711 } | 3706 } |
3712 return true; | 3707 return true; |
3713 } | 3708 } |
3714 } | 3709 } |
3715 | 3710 |
3716 /// An Iterable corresponding to a sync* method. | 3711 /// An Iterable corresponding to a sync* method. |
3717 /// | 3712 /// |
3718 /// Each invocation of a sync* method will return a new instance of this class. | 3713 /// Each invocation of a sync* method will return a new instance of this class. |
3719 class SyncStarIterable extends IterableBase { | 3714 class SyncStarIterable extends IterableBase { |
3720 // This is a function that will return a helper function that does the | 3715 // This is a function that will return a helper function that does the |
3721 // iteration of the sync*. | 3716 // iteration of the sync*. |
3722 // | 3717 // |
3723 // Each invocation should give a helper with fresh state. | 3718 // Each invocation should give a body with fresh state. |
3724 final dynamic /* js function */ _outerHelper; | 3719 final dynamic /* js function */ _outerHelper; |
3725 | 3720 |
3726 SyncStarIterable(this._outerHelper); | 3721 SyncStarIterable(this._outerHelper); |
3727 | 3722 |
3728 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | 3723 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
3729 } | 3724 } |
OLD | NEW |