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' |
| 29 show Future, |
| 30 DeferredLoadException, |
| 31 Completer, |
| 32 StreamController, |
| 33 Stream, |
| 34 StreamSubscription; |
29 | 35 |
30 import 'dart:_foreign_helper' show | 36 import 'dart:_foreign_helper' show |
31 DART_CLOSURE_TO_JS, | 37 DART_CLOSURE_TO_JS, |
32 JS, | 38 JS, |
33 JS_CALL_IN_ISOLATE, | 39 JS_CALL_IN_ISOLATE, |
34 JS_CONST, | 40 JS_CONST, |
35 JS_CURRENT_ISOLATE, | 41 JS_CURRENT_ISOLATE, |
36 JS_CURRENT_ISOLATE_CONTEXT, | 42 JS_CURRENT_ISOLATE_CONTEXT, |
37 JS_DART_OBJECT_CONSTRUCTOR, | 43 JS_DART_OBJECT_CONSTRUCTOR, |
38 JS_EFFECT, | 44 JS_EFFECT, |
(...skipping 3409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3448 throw new MainError("No top-level function named 'main'."); | 3454 throw new MainError("No top-level function named 'main'."); |
3449 } | 3455 } |
3450 | 3456 |
3451 void badMain() { | 3457 void badMain() { |
3452 throw new MainError("'main' is not a function."); | 3458 throw new MainError("'main' is not a function."); |
3453 } | 3459 } |
3454 | 3460 |
3455 void mainHasTooManyParameters() { | 3461 void mainHasTooManyParameters() { |
3456 throw new MainError("'main' expects too many parameters."); | 3462 throw new MainError("'main' expects too many parameters."); |
3457 } | 3463 } |
| 3464 |
| 3465 /// Runtime support for async-await transformation. |
| 3466 /// |
| 3467 /// This function is called by a transformed function on each await and return |
| 3468 /// in the untransformed function, and before starting. |
| 3469 /// |
| 3470 /// If [object] is not a future it will be wrapped in a `new Future.value`. |
| 3471 /// |
| 3472 /// If [helperCallback] is null it indicates a return from the async function, |
| 3473 /// and we complete the completer with object. |
| 3474 /// |
| 3475 /// Otherwise [helperCallback] is set up to be called when the future is |
| 3476 /// successfull and [errorCallback] if it is completed with an error. |
| 3477 /// |
| 3478 /// If helperCallback or errorCallback throws we complete the completer with the |
| 3479 /// error. |
| 3480 /// |
| 3481 /// Returns the future of the completer for convenience of the first call. |
| 3482 dynamic thenHelper(dynamic object, |
| 3483 dynamic /* js function */ helperCallback, |
| 3484 Completer completer, |
| 3485 dynamic /* js function */ errorCallback) { |
| 3486 if (helperCallback == null) { |
| 3487 completer.complete(object); |
| 3488 return; |
| 3489 } |
| 3490 Future future = object is Future ? object : new Future.value(object); |
| 3491 future.then(_wrapJsFunction(helperCallback, completer), |
| 3492 onError: _wrapJsFunction(errorCallback, completer)); |
| 3493 return completer.future; |
| 3494 } |
| 3495 |
| 3496 // Used to avoid creating two identical closure classes in [thenHelper]. |
| 3497 Function _wrapJsFunction(dynamic /* js function */ function, |
| 3498 Completer completer) { |
| 3499 return (result) { |
| 3500 try { |
| 3501 JS('', '#(#)', function, result); |
| 3502 } catch (e, st) { |
| 3503 completer.completeError(e, st); |
| 3504 } |
| 3505 }; |
| 3506 } |
| 3507 |
| 3508 |
| 3509 /// Runtime support for async*. |
| 3510 /// |
| 3511 /// Called by the transformed function for each original return, await, yield, |
| 3512 /// yield* and before starting the function. |
| 3513 /// |
| 3514 /// On a return call with helperCallback == null, and we close the stream. |
| 3515 /// |
| 3516 /// If object is an [IterationMarker] it indicates a yield or yield*. |
| 3517 /// If the stream subscription has been canceled we schedule the errorCallback |
| 3518 /// that should run through enclosing finally blocks before exiting. |
| 3519 /// |
| 3520 /// If it is a single-yield we add the value to the stream. |
| 3521 /// If the stream subscription has been paused we return early, otherwise |
| 3522 /// we schedule the helper function to be executed again. |
| 3523 /// |
| 3524 /// If it is a yield* we start listening to the yielded stream, and add all |
| 3525 /// events and errors to our own controller (taking care if the subscription has |
| 3526 /// been paused or canceled) - when the sub-stream is done we schedule the |
| 3527 /// helper function again. |
| 3528 /// |
| 3529 /// If object is not and [IterationMarker] it indicates an await. |
| 3530 /// If object is not a Future, it is wrapped in a `Future.value`. |
| 3531 /// Then set up helperCallBack to be called on successfull completion of the |
| 3532 /// error |
| 3533 /// |
| 3534 /// If helperCallback or errorCallback throws we add the error to the stream |
| 3535 /// If the object is a YIELD_SINGLE we add the object to the stream, and if the |
| 3536 /// stream has been canceled call the errorCallBack. |
| 3537 dynamic streamHelper(dynamic object, |
| 3538 dynamic /* js function */ helperCallback, |
| 3539 AsyncStarStreamController controller, |
| 3540 dynamic /* js function */ errorCallback) { |
| 3541 if (helperCallback == null) { |
| 3542 // This happens on return from the async* function. |
| 3543 controller.close(); |
| 3544 return; |
| 3545 } |
| 3546 |
| 3547 if (object is IterationMarker) { |
| 3548 if (controller.stopRunning) { |
| 3549 _wrapJsFunctionForStream(errorCallback, controller)(); |
| 3550 return; |
| 3551 } |
| 3552 if (object.state == IterationMarker.YIELD_SINGLE) { |
| 3553 controller.add(object.value); |
| 3554 } else if (object.state == IterationMarker.YIELD_STAR) { |
| 3555 Stream stream = object.value; |
| 3556 controller.subSubscription = stream.listen((value) { |
| 3557 if (controller.stopRunning) { |
| 3558 controller.subSubscription.cancel(); |
| 3559 _wrapJsFunctionForStream(errorCallback, controller)(null); |
| 3560 return; |
| 3561 } |
| 3562 controller.add(value); |
| 3563 }, onError: (error, stackTrace) { |
| 3564 if (controller.stopRunning) { |
| 3565 controller.subSubscription.cancel(); |
| 3566 // This will run the enclosing finally blocks. |
| 3567 _wrapJsFunctionForStream(errorCallback, controller)(null); |
| 3568 return; |
| 3569 } |
| 3570 controller.addError(error, stackTrace); |
| 3571 if (controller.isPaused) { |
| 3572 controller.subSubscription.pause(); |
| 3573 } |
| 3574 }, onDone: () { |
| 3575 controller.subSubscription = null; |
| 3576 // Resume producing. |
| 3577 streamHelper(null, helperCallback, controller, null); |
| 3578 }); |
| 3579 return; |
| 3580 } |
| 3581 if (controller.isPaused) { |
| 3582 return; |
| 3583 } |
| 3584 object = null; |
| 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 StreamSubscription subSubscription = null; |
| 3602 Stream get stream => controller.stream; |
| 3603 bool stopRunning = false; |
| 3604 bool get isPaused => controller.isPaused; |
| 3605 add(event) => controller.add(event); |
| 3606 addError(error, stackTrace) => controller.addError(error, stackTrace); |
| 3607 close() => controller.close(); |
| 3608 |
| 3609 AsyncStarStreamController(helperCallBack) { |
| 3610 controller = new StreamController( |
| 3611 onPause: () { |
| 3612 if (subSubscription != null) { |
| 3613 subSubscription.pause(); |
| 3614 } |
| 3615 }, |
| 3616 onResume: () { |
| 3617 if (subSubscription == null) { |
| 3618 helperCallBack(null, helperCallBack, controller, null); |
| 3619 } else { |
| 3620 subSubscription.resume(); |
| 3621 } |
| 3622 }, onCancel: () { |
| 3623 stopRunning = true; |
| 3624 }); |
| 3625 } |
| 3626 } |
| 3627 |
| 3628 makeAsyncStarController(helperCallback) { |
| 3629 return new AsyncStarStreamController(helperCallback); |
| 3630 } |
| 3631 |
| 3632 // Used to avoid creating identical closure classes in [streamHelper]. |
| 3633 Function _wrapJsFunctionForStream(dynamic /* js function */ function, |
| 3634 AsyncStarStreamController controller) { |
| 3635 return (result) { |
| 3636 try { |
| 3637 JS('', '#(#)', function, result); |
| 3638 } catch (e, st) { |
| 3639 controller.addError(e, st); |
| 3640 } |
| 3641 }; |
| 3642 } |
| 3643 |
| 3644 |
| 3645 class IterationMarker { |
| 3646 static const YIELD_SINGLE = 0; |
| 3647 static const YIELD_STAR = 1; |
| 3648 static const ITERATION_ENDED = 2; |
| 3649 |
| 3650 final value; |
| 3651 final int state; |
| 3652 |
| 3653 IterationMarker._(this.state, this.value); |
| 3654 |
| 3655 static yieldStar(Iterable iterable) { |
| 3656 return new IterationMarker._(YIELD_STAR, iterable); |
| 3657 } |
| 3658 |
| 3659 static endOfIteration() { |
| 3660 return new IterationMarker._(ITERATION_ENDED, null); |
| 3661 } |
| 3662 |
| 3663 static yieldSingle(value) { |
| 3664 return new IterationMarker._(YIELD_SINGLE, value); |
| 3665 } |
| 3666 |
| 3667 toString() => "IterationMarker($state, $value)"; |
| 3668 } |
| 3669 |
| 3670 class SyncStarIterator implements Iterator { |
| 3671 final Function _helper; |
| 3672 |
| 3673 // if [runningNested] this will be the nested iterator, otherwise it will be |
| 3674 // the current value. |
| 3675 dynamic _current = null; |
| 3676 bool _runningNested = false; |
| 3677 |
| 3678 get current => _runningNested ? _current.current : _current; |
| 3679 |
| 3680 SyncStarIterator(helper) |
| 3681 : _helper = ((arg) => JS('', '#(#)', helper, arg)); |
| 3682 |
| 3683 bool moveNext() { |
| 3684 if (_runningNested) { |
| 3685 if (_current.moveNext()) { |
| 3686 return true; |
| 3687 } else { |
| 3688 _runningNested = false; |
| 3689 } |
| 3690 } |
| 3691 _current = _helper(null); |
| 3692 if (_current is IterationMarker) { |
| 3693 if (_current.state == IterationMarker.ITERATION_ENDED) { |
| 3694 _current = null; |
| 3695 // We rely on [_helper] to repeatedly return `ITERATION_ENDED`. |
| 3696 return false; |
| 3697 } else { |
| 3698 assert(_current.state == IterationMarker.YIELD_STAR); |
| 3699 _current = _current.value.iterator; |
| 3700 _runningNested = true; |
| 3701 return moveNext(); |
| 3702 } |
| 3703 } |
| 3704 return true; |
| 3705 } |
| 3706 } |
| 3707 |
| 3708 /// An Iterable corresponding to a sync* method. |
| 3709 /// |
| 3710 /// Each invocation of a sync* method will return a new instance of this class. |
| 3711 class SyncStarIterable extends IterableBase { |
| 3712 // This is a function that will return a helper function that does the |
| 3713 // iteration of the sync*. |
| 3714 // |
| 3715 // Each invocation should give a helper with fresh state. |
| 3716 final dynamic /* js function */ _outerHelper; |
| 3717 |
| 3718 SyncStarIterable(this._outerHelper); |
| 3719 |
| 3720 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); |
| 3721 } |
| 3722 |
OLD | NEW |