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 */ bodyFunctionOrErrorCode, |
3492 Completer completer, | 3493 Completer completer) { |
3493 dynamic /* js function */ errorCallback) { | 3494 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
3494 if (helperCallback == null) { | |
3495 completer.complete(object); | 3495 completer.complete(object); |
3496 return; | 3496 return; |
| 3497 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
| 3498 // The error is a js-error. |
| 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(bodyFunctionOrErrorCode, |
3500 onError: (errorCallback == null) | 3505 async_error_codes.SUCCESS), |
3501 ? null | 3506 onError: _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
3502 : _wrapJsFunctionForThenHelper(errorCallback, completer)); | 3507 async_error_codes.ERROR)); |
3503 return completer.future; | 3508 return completer.future; |
3504 } | 3509 } |
3505 | 3510 |
3506 Function _wrapJsFunctionForThenHelper(dynamic /* js function */ function, | 3511 Function _wrapJsFunctionForAsync(dynamic /* js function */ function, |
3507 Completer completer) { | 3512 int errorCode) { |
3508 return (result) { | 3513 return (result) { |
3509 try { | 3514 JS('', '#(#, #)', function, errorCode, result); |
3510 JS('', '#(#)', function, result); | |
3511 } catch (e, st) { | |
3512 completer.completeError(e, st); | |
3513 } | |
3514 }; | 3515 }; |
3515 } | 3516 } |
3516 | 3517 |
3517 | |
3518 /// Implements the runtime support for async* functions. | 3518 /// Implements the runtime support for async* functions. |
3519 /// | 3519 /// |
3520 /// Called by the transformed function for each original return, await, yield, | 3520 /// Called by the transformed function for each original return, await, yield, |
3521 /// yield* and before starting the function. | 3521 /// yield* and before starting the function. |
3522 /// | 3522 /// |
3523 /// When the async* function wants to return it calls this function. with | 3523 /// When the async* function wants to return it calls this function with |
3524 /// [helperCallback] == null, the streamHelper takes this as signal to close the | 3524 /// [asyncBody] == [async_error_codes.SUCCESS], the asyncStarHelper takes this |
3525 /// stream. | 3525 /// as signal to close the stream. |
3526 /// | 3526 /// |
3527 /// If the async* function wants to do a yield or yield* it calls this function | 3527 /// 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 | 3528 /// it calls this function with [asyncBody] == [async_error_codes.ERROR], |
3529 /// special meaning; it is a callback that will run all enclosing finalizers. | 3529 /// the streamHelper takes this as signal to addError [object] to the |
| 3530 /// [controller] and close it. |
| 3531 /// |
| 3532 /// If the async* function wants to do a yield or yield*, it calls this function |
| 3533 /// with [object] being an [IterationMarker]. |
3530 /// | 3534 /// |
3531 /// In the case of a yield or yield*, if the stream subscription has been | 3535 /// In the case of a yield or yield*, if the stream subscription has been |
3532 /// canceled [errorCallback] is scheduled. | 3536 /// canceled, schedules [asyncBody] to be called with |
| 3537 /// [async_error_codes.STREAM_WAS_CANCELED]. |
3533 /// | 3538 /// |
3534 /// If [object] is a single-yield [IterationMarker], adds the value of the | 3539 /// If [object] is a single-yield [IterationMarker], adds the value of the |
3535 /// [IterationMarker] to the stream. If the stream subscription has been | 3540 /// [IterationMarker] to the stream. If the stream subscription has been |
3536 /// paused, return early. Otherwise schedule the helper function to be | 3541 /// paused, return early. Otherwise schedule the helper function to be |
3537 /// executed again. | 3542 /// executed again. |
3538 /// | 3543 /// |
3539 /// If [object] is a yield-star [IterationMarker], starts listening to the | 3544 /// 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 | 3545 /// 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 | 3546 /// care if the subscription has been paused or canceled) - when the sub-stream |
3542 /// is done, schedules [helperCallback] again. | 3547 /// is done, schedules [asyncBody] again. |
3543 /// | 3548 /// |
3544 /// If the async* function wants to do an await it calls this function with | 3549 /// If the async* function wants to do an await it calls this function with |
3545 /// [object] not and [IterationMarker]. | 3550 /// [object] not and [IterationMarker]. |
3546 /// | 3551 /// |
3547 /// If [object] is not a [Future], it is wrapped in a `Future.value`. | 3552 /// If [object] is not a [Future], it is wrapped in a `Future.value`. |
3548 /// The [helperCallback] is called on successfull completion of the | 3553 /// The [asyncBody] is called on completion of the future (see [asyncHelper]. |
3549 /// future. | 3554 void asyncStarHelper(dynamic object, |
3550 /// | 3555 dynamic /* int | js function */ bodyFunctionOrErrorCode, |
3551 /// If [helperCallback] or [errorCallback] throws the error is added to the | 3556 AsyncStarStreamController controller) { |
3552 /// stream. | 3557 if (identical(bodyFunctionOrErrorCode, async_error_codes.SUCCESS)) { |
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. | 3558 // This happens on return from the async* function. |
3559 controller.close(); | 3559 controller.close(); |
3560 return; | 3560 return; |
| 3561 } else if (identical(bodyFunctionOrErrorCode, async_error_codes.ERROR)) { |
| 3562 // The error is a js-error. |
| 3563 controller.addError(unwrapException(object), |
| 3564 getTraceFromException(object)); |
| 3565 controller.close(); |
| 3566 return; |
3561 } | 3567 } |
3562 | 3568 |
3563 if (object is IterationMarker) { | 3569 if (object is IterationMarker) { |
3564 if (controller.stopRunning) { | 3570 if (controller.stopRunning) { |
3565 _wrapJsFunctionForStream(errorCallback, controller)(); | 3571 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3572 async_error_codes.STREAM_WAS_CANCELED)(null); |
3566 return; | 3573 return; |
3567 } | 3574 } |
3568 if (object.state == IterationMarker.YIELD_SINGLE) { | 3575 if (object.state == IterationMarker.YIELD_SINGLE) { |
3569 controller.add(object.value); | 3576 controller.add(object.value); |
3570 // If the controller is paused we stop producing more values. | 3577 // If the controller is paused we stop producing more values. |
3571 if (controller.isPaused) { | 3578 if (controller.isPaused) { |
3572 return; | 3579 return; |
3573 } | 3580 } |
3574 // TODO(sigurdm): We should not suspend here according to the spec. | 3581 // TODO(sigurdm): We should not suspend here according to the spec. |
3575 scheduleMicrotask(() { | 3582 scheduleMicrotask(() { |
3576 _wrapJsFunctionForStream(helperCallback, controller)(null); | 3583 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3584 async_error_codes.SUCCESS) |
| 3585 (null); |
3577 }); | 3586 }); |
3578 return; | 3587 return; |
3579 } else if (object.state == IterationMarker.YIELD_STAR) { | 3588 } else if (object.state == IterationMarker.YIELD_STAR) { |
3580 Stream stream = object.value; | 3589 Stream stream = object.value; |
3581 controller.isAdding = true; | 3590 controller.isAdding = true; |
3582 // Errors of [stream] are passed though to the main stream. (see | 3591 // Errors of [stream] are passed though to the main stream. (see |
3583 // [AsyncStreamController.addStream]. | 3592 // [AsyncStreamController.addStream]. |
3584 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. | 3593 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |
3585 controller.addStream(stream).then((_) { | 3594 controller.addStream(stream).then((_) { |
3586 controller.isAdding = false; | 3595 controller.isAdding = false; |
3587 _wrapJsFunctionForStream(helperCallback, controller)(null); | 3596 _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
| 3597 async_error_codes.SUCCESS)(null); |
3588 }); | 3598 }); |
3589 return; | 3599 return; |
3590 } | 3600 } |
3591 } | 3601 } |
3592 | 3602 |
3593 Future future = object is Future ? object : new Future.value(object); | 3603 Future future = object is Future ? object : new Future.value(object); |
3594 future.then(_wrapJsFunctionForStream(helperCallback, controller), | 3604 future.then(_wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
3595 onError: errorCallback == null | 3605 async_error_codes.SUCCESS), |
3596 ? null | 3606 onError: _wrapJsFunctionForAsync(bodyFunctionOrErrorCode, |
3597 : _wrapJsFunctionForStream(errorCallback, controller)); | 3607 async_error_codes.ERROR)); |
3598 } | 3608 } |
3599 | 3609 |
3600 Stream streamOfController(AsyncStarStreamController controller) { | 3610 Stream streamOfController(AsyncStarStreamController controller) { |
3601 return controller.stream; | 3611 return controller.stream; |
3602 } | 3612 } |
3603 | 3613 |
3604 /// A wrapper around a [StreamController] that remembers if that controller | 3614 /// A wrapper around a [StreamController] that remembers if that controller |
3605 /// got a cancel. | 3615 /// got a cancel. |
3606 /// | 3616 /// |
3607 /// Also has a subSubscription that when not null will provide events for the | 3617 /// 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. | 3618 /// stream, and will be paused and resumed along with this controller. |
3609 class AsyncStarStreamController { | 3619 class AsyncStarStreamController { |
3610 StreamController controller; | 3620 StreamController controller; |
3611 Stream get stream => controller.stream; | 3621 Stream get stream => controller.stream; |
3612 bool stopRunning = false; | 3622 bool stopRunning = false; |
3613 bool isAdding = false; | 3623 bool isAdding = false; |
3614 bool get isPaused => controller.isPaused; | 3624 bool get isPaused => controller.isPaused; |
3615 add(event) => controller.add(event); | 3625 add(event) => controller.add(event); |
3616 addStream(Stream stream) { | 3626 addStream(Stream stream) { |
3617 return controller.addStream(stream, cancelOnError: false); | 3627 return controller.addStream(stream, cancelOnError: false); |
3618 } | 3628 } |
3619 addError(error, stackTrace) => controller.addError(error, stackTrace); | 3629 addError(error, stackTrace) => controller.addError(error, stackTrace); |
3620 close() => controller.close(); | 3630 close() => controller.close(); |
3621 | 3631 |
3622 AsyncStarStreamController(helperCallback) { | 3632 AsyncStarStreamController(helperCallback) { |
3623 controller = new StreamController( | 3633 controller = new StreamController( |
3624 onListen: () { | 3634 onListen: () { |
3625 scheduleMicrotask(() => JS('', '#(null)', helperCallback)); | 3635 scheduleMicrotask(() { |
| 3636 JS('', '#(#, null)', helperCallback, async_error_codes.SUCCESS); |
| 3637 }); |
3626 }, | 3638 }, |
3627 onResume: () { | 3639 onResume: () { |
3628 if (!isAdding) { | 3640 if (!isAdding) { |
3629 streamHelper(null, helperCallback, this, null); | 3641 asyncStarHelper(null, helperCallback, this); |
3630 } | 3642 } |
3631 }, onCancel: () { | 3643 }, onCancel: () { |
3632 stopRunning = true; | 3644 stopRunning = true; |
3633 }); | 3645 }); |
3634 } | 3646 } |
3635 } | 3647 } |
3636 | 3648 |
3637 makeAsyncStarController(helperCallback) { | 3649 makeAsyncStarController(helperCallback) { |
3638 return new AsyncStarStreamController(helperCallback); | 3650 return new AsyncStarStreamController(helperCallback); |
3639 } | 3651 } |
3640 | 3652 |
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 { | 3653 class IterationMarker { |
3654 static const YIELD_SINGLE = 0; | 3654 static const YIELD_SINGLE = 0; |
3655 static const YIELD_STAR = 1; | 3655 static const YIELD_STAR = 1; |
3656 static const ITERATION_ENDED = 2; | 3656 static const ITERATION_ENDED = 2; |
| 3657 static const UNCAUGHT_ERROR = 3; |
3657 | 3658 |
3658 final value; | 3659 final value; |
3659 final int state; | 3660 final int state; |
3660 | 3661 |
3661 IterationMarker._(this.state, this.value); | 3662 IterationMarker._(this.state, this.value); |
3662 | 3663 |
3663 static yieldStar(dynamic /* Iterable or Stream */ values) { | 3664 static yieldStar(dynamic /* Iterable or Stream */ values) { |
3664 return new IterationMarker._(YIELD_STAR, values); | 3665 return new IterationMarker._(YIELD_STAR, values); |
3665 } | 3666 } |
3666 | 3667 |
3667 static endOfIteration() { | 3668 static endOfIteration() { |
3668 return new IterationMarker._(ITERATION_ENDED, null); | 3669 return new IterationMarker._(ITERATION_ENDED, null); |
3669 } | 3670 } |
3670 | 3671 |
3671 static yieldSingle(dynamic value) { | 3672 static yieldSingle(dynamic value) { |
3672 return new IterationMarker._(YIELD_SINGLE, value); | 3673 return new IterationMarker._(YIELD_SINGLE, value); |
3673 } | 3674 } |
3674 | 3675 |
| 3676 static uncaughtError(dynamic error) { |
| 3677 return new IterationMarker._(UNCAUGHT_ERROR, error); |
| 3678 } |
| 3679 |
3675 toString() => "IterationMarker($state, $value)"; | 3680 toString() => "IterationMarker($state, $value)"; |
3676 } | 3681 } |
3677 | 3682 |
3678 class SyncStarIterator implements Iterator { | 3683 class SyncStarIterator implements Iterator { |
3679 final Function _helper; | 3684 final Function _body; |
3680 | 3685 |
3681 // If [runningNested] this is the nested iterator, otherwise it is the | 3686 // If [runningNested] this is the nested iterator, otherwise it is the |
3682 // current value. | 3687 // current value. |
3683 dynamic _current = null; | 3688 dynamic _current = null; |
3684 bool _runningNested = false; | 3689 bool _runningNested = false; |
3685 | 3690 |
3686 get current => _runningNested ? _current.current : _current; | 3691 get current => _runningNested ? _current.current : _current; |
3687 | 3692 |
3688 SyncStarIterator(helper) | 3693 SyncStarIterator(body) |
3689 : _helper = ((arg) => JS('', '#(#)', helper, arg)); | 3694 : _body = (() => JS('', '#()', body)); |
3690 | 3695 |
3691 bool moveNext() { | 3696 bool moveNext() { |
3692 if (_runningNested) { | 3697 if (_runningNested) { |
3693 if (_current.moveNext()) { | 3698 if (_current.moveNext()) { |
3694 return true; | 3699 return true; |
3695 } else { | 3700 } else { |
3696 _runningNested = false; | 3701 _runningNested = false; |
3697 } | 3702 } |
3698 } | 3703 } |
3699 _current = _helper(null); | 3704 _current = _body(); |
3700 if (_current is IterationMarker) { | 3705 if (_current is IterationMarker) { |
3701 if (_current.state == IterationMarker.ITERATION_ENDED) { | 3706 if (_current.state == IterationMarker.ITERATION_ENDED) { |
3702 _current = null; | 3707 _current = null; |
3703 // Rely on [_helper] to repeatedly return `ITERATION_ENDED`. | 3708 // Rely on [_body] to repeatedly return `ITERATION_ENDED`. |
3704 return false; | 3709 return false; |
| 3710 } else if (_current.state == IterationMarker.UNCAUGHT_ERROR) { |
| 3711 // Rely on [_body] to repeatedly return `UNCAUGHT_ERROR`. |
| 3712 // This is a wrapped exception, so we use JavaScript throw to throw it. |
| 3713 JS('', 'throw #', _current.value); |
3705 } else { | 3714 } else { |
3706 assert(_current.state == IterationMarker.YIELD_STAR); | 3715 assert(_current.state == IterationMarker.YIELD_STAR); |
3707 _current = _current.value.iterator; | 3716 _current = _current.value.iterator; |
3708 _runningNested = true; | 3717 _runningNested = true; |
3709 return moveNext(); | 3718 return moveNext(); |
3710 } | 3719 } |
3711 } | 3720 } |
3712 return true; | 3721 return true; |
3713 } | 3722 } |
3714 } | 3723 } |
3715 | 3724 |
3716 /// An Iterable corresponding to a sync* method. | 3725 /// An Iterable corresponding to a sync* method. |
3717 /// | 3726 /// |
3718 /// Each invocation of a sync* method will return a new instance of this class. | 3727 /// Each invocation of a sync* method will return a new instance of this class. |
3719 class SyncStarIterable extends IterableBase { | 3728 class SyncStarIterable extends IterableBase { |
3720 // This is a function that will return a helper function that does the | 3729 // This is a function that will return a helper function that does the |
3721 // iteration of the sync*. | 3730 // iteration of the sync*. |
3722 // | 3731 // |
3723 // Each invocation should give a helper with fresh state. | 3732 // Each invocation should give a body with fresh state. |
3724 final dynamic /* js function */ _outerHelper; | 3733 final dynamic /* js function */ _outerHelper; |
3725 | 3734 |
3726 SyncStarIterable(this._outerHelper); | 3735 SyncStarIterable(this._outerHelper); |
3727 | 3736 |
3728 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); | 3737 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
3729 } | 3738 } |
OLD | NEW |