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, | |
25 enterJsAsync, | 24 enterJsAsync, |
26 isWorker; | 25 isWorker, |
| 26 leaveJsAsync; |
27 | 27 |
28 import 'dart:async' show Future, DeferredLoadException, Completer; | 28 import 'dart:async' show |
| 29 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 calls this function. with |
| 3524 /// [helperCallback] == null, the streamHelper takes this as signal to close the |
| 3525 /// stream. |
| 3526 /// |
| 3527 /// If the async* function wants to do a yield or yield* it calls this function |
| 3528 /// with [object] being an [IterationMarker]. In this case [errorCallback] has a |
| 3529 /// special meaning; it is a callback that will run all enclosing finalizers. |
| 3530 /// |
| 3531 /// In the case of a yield or yield*, if the stream subscription has been |
| 3532 /// canceled [errorCallback] is scheduled. |
| 3533 /// |
| 3534 /// If [object] is a single-yield [IterationMarker], adds the value of the |
| 3535 /// [IterationMarker] to the stream. If the stream subscription has been |
| 3536 /// paused, return early. Otherwise schedule the helper function to be |
| 3537 /// executed again. |
| 3538 /// |
| 3539 /// 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 |
| 3541 /// care if the subscription has been paused or canceled) - when the sub-stream |
| 3542 /// is done, schedules [helperCallback] again. |
| 3543 /// |
| 3544 /// If the async* function wants to do an await it calls this function with |
| 3545 /// [object] not and [IterationMarker]. |
| 3546 /// |
| 3547 /// If [object] is not a [Future], it is wrapped in a `Future.value`. |
| 3548 /// The [helperCallback] is called on successfull completion of the |
| 3549 /// future. |
| 3550 /// |
| 3551 /// If [helperCallback] or [errorCallback] throws the error is added to the |
| 3552 /// stream. |
| 3553 dynamic 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. |
| 3559 controller.close(); |
| 3560 return null; |
| 3561 } |
| 3562 |
| 3563 if (object is IterationMarker) { |
| 3564 if (controller.stopRunning) { |
| 3565 _wrapJsFunctionForStream(errorCallback, controller)(); |
| 3566 return null; |
| 3567 } |
| 3568 if (object.state == IterationMarker.YIELD_SINGLE) { |
| 3569 controller.add(object.value); |
| 3570 // If the controller is paused we stop producing more values. |
| 3571 if (controller.isPaused) { |
| 3572 return null; |
| 3573 } |
| 3574 // TODO(sigurdm): We should not suspend here according to the spec. |
| 3575 scheduleMicrotask(() { |
| 3576 _wrapJsFunctionForStream(helperCallback, controller)(null); |
| 3577 }); |
| 3578 return; |
| 3579 } else if (object.state == IterationMarker.YIELD_STAR) { |
| 3580 Stream stream = object.value; |
| 3581 controller.isAdding = true; |
| 3582 // Errors of [stream] are passed though to the main stream. (see |
| 3583 // [AsyncStreamController.addStream]. |
| 3584 // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad. |
| 3585 controller.addStream(stream).then((_) { |
| 3586 controller.isAdding = false; |
| 3587 _wrapJsFunctionForStream(helperCallback, controller)(null); |
| 3588 }); |
| 3589 return null; |
| 3590 } |
| 3591 } |
| 3592 |
| 3593 Future future = object is Future ? object : new Future.value(object); |
| 3594 future.then(_wrapJsFunctionForStream(helperCallback, controller), |
| 3595 onError: errorCallback == null |
| 3596 ? null |
| 3597 : _wrapJsFunctionForStream(errorCallback, controller)); |
| 3598 return controller.stream; |
| 3599 } |
| 3600 |
| 3601 /// A wrapper around a [StreamController] that remembers if that controller |
| 3602 /// got a cancel. |
| 3603 /// |
| 3604 /// Also has a subSubscription that when not null will provide events for the |
| 3605 /// stream, and will be paused and resumed along with this controller. |
| 3606 class AsyncStarStreamController { |
| 3607 StreamController controller; |
| 3608 Stream get stream => controller.stream; |
| 3609 bool stopRunning = false; |
| 3610 bool isAdding = false; |
| 3611 bool get isPaused => controller.isPaused; |
| 3612 add(event) => controller.add(event); |
| 3613 addStream(Stream stream) { |
| 3614 return controller.addStream(stream, cancelOnError: false); |
| 3615 } |
| 3616 addError(error, stackTrace) => controller.addError(error, stackTrace); |
| 3617 close() => controller.close(); |
| 3618 |
| 3619 AsyncStarStreamController(helperCallback) { |
| 3620 controller = new StreamController( |
| 3621 onResume: () { |
| 3622 if (!isAdding) { |
| 3623 streamHelper(null, helperCallback, this, null); |
| 3624 } |
| 3625 }, onCancel: () { |
| 3626 stopRunning = true; |
| 3627 }); |
| 3628 } |
| 3629 } |
| 3630 |
| 3631 makeAsyncStarController(helperCallback) { |
| 3632 return new AsyncStarStreamController(helperCallback); |
| 3633 } |
| 3634 |
| 3635 Function _wrapJsFunctionForStream(dynamic /* js function */ function, |
| 3636 AsyncStarStreamController controller) { |
| 3637 return (result) { |
| 3638 try { |
| 3639 JS('', '#(#)', function, result); |
| 3640 } catch (e, st) { |
| 3641 controller.addError(e, st); |
| 3642 } |
| 3643 }; |
| 3644 } |
| 3645 |
| 3646 |
| 3647 class IterationMarker { |
| 3648 static const YIELD_SINGLE = 0; |
| 3649 static const YIELD_STAR = 1; |
| 3650 static const ITERATION_ENDED = 2; |
| 3651 |
| 3652 final value; |
| 3653 final int state; |
| 3654 |
| 3655 IterationMarker._(this.state, this.value); |
| 3656 |
| 3657 static yieldStar(dynamic /* Iterable or Stream */ values) { |
| 3658 return new IterationMarker._(YIELD_STAR, values); |
| 3659 } |
| 3660 |
| 3661 static endOfIteration() { |
| 3662 return new IterationMarker._(ITERATION_ENDED, null); |
| 3663 } |
| 3664 |
| 3665 static yieldSingle(dynamic value) { |
| 3666 return new IterationMarker._(YIELD_SINGLE, value); |
| 3667 } |
| 3668 |
| 3669 toString() => "IterationMarker($state, $value)"; |
| 3670 } |
| 3671 |
| 3672 class SyncStarIterator implements Iterator { |
| 3673 final Function _helper; |
| 3674 |
| 3675 // If [runningNested] this is the nested iterator, otherwise it is the |
| 3676 // current value. |
| 3677 dynamic _current = null; |
| 3678 bool _runningNested = false; |
| 3679 |
| 3680 get current => _runningNested ? _current.current : _current; |
| 3681 |
| 3682 SyncStarIterator(helper) |
| 3683 : _helper = ((arg) => JS('', '#(#)', helper, arg)); |
| 3684 |
| 3685 bool moveNext() { |
| 3686 if (_runningNested) { |
| 3687 if (_current.moveNext()) { |
| 3688 return true; |
| 3689 } else { |
| 3690 _runningNested = false; |
| 3691 } |
| 3692 } |
| 3693 _current = _helper(null); |
| 3694 if (_current is IterationMarker) { |
| 3695 if (_current.state == IterationMarker.ITERATION_ENDED) { |
| 3696 _current = null; |
| 3697 // Rely on [_helper] to repeatedly return `ITERATION_ENDED`. |
| 3698 return false; |
| 3699 } else { |
| 3700 assert(_current.state == IterationMarker.YIELD_STAR); |
| 3701 _current = _current.value.iterator; |
| 3702 _runningNested = true; |
| 3703 return moveNext(); |
| 3704 } |
| 3705 } |
| 3706 return true; |
| 3707 } |
| 3708 } |
| 3709 |
| 3710 /// An Iterable corresponding to a sync* method. |
| 3711 /// |
| 3712 /// Each invocation of a sync* method will return a new instance of this class. |
| 3713 class SyncStarIterable extends IterableBase { |
| 3714 // This is a function that will return a helper function that does the |
| 3715 // iteration of the sync*. |
| 3716 // |
| 3717 // Each invocation should give a helper with fresh state. |
| 3718 final dynamic /* js function */ _outerHelper; |
| 3719 |
| 3720 SyncStarIterable(this._outerHelper); |
| 3721 |
| 3722 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
| 3723 } |
OLD | NEW |