| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| // Patch file for the dart:async library. | |
| import 'dart:_js_helper' show patch, setTraceForException, ReifyFunctionTypes; | |
| import 'dart:_isolate_helper' | |
| show TimerImpl, global, leaveJsAsync, enterJsAsync; | |
| import 'dart:_foreign_helper' show JS, JSExportName; | |
| import 'dart:_runtime' as dart; | |
| typedef void _Callback(); | |
| typedef void _TakeCallback(_Callback callback); | |
| @JSExportName('async') | |
| @ReifyFunctionTypes(false) | |
| _async<T>(Function() initGenerator) { | |
| var iter; | |
| Object Function(Object) onValue; | |
| Object Function(Object) onError; | |
| onAwait(Object value) { | |
| _Future f; | |
| if (value is _Future) { | |
| f = value; | |
| } else if (value is Future) { | |
| f = new _Future(); | |
| _Future._chainForeignFuture(value, f); | |
| } else { | |
| f = new _Future.value(value); | |
| } | |
| f = JS('', '#', f._thenNoZoneRegistration(onValue, onError)); | |
| return f; | |
| } | |
| onValue = (value) { | |
| var iteratorResult = JS('', '#.next(#)', iter, value); | |
| value = JS('', '#.value', iteratorResult); | |
| return JS('bool', '#.done', iteratorResult) ? value : onAwait(value); | |
| }; | |
| // If the awaited Future throws, we want to convert this to an exception | |
| // thrown from the `yield` point, as if it was thrown there. | |
| // | |
| // If the exception is not caught inside `gen`, it will emerge here, which | |
| // will send it to anyone listening on this async function's Future<T>. | |
| // | |
| // In essence, we are giving the code inside the generator a chance to | |
| // use try-catch-finally. | |
| onError = (value) { | |
| var iteratorResult = JS('', '#.throw(#)', iter, value); | |
| value = JS('', '#.value', iteratorResult); | |
| return JS('bool', '#.done', iteratorResult) ? value : onAwait(value); | |
| }; | |
| var zone = Zone.current; | |
| if (zone != Zone.root) { | |
| onValue = zone.registerUnaryCallback(onValue); | |
| onError = zone.registerUnaryCallback(onError); | |
| } | |
| var asyncFuture = new _Future<T>(); | |
| var body = () { | |
| try { | |
| iter = JS('', '#[Symbol.iterator]()', initGenerator()); | |
| var iteratorValue = JS('', '#.next(null)', iter); | |
| var value = JS('', '#.value', iteratorValue); | |
| if (JS('bool', '#.done', iteratorValue)) { | |
| // TODO(jmesserly): this is needed to work around unsoundness in our | |
| // allowed cast failures. We have async methods that return a raw Future | |
| // where a Future<T> is expected. If we call: | |
| // | |
| // asyncFuture._complete(value); | |
| // | |
| // Then it ends up interpreting these invalid Future<dynamic> as values | |
| // rather than as futures (because complete checks `is Future<T>`). | |
| // | |
| // For now we inline `_Future._complete` and handle the unsoundness by | |
| // checking against raw future types instead of the Fuutre<T> types. | |
| if (value is Future) { | |
| if (value is _Future) { | |
| _Future._chainCoreFuture(value, asyncFuture); | |
| } else { | |
| _Future._chainForeignFuture(value, asyncFuture); | |
| } | |
| } else { | |
| asyncFuture._completeWithValue(JS('', '#', value)); | |
| } | |
| } else { | |
| _Future._chainCoreFuture(onAwait(value), asyncFuture); | |
| } | |
| } catch (e, s) { | |
| if (dart.startAsyncSynchronously) { | |
| scheduleMicrotask(() { | |
| _completeWithErrorCallback(asyncFuture, e, s); | |
| }); | |
| } else { | |
| _completeWithErrorCallback(asyncFuture, e, s); | |
| } | |
| } | |
| }; | |
| if (dart.startAsyncSynchronously) { | |
| body(); | |
| } else { | |
| scheduleMicrotask(body); | |
| } | |
| return asyncFuture; | |
| } | |
| @patch | |
| class _AsyncRun { | |
| @patch | |
| static void _scheduleImmediate(void callback()) { | |
| _scheduleImmediateClosure(callback); | |
| } | |
| // Lazily initialized. | |
| static final _TakeCallback _scheduleImmediateClosure = | |
| _initializeScheduleImmediate(); | |
| static _TakeCallback _initializeScheduleImmediate() { | |
| // TODO(rnystrom): Not needed by dev_compiler. | |
| // requiresPreamble(); | |
| if (JS('', '#.scheduleImmediate', global) != null) { | |
| return _scheduleImmediateJsOverride; | |
| } | |
| if (JS('', '#.MutationObserver', global) != null && | |
| JS('', '#.document', global) != null) { | |
| // Use mutationObservers. | |
| var div = JS('', '#.document.createElement("div")', global); | |
| var span = JS('', '#.document.createElement("span")', global); | |
| _Callback storedCallback; | |
| internalCallback(_) { | |
| leaveJsAsync(); | |
| var f = storedCallback; | |
| storedCallback = null; | |
| f(); | |
| } | |
| ; | |
| var observer = | |
| JS('', 'new #.MutationObserver(#)', global, internalCallback); | |
| JS('', '#.observe(#, { childList: true })', observer, div); | |
| return (void callback()) { | |
| assert(storedCallback == null); | |
| enterJsAsync(); | |
| storedCallback = callback; | |
| // Because of a broken shadow-dom polyfill we have to change the | |
| // children instead a cheap property. | |
| // See https://github.com/Polymer/ShadowDOM/issues/468 | |
| JS('', '#.firstChild ? #.removeChild(#): #.appendChild(#)', div, div, | |
| span, div, span); | |
| }; | |
| } else if (JS('', '#.setImmediate', global) != null) { | |
| return _scheduleImmediateWithSetImmediate; | |
| } | |
| // TODO(20055): We should use DOM promises when available. | |
| return _scheduleImmediateWithTimer; | |
| } | |
| static void _scheduleImmediateJsOverride(void callback()) { | |
| internalCallback() { | |
| leaveJsAsync(); | |
| callback(); | |
| } | |
| ; | |
| enterJsAsync(); | |
| JS('void', '#.scheduleImmediate(#)', global, internalCallback); | |
| } | |
| static void _scheduleImmediateWithSetImmediate(void callback()) { | |
| internalCallback() { | |
| leaveJsAsync(); | |
| callback(); | |
| } | |
| ; | |
| enterJsAsync(); | |
| JS('void', '#.setImmediate(#)', global, internalCallback); | |
| } | |
| static void _scheduleImmediateWithTimer(void callback()) { | |
| Timer._createTimer(Duration.zero, callback); | |
| } | |
| } | |
| @patch | |
| class DeferredLibrary { | |
| @patch | |
| Future<Null> load() { | |
| throw 'DeferredLibrary not supported. ' | |
| 'please use the `import "lib.dart" deferred as lib` syntax.'; | |
| } | |
| } | |
| @patch | |
| class Timer { | |
| @patch | |
| static Timer _createTimer(Duration duration, void callback()) { | |
| int milliseconds = duration.inMilliseconds; | |
| if (milliseconds < 0) milliseconds = 0; | |
| return new TimerImpl(milliseconds, callback); | |
| } | |
| @patch | |
| static Timer _createPeriodicTimer( | |
| Duration duration, void callback(Timer timer)) { | |
| int milliseconds = duration.inMilliseconds; | |
| if (milliseconds < 0) milliseconds = 0; | |
| return new TimerImpl.periodic(milliseconds, callback); | |
| } | |
| } | |
| @patch | |
| void _rethrow(Object error, StackTrace stackTrace) { | |
| setTraceForException(error, stackTrace); | |
| dart.throw_(error); | |
| } |