Version 2.19.0-38.0.dev

Merge commit '63a9057f12ac9b7d4e6f2ca698a3e38dc161d409' into 'dev'
diff --git a/pkg/dart2js_info/pubspec.yaml b/pkg/dart2js_info/pubspec.yaml
index 04e3c7b..68bab56 100644
--- a/pkg/dart2js_info/pubspec.yaml
+++ b/pkg/dart2js_info/pubspec.yaml
@@ -20,6 +20,7 @@
 
 # Use 'any' constraints here; we get our versions from the DEPS file.
 dev_dependencies:
+  expect: any
   lints: any
   test: any
 
diff --git a/pkg/dart2js_info/test/classes/class_filter.txt b/pkg/dart2js_info/test/classes/class_filter.txt
new file mode 100644
index 0000000..aa011aa
--- /dev/null
+++ b/pkg/dart2js_info/test/classes/class_filter.txt
@@ -0,0 +1,5 @@
+dart:_rti - _Universe
+dart:core - Error
+testroot:classes.dart - Subsub1
+testroot:classes.dart - Super
+package:expect/expect.dart - Expect
diff --git a/pkg/dart2js_info/test/classes/classes.dart b/pkg/dart2js_info/test/classes/classes.dart
new file mode 100644
index 0000000..d7b49e0
--- /dev/null
+++ b/pkg/dart2js_info/test/classes/classes.dart
@@ -0,0 +1,45 @@
+import 'package:expect/expect.dart';
+
+class Super<T> {
+  void method(T t) {
+    print(t.runtimeType);
+  }
+}
+
+class Mixin {
+  void method(int t) {
+    print(t + 1);
+  }
+}
+
+class Clazz = Super<int> with Mixin;
+
+class Subclass extends Clazz {
+  void test() {
+    void Function(int) f = super.method;
+    f(42);
+    print(f);
+  }
+}
+
+class Subsub1 extends Subclass {
+  void a(dynamic x) {
+    print(x);
+  }
+}
+
+class Subsub2 extends Subclass {
+  void a(dynamic x) {
+    print(x);
+    print(x);
+  }
+}
+
+main() {
+  Super<Object> s = Subclass()..test();
+  Expect.throws(() => s.method(''));
+  dynamic x = Subsub1();
+  x.a(x);
+  x = Subsub2();
+  x.a(x);
+}
diff --git a/pkg/dart2js_info/test/classes/classes.js.info.data b/pkg/dart2js_info/test/classes/classes.js.info.data
new file mode 100644
index 0000000..cc738e1
--- /dev/null
+++ b/pkg/dart2js_info/test/classes/classes.js.info.data
Binary files differ
diff --git a/pkg/dart2js_info/test/runtime_coverage_test.dart b/pkg/dart2js_info/test/runtime_coverage_test.dart
new file mode 100644
index 0000000..9d5d048
--- /dev/null
+++ b/pkg/dart2js_info/test/runtime_coverage_test.dart
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, 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.
+
+// Tests for dart2js_info's runtime_coverage command.
+//
+// Regenerate files with dart2js flags:
+// --multi-root-scheme='testroot'
+// --multi-root='$PATH_TO_TEST_ROOT'
+// --entry-uri='testroot:$TEST_FILE.dart'
+// --dump-info=binary
+// --packages=$PATH_TO_SDK/.dart_tool/package_config.json
+
+import 'dart:io';
+
+import 'package:dart2js_info/binary_serialization.dart';
+import 'package:dart2js_info/src/util.dart';
+import 'package:test/test.dart';
+
+import '../bin/src/runtime_coverage_analysis.dart';
+
+void main() {
+  group('runtime coverage', () {
+    group('class filter (angular info)', () {
+      final infoBinaryFile =
+          File.fromUri(Platform.script.resolve('classes/classes.js.info.data'));
+      final allInfo = decode(infoBinaryFile.readAsBytesSync());
+      final classFilters =
+          File.fromUri(Platform.script.resolve('classes/class_filter.txt'))
+              .readAsLinesSync();
+      final runtimeClassInfos = <String, RuntimeClassInfo>{};
+
+      setUp(() {
+        runtimeClassInfos.clear();
+      });
+
+      test('class filters are formatted properly', () {
+        for (final filterString in classFilters) {
+          expect(filterString.contains(' - '), isTrue);
+        }
+      });
+
+      test('AngularInfo conversions throws on invalid schemes', () {
+        expect(
+            () => RuntimeClassInfo.fromAngularInfo(
+                'no/scheme/here.dart - ClassName'),
+            throwsArgumentError);
+        expect(
+            () => RuntimeClassInfo.fromAngularInfo('noscheme.dart - ClassName'),
+            throwsArgumentError);
+      });
+
+      test('class filters parse and annotate properly', () {
+        // Process class filters.
+        for (final filterString in classFilters) {
+          final runtimeClassInfo =
+              RuntimeClassInfo.fromAngularInfo(filterString);
+          expect(runtimeClassInfo.annotated, isFalse);
+          runtimeClassInfos[runtimeClassInfo.key] = runtimeClassInfo;
+        }
+
+        // Annotate class filters with their corresponding ClassInfo.
+        for (final classInfo in allInfo.classes) {
+          final name = qualifiedName(classInfo);
+          final nameWithoutScheme =
+              name.substring(name.indexOf(':') + 1, name.length);
+          final runtimeClassInfo = runtimeClassInfos[nameWithoutScheme];
+          if (runtimeClassInfo != null) {
+            runtimeClassInfo.annotateWithClassInfo(classInfo);
+            expect(runtimeClassInfos[runtimeClassInfo.key], isNotNull);
+          }
+        }
+
+        // Check that all class info objects are annotated.
+        for (final runtimeClassInfo in runtimeClassInfos.values) {
+          expect(runtimeClassInfo.annotated, isTrue);
+        }
+      });
+    });
+  });
+}
diff --git a/pkg/dart2wasm/bin/run_wasm.js b/pkg/dart2wasm/bin/run_wasm.js
index 0078a7b..a0673a0 100644
--- a/pkg/dart2wasm/bin/run_wasm.js
+++ b/pkg/dart2wasm/bin/run_wasm.js
@@ -163,6 +163,19 @@
         var factoryFunction = constructor.bind.apply(constructor, [null, ...args]);
         return new factoryFunction();
     },
+    getTimeZoneNameForSeconds: function(secondsSinceEpoch) {
+        var date = new Date(secondsSinceEpoch * 1000);
+        var match = /\((.*)\)/.exec(date.toString());
+        if (match == null) {
+            // This should never happen on any recent browser.
+            return '';
+        }
+        return stringToDartString(match[1]);
+
+    },
+    getTimeZoneOffsetInSeconds: function(secondsSinceEpoch) {
+        return new Date(secondsSinceEpoch * 1000).getTimezoneOffset() * 60;
+    },
 };
 
 function instantiate(filename, imports) {
diff --git a/sdk/lib/_internal/js_shared/lib/js_util_patch.dart b/sdk/lib/_internal/js_shared/lib/js_util_patch.dart
index f67886e..139f411 100644
--- a/sdk/lib/_internal/js_shared/lib/js_util_patch.dart
+++ b/sdk/lib/_internal/js_shared/lib/js_util_patch.dart
@@ -374,14 +374,55 @@
 @patch
 List<Object?> objectKeys(Object? object) => JS('', 'Object.keys(#)', object);
 
+// TODO(joshualitt): Move these `is` checks to a helper library to help
+// declutter this patch file.
+bool _isJavaScriptDate(value) => JS('bool', '# instanceof Date', value);
+bool _isJavaScriptRegExp(value) => JS('bool', '# instanceof RegExp', value);
+
+@patch
+bool isJavaScriptArray(value) => JS('bool', '# instanceof Array', value);
+
+// Although it may be tempting to try and rewrite [isJavaScriptSimpleObject]
+// using `js_util` calls, it turns out this can be fragile on some browsers
+// under some situations.
+@patch
+bool isJavaScriptSimpleObject(value) {
+  var proto = JS('', 'Object.getPrototypeOf(#)', value);
+  return JS('bool', '# === Object.prototype', proto) ||
+      JS('bool', '# === null', proto);
+}
+
+bool _isJavaScriptPromise(value) =>
+    JS('bool', r'typeof Promise != "undefined" && # instanceof Promise', value);
+
+DateTime _dateToDateTime(date) {
+  int millisSinceEpoch = JS('int', '#.getTime()', date);
+  return new DateTime.fromMillisecondsSinceEpoch(millisSinceEpoch, isUtc: true);
+}
+
 @patch
 Object? dartify(Object? o) {
   var _convertedObjects = HashMap.identity();
-  Object? convert() {
+  Object? convert(Object? o) {
     if (_convertedObjects.containsKey(o)) {
       return _convertedObjects[o];
     }
     if (o == null || o is bool || o is num || o is String) return o;
+
+    if (_isJavaScriptDate(o)) {
+      return _dateToDateTime(o);
+    }
+
+    if (_isJavaScriptRegExp(o)) {
+      // TODO(joshualitt): Consider investigating if there is a way to convert
+      // from `JSRegExp` to `RegExp`.
+      throw new ArgumentError('structured clone of RegExp');
+    }
+
+    if (_isJavaScriptPromise(o)) {
+      return promiseToFuture(o);
+    }
+
     if (isJavaScriptSimpleObject(o)) {
       Map<Object?, Object?> dartObject = {};
       _convertedObjects[o] = dartObject;
@@ -394,23 +435,27 @@
         Object? jsKey = originalKeys[i];
         Object? dartKey = dartKeys[i];
         if (jsKey != null) {
-          dartObject[dartKey] = dartify(getProperty(o, jsKey));
+          dartObject[dartKey] = convert(getProperty(o, jsKey));
         }
       }
       return dartObject;
     }
+
     if (isJavaScriptArray(o)) {
+      var l = JS<List>('returns:List;creates:;', '#', o);
       List<Object?> dartObject = [];
       _convertedObjects[o] = dartObject;
       int length = getProperty(o, 'length');
       for (int i = 0; i < length; i++) {
-        dartObject.add(dartify(getProperty(o, i)));
+        dartObject.add(convert(l[i]));
       }
       return dartObject;
     }
-    throw ArgumentError(
-        "JavaScriptObject $o must be a primitive, simple object, or array");
+
+    // Assume anything else is already a valid Dart object, either by having
+    // already been processed, or e.g. a cloneable native class.
+    return o;
   }
 
-  return convert();
+  return convert(o);
 }
diff --git a/sdk/lib/_internal/wasm/lib/date_patch.dart b/sdk/lib/_internal/wasm/lib/date_patch.dart
index 9b31078..72f91011 100644
--- a/sdk/lib/_internal/wasm/lib/date_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/date_patch.dart
@@ -17,16 +17,19 @@
 class DateTime {
   // Natives.
   // The natives have been moved up here to work around Issue 10401.
-  @pragma("vm:external-name", "DateTime_currentTimeMicros")
   static int _getCurrentMicros() =>
       (_jsDateNow() * Duration.microsecondsPerMillisecond).toInt();
 
-  @pragma("vm:external-name", "DateTime_timeZoneName")
-  external static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch);
+  @pragma("wasm:import", "dart2wasm.getTimeZoneNameForSeconds")
+  external static String _timeZoneNameForClampedSecondsRaw(
+      double secondsSinceEpoch);
 
-  @pragma("vm:external-name", "DateTime_timeZoneOffsetInSeconds")
-  external static int _timeZoneOffsetInSecondsForClampedSeconds(
-      int secondsSinceEpoch);
+  static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch) =>
+      _timeZoneNameForClampedSecondsRaw(secondsSinceEpoch.toDouble());
+
+  @pragma("wasm:import", "dart2wasm.getTimeZoneOffsetInSeconds")
+  external static double _timeZoneOffsetInSecondsForClampedSeconds(
+      double secondsSinceEpoch);
 
   static const _MICROSECOND_INDEX = 0;
   static const _MILLISECOND_INDEX = 1;
@@ -424,7 +427,9 @@
 
   static int _timeZoneOffsetInSeconds(int microsecondsSinceEpoch) {
     int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch);
-    return _timeZoneOffsetInSecondsForClampedSeconds(equivalentSeconds);
+    return _timeZoneOffsetInSecondsForClampedSeconds(
+            equivalentSeconds.toDouble())
+        .toInt();
   }
 
   static String _timeZoneName(int microsecondsSinceEpoch) {
diff --git a/sdk/lib/_internal/wasm/lib/stopwatch_patch.dart b/sdk/lib/_internal/wasm/lib/stopwatch_patch.dart
new file mode 100644
index 0000000..b20f9eb
--- /dev/null
+++ b/sdk/lib/_internal/wasm/lib/stopwatch_patch.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2022, 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.
+
+// part of "core_patch.dart";
+
+@pragma("wasm:import", "performance.now")
+external double _performanceNow();
+
+@patch
+class Stopwatch {
+  @patch
+  static int _initTicker() {
+    return 1000;
+  }
+
+  @patch
+  static int _now() => _performanceNow().toInt();
+
+  @patch
+  int get elapsedMicroseconds => 1000 * elapsedTicks;
+
+  @patch
+  int get elapsedMilliseconds => elapsedTicks;
+}
diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart
index 301f486..e47e6eb 100644
--- a/sdk/lib/js_util/js_util.dart
+++ b/sdk/lib/js_util/js_util.dart
@@ -150,13 +150,10 @@
 external List<Object?> objectKeys(Object? object);
 
 /// Returns `true` if a given object is a JavaScript array.
-bool isJavaScriptArray(value) => instanceOfString(value, 'Array');
+external bool isJavaScriptArray(value);
 
 /// Returns `true` if a given object is a simple JavaScript object.
-bool isJavaScriptSimpleObject(value) {
-  final Object? proto = objectGetPrototypeOf(value);
-  return proto == null || proto == objectPrototype;
-}
+external bool isJavaScriptSimpleObject(value);
 
 /// Effectively the inverse of [jsify], [dartify] Takes a JavaScript object, and
 /// converts it to a Dart based object. Only JS primitives, arrays, or 'map'
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index 5107be5..1ae9fe5 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -212,6 +212,7 @@
           "_internal/vm/lib/map_patch.dart",
           "_internal/wasm/lib/object_patch.dart",
           "_internal/wasm/lib/stack_trace_patch.dart",
+          "_internal/wasm/lib/stopwatch_patch.dart",
           "_internal/wasm/lib/string_buffer_patch.dart",
           "_internal/wasm/lib/string_patch.dart",
           "_internal/wasm/lib/type.dart"
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index f9f859e..ced8e76 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -202,6 +202,7 @@
       - _internal/vm/lib/map_patch.dart
       - _internal/wasm/lib/object_patch.dart
       - _internal/wasm/lib/stack_trace_patch.dart
+      - _internal/wasm/lib/stopwatch_patch.dart
       - _internal/wasm/lib/string_buffer_patch.dart
       - _internal/wasm/lib/string_patch.dart
       - _internal/wasm/lib/type.dart
diff --git a/tests/lib/js/js_util/dartify_test.dart b/tests/lib/js/js_util/dartify_test.dart
index 9fef877..2d9d08d 100644
--- a/tests/lib/js/js_util/dartify_test.dart
+++ b/tests/lib/js/js_util/dartify_test.dart
@@ -30,7 +30,15 @@
     };
     globalThis.recObjectData = {};
     globalThis.recObjectData = {'foo': globalThis.recObjectData}
-    globalThis.throwData = function() {};
+    globalThis.throwData = new RegExp();
+    globalThis.complexData = {
+      'a': new Date(0),
+      'b': new Promise((resolve, reject) => {}),
+    };
+    globalThis.complexList = [
+      new Date(0),
+      new Promise((resolve, reject) => {}),
+    ];
     """);
 
   test('convert an array', () {
@@ -79,7 +87,22 @@
     Expect.deepEquals(expectedValues, dartObject);
   });
 
-  test('throws if object is not an object literal or array', () {
+  test('complex types convert in an Object', () {
+    Object? jsObject = js_util.getProperty(js_util.globalThis, 'complexData');
+    Map<Object?, Object?> dartObject =
+        js_util.dartify(jsObject) as Map<Object?, Object?>;
+    Expect.isTrue(dartObject['a']! is DateTime);
+    Expect.isTrue(dartObject['b']! is Future);
+  });
+
+  test('complex types convert in a List', () {
+    Object? jsArray = js_util.getProperty(js_util.globalThis, 'complexList');
+    List<Object?> dartList = js_util.dartify(jsArray) as List<Object?>;
+    Expect.isTrue(dartList[0] is DateTime);
+    Expect.isTrue(dartList[1] is Future);
+  });
+
+  test('throws if object is a regexp', () {
     expect(
         () => js_util
             .dartify(js_util.getProperty(js_util.globalThis, 'throwData')),
diff --git a/tests/lib_2/js/js_util/dartify_test.dart b/tests/lib_2/js/js_util/dartify_test.dart
index 9fef877..4019af8 100644
--- a/tests/lib_2/js/js_util/dartify_test.dart
+++ b/tests/lib_2/js/js_util/dartify_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// @dart = 2.9
+
 // Tests the dartify functionality of the js_util library.
 
 @JS()
@@ -30,13 +32,21 @@
     };
     globalThis.recObjectData = {};
     globalThis.recObjectData = {'foo': globalThis.recObjectData}
-    globalThis.throwData = function() {};
+    globalThis.throwData = new RegExp();
+    globalThis.complexData = {
+      'a': new Date(0),
+      'b': new Promise((resolve, reject) => {}),
+    };
+    globalThis.complexList = [
+      new Date(0),
+      new Promise((resolve, reject) => {}),
+    ];
     """);
 
   test('convert an array', () {
-    Object? jsArray = js_util.getProperty(js_util.globalThis, 'arrayData');
-    Object? dartArray = js_util.dartify(jsArray);
-    List<Object?> expectedValues = [
+    Object jsArray = js_util.getProperty(js_util.globalThis, 'arrayData');
+    Object dartArray = js_util.dartify(jsArray);
+    List<Object> expectedValues = [
       1,
       2,
       false,
@@ -50,16 +60,16 @@
   });
 
   test('convert a recursive array', () {
-    Object? jsArray = js_util.getProperty(js_util.globalThis, 'recArrayData');
-    Object? dartArray = js_util.dartify(jsArray);
-    List<Object?> expectedValues = [[]];
+    Object jsArray = js_util.getProperty(js_util.globalThis, 'recArrayData');
+    Object dartArray = js_util.dartify(jsArray);
+    List<Object> expectedValues = [[]];
     Expect.deepEquals(expectedValues, dartArray);
   });
 
   test('convert an object literal', () {
-    Object? jsObject = js_util.getProperty(js_util.globalThis, 'objectData');
-    Object? dartObject = js_util.dartify(jsObject);
-    Map<Object?, Object?> expectedValues = {
+    Object jsObject = js_util.getProperty(js_util.globalThis, 'objectData');
+    Object dartObject = js_util.dartify(jsObject);
+    Map<Object, Object> expectedValues = {
       'a': 1,
       'b': [1, 2, 3],
       'c': {
@@ -71,15 +81,31 @@
   });
 
   test('convert a recursive object literal', () {
-    Object? jsObject = js_util.getProperty(js_util.globalThis, 'recObjectData');
-    Object? dartObject = js_util.dartify(jsObject);
-    Map<Object?, Object?> expectedValues = {
+    Object jsObject = js_util.getProperty(js_util.globalThis, 'recObjectData');
+    Object dartObject = js_util.dartify(jsObject);
+    Map<Object, Object> expectedValues = {
       'foo': {},
     };
     Expect.deepEquals(expectedValues, dartObject);
   });
 
-  test('throws if object is not an object literal or array', () {
+  test('complex types convert in an Object', () {
+    Object jsObject = js_util.getProperty(js_util.globalThis, 'complexData');
+    Map<Object, Object> dartObject =
+        js_util.dartify(jsObject) as Map<Object, Object>;
+    print(dartObject);
+    Expect.isTrue(dartObject['a'] is DateTime);
+    Expect.isTrue(dartObject['b'] is Future);
+  });
+
+  test('complex types convert in a List', () {
+    Object jsArray = js_util.getProperty(js_util.globalThis, 'complexList');
+    List<Object> dartList = js_util.dartify(jsArray) as List<Object>;
+    Expect.isTrue(dartList[0] is DateTime);
+    Expect.isTrue(dartList[1] is Future);
+  });
+
+  test('throws if object is a regexp', () {
     expect(
         () => js_util
             .dartify(js_util.getProperty(js_util.globalThis, 'throwData')),
diff --git a/tools/VERSION b/tools/VERSION
index 6112c90..067fcd7 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 19
 PATCH 0
-PRERELEASE 37
+PRERELEASE 38
 PRERELEASE_PATCH 0
\ No newline at end of file