[dartdevc] simplify microtask scheduling using JS Promise
All of DDC's supported platforms have Promises, so we can use them
instead of MutationObservers (web) and timers (node.js).
See issue #20055 (same issue, but for dart2js).
Change-Id: Id635a4a9fa104a2ab19dd20824d209f682f831f9
Reviewed-on: https://dart-review.googlesource.com/c/91765
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Jenny Messerly <jmesserly@google.com>
diff --git a/pkg/dev_compiler/test/sourcemap/ddc_common.dart b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
index 3d187dd..437b008 100644
--- a/pkg/dev_compiler/test/sourcemap/ddc_common.dart
+++ b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
@@ -134,15 +134,24 @@
let global = new Function('return this;')();
$d8Preambles
+ // d8 does not seem to print the `.stack` property like
+ // node.js and browsers do, so include that.
+ Error.prototype.toString = function() {
+ // Note: on d8, the stack property includes the error message too.
+ return this.stack;
+ };
+
+ global.scheduleImmediate = function(callback) {
+ // Ensure unhandled promise rejections get printed.
+ Promise.resolve(null).then(callback).catch(e => console.error(e));
+ };
+
let main = $inputFileNameNoExt.main;
dart.ignoreWhitelistedErrors(false);
try {
dartMainRunner(main, []);
} catch(e) {
console.error(e);
- // d8 does not seem to print the `.stack` property like
- // node.js and browsers do.
- console.error(e.stack);
}
""";
}
diff --git a/pkg/dev_compiler/tool/input_sdk/patch/async_patch.dart b/pkg/dev_compiler/tool/input_sdk/patch/async_patch.dart
index fe36fea..ca4ef7d 100644
--- a/pkg/dev_compiler/tool/input_sdk/patch/async_patch.dart
+++ b/pkg/dev_compiler/tool/input_sdk/patch/async_patch.dart
@@ -9,9 +9,6 @@
import 'dart:_foreign_helper' show JS, JSExportName;
import 'dart:_runtime' as dart;
-typedef void _Callback();
-typedef void _TakeCallback(_Callback callback);
-
/// This function adapts ES6 generators to implement Dart's async/await.
///
/// It's designed to interact with Dart's Future and follow Dart async/await
@@ -135,77 +132,39 @@
@patch
class _AsyncRun {
@patch
- static void _scheduleImmediate(void callback()) {
+ static void _scheduleImmediate(void Function() callback) {
_scheduleImmediateClosure(callback);
}
// Lazily initialized.
- static final _TakeCallback _scheduleImmediateClosure =
- _initializeScheduleImmediate();
+ static final _scheduleImmediateClosure = _initializeScheduleImmediate();
- static _TakeCallback _initializeScheduleImmediate() {
- // TODO(rnystrom): Not needed by dev_compiler.
- // requiresPreamble();
+ static void Function(void Function()) _initializeScheduleImmediate() {
+ // d8 support, see preambles/d8.js for the definiton of `scheduleImmediate`.
+ //
+ // TODO(jmesserly): do we need this? It's only for our d8 stack trace test.
if (JS('', '#.scheduleImmediate', dart.global_) != null) {
- return _scheduleImmediateJsOverride;
+ return _scheduleImmediateJSOverride;
}
- if (JS('', '#.MutationObserver', dart.global_) != null &&
- JS('', '#.document', dart.global_) != null) {
- // Use mutationObservers.
- var div = JS('', '#.document.createElement("div")', dart.global_);
- var span = JS('', '#.document.createElement("span")', dart.global_);
- _Callback storedCallback;
-
- internalCallback(_) {
- var f = storedCallback;
- storedCallback = null;
- dart.removeAsyncCallback();
- f();
- }
-
- var observer =
- JS('', 'new #.MutationObserver(#)', dart.global_, internalCallback);
- JS('', '#.observe(#, { childList: true })', observer, div);
-
- return (void callback()) {
- assert(storedCallback == null);
- dart.addAsyncCallback();
- 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', dart.global_) != null) {
- return _scheduleImmediateWithSetImmediate;
- }
- // TODO(20055): We should use DOM promises when available.
- return _scheduleImmediateWithTimer;
+ return _scheduleImmediateWithPromise;
}
- static void _scheduleImmediateJsOverride(void callback()) {
- internalCallback() {
+ @ReifyFunctionTypes(false)
+ static void _scheduleImmediateJSOverride(void Function() callback) {
+ dart.addAsyncCallback();
+ JS('void', '#.scheduleImmediate(#)', dart.global_, () {
dart.removeAsyncCallback();
callback();
- }
-
- dart.addAsyncCallback();
- JS('void', '#.scheduleImmediate(#)', dart.global_, internalCallback);
+ });
}
- static void _scheduleImmediateWithSetImmediate(void callback()) {
- internalCallback() {
+ @ReifyFunctionTypes(false)
+ static Object _scheduleImmediateWithPromise(void Function() callback) {
+ dart.addAsyncCallback();
+ JS('', '#.Promise.resolve(null).then(#)', dart.global_, () {
dart.removeAsyncCallback();
callback();
- }
-
- dart.addAsyncCallback();
- JS('void', '#.setImmediate(#)', dart.global_, internalCallback);
- }
-
- static void _scheduleImmediateWithTimer(void callback()) {
- Timer._createTimer(Duration.zero, callback);
+ });
}
}
diff --git a/pkg/expect/lib/async_minitest.dart b/pkg/expect/lib/async_minitest.dart
index 6d135bc..40c69da 100644
--- a/pkg/expect/lib/async_minitest.dart
+++ b/pkg/expect/lib/async_minitest.dart
@@ -56,7 +56,7 @@
_popName(oldName);
}
-void expect(Object value, Object matcher) {
+void expect(Object value, Object matcher, {String reason}) {
Matcher m;
if (matcher is _Matcher) {
m = matcher.call;
@@ -260,6 +260,10 @@
Expect.type<List>(o);
}
+void isNotNull(Object o) {
+ Expect.isNotNull(o);
+}
+
abstract class _Matcher {
void call(Object o);
}
diff --git a/tests/lib_2/html/websql_test.dart b/tests/lib_2/html/websql_test.dart
index 1d52d5d..07f9ff2 100644
--- a/tests/lib_2/html/websql_test.dart
+++ b/tests/lib_2/html/websql_test.dart
@@ -4,9 +4,7 @@
import 'dart:html';
import 'dart:web_sql';
-import 'package:unittest/unittest.dart';
-import 'package:unittest/html_config.dart';
-import 'package:async_helper/async_helper.dart';
+import 'package:expect/async_minitest.dart';
Future<SqlResultSet> createTable(
SqlTransaction transaction, String tableName, String columnName) async {
@@ -52,8 +50,6 @@
}
main() async {
- useHtmlConfiguration();
-
await setup();
group('Database', () {
diff --git a/tools/testing/dart/test_controller.js b/tools/testing/dart/test_controller.js
index 8a82fce..8515ddf 100644
--- a/tools/testing/dart/test_controller.js
+++ b/tools/testing/dart/test_controller.js
@@ -103,6 +103,17 @@
notifyDone('FAIL');
};
+window.onunhandledrejection = function (e) {
+ var reason = e.reason != null ? e.reason.stack : null;
+ var message = ('window.onunhandledrejection called: \n\n' + reason + '\n\n');
+ if (testExpectsGlobalError) {
+ testSuppressedGlobalErrors.push({message: message});
+ return;
+ }
+ recordEvent('window_onerror', message);
+ notifyDone('FAIL');
+};
+
var waitForDone = false;
var driverWindowCached = false;