Version 2.13.0-71.0.dev

Merge commit '7b68624218c2eb84af346790b579947062c9b012' into 'dev'
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 7d4584e..0a38cf0 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -42,7 +42,6 @@
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/services/available_declarations.dart';
-import 'package:analyzer/src/util/glob.dart';
 
 /// Implementations of [AbstractAnalysisServer] implement a server that listens
 /// on a [CommunicationChannel] for analysis messages and process them.
@@ -109,24 +108,12 @@
   /// The set of the files that are currently priority.
   final Set<String> priorityFiles = <String>{};
 
-  final List<String> analyzableFilePatterns = <String>[
-    '**/*.${AnalysisEngine.SUFFIX_DART}',
-    '**/${AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE}',
-    '**/${AnalysisEngine.FIX_DATA_FILE}',
-    '**/${AnalysisEngine.PUBSPEC_YAML_FILE}',
-    '**/${AnalysisEngine.ANDROID_MANIFEST_FILE}'
-  ];
-
   /// The [ResourceProvider] using which paths are converted into [Resource]s.
   final OverlayResourceProvider resourceProvider;
 
   /// The next modification stamp for a changed file in the [resourceProvider].
   int overlayModificationStamp = 0;
 
-  /// A list of the globs used to determine which files should be analyzed. The
-  /// list is lazily created and should be accessed using [analyzedFilesGlobs].
-  List<Glob> _analyzedFilesGlobs;
-
   AbstractAnalysisServer(
     this.options,
     this.sdkManager,
@@ -183,31 +170,11 @@
       byteStore,
       analysisPerformanceLogger,
       analysisDriverScheduler,
-      analyzedFilesGlobs,
       instrumentationService,
     );
     searchEngine = SearchEngineImpl(driverMap.values);
   }
 
-  /// Return a list of the globs used to determine which files should be
-  /// analyzed.
-  List<Glob> get analyzedFilesGlobs {
-    if (_analyzedFilesGlobs == null) {
-      _analyzedFilesGlobs = <Glob>[];
-      for (var pattern in analyzableFilePatterns) {
-        try {
-          _analyzedFilesGlobs
-              .add(Glob(resourceProvider.pathContext.separator, pattern));
-        } catch (exception, stackTrace) {
-          AnalysisEngine.instance.instrumentationService.logException(
-              CaughtException.withMessage(
-                  'Invalid glob pattern: "$pattern"', exception, stackTrace));
-        }
-      }
-    }
-    return _analyzedFilesGlobs;
-  }
-
   /// The list of current analysis sessions in all contexts.
   List<AnalysisSession> get currentSessions {
     return driverMap.values.map((driver) => driver.currentSession).toList();
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 0de59b9..9ff6e06 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -26,7 +26,6 @@
 import 'package:analyzer/src/pubspec/pubspec_validator.dart';
 import 'package:analyzer/src/task/options.dart';
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
-import 'package:analyzer/src/util/glob.dart';
 import 'package:analyzer/src/workspace/bazel.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
 import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
@@ -177,9 +176,6 @@
   @override
   List<String> includedPaths = <String>[];
 
-  /// A list of the globs used to determine which files should be analyzed.
-  final List<Glob> analyzedFilesGlobs;
-
   /// The instrumentation service used to report instrumentation data.
   final InstrumentationService _instrumentationService;
 
@@ -206,7 +202,6 @@
     this._byteStore,
     this._performanceLog,
     this._scheduler,
-    this.analyzedFilesGlobs,
     this._instrumentationService,
   ) {
     pathContext = resourceProvider.pathContext;
@@ -658,19 +653,6 @@
       bazelSubscriptions[folder] = _BazelWorkspaceSubscription(subscription);
     }
   }
-
-  /// Create and return a source representing the given [file] within the given
-  /// [driver].
-  static Source createSourceInContext(AnalysisDriver driver, File file) {
-    // TODO(brianwilkerson) Optimize this, by allowing support for source
-    // factories to restore URI's from a file path rather than a source.
-    var source = file.createSource();
-    if (driver == null) {
-      return source;
-    }
-    var uri = driver.sourceFactory.restoreUri(source);
-    return file.createSource(uri);
-  }
 }
 
 /// A watcher with subscription used to detect changes to some file.
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index d7efddf..46b867d 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -508,13 +508,23 @@
   }
 
   String _getSdkPath(ArgResults args) {
-    if (args[DART_SDK] != null) {
-      return args[DART_SDK];
-    } else if (args[DART_SDK_ALIAS] != null) {
-      return args[DART_SDK_ALIAS];
-    } else {
-      return getSdkPath();
+    String sdkPath;
+
+    void tryCandidateArgument(String argumentName) {
+      var argumentValue = args[argumentName];
+      if (sdkPath == null && argumentValue is String) {
+        sdkPath = argumentValue;
+      }
     }
+
+    tryCandidateArgument(DART_SDK);
+    tryCandidateArgument(DART_SDK_ALIAS);
+    sdkPath ??= getSdkPath();
+
+    var pathContext = PhysicalResourceProvider.INSTANCE.pathContext;
+    return pathContext.normalize(
+      pathContext.absolute(sdkPath),
+    );
   }
 
   /// Print information about how to use the server.
diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart
index ddf271f..6cf3730 100644
--- a/pkg/analyzer/test/generated/resolver_test.dart
+++ b/pkg/analyzer/test/generated/resolver_test.dart
@@ -22,6 +22,7 @@
     defineReflectiveTests(ErrorResolverTest);
     defineReflectiveTests(PrefixedNamespaceTest);
     defineReflectiveTests(StrictModeTest);
+    defineReflectiveTests(StrictModeWithoutNullSafetyTest);
     defineReflectiveTests(TypePropagationTest);
   });
 }
@@ -278,10 +279,60 @@
 /// The class `StrictModeTest` contains tests to ensure that the correct errors
 /// and warnings are reported when the analysis engine is run in strict mode.
 @reflectiveTest
-class StrictModeTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
-  // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
-  //  test cases.
+class StrictModeTest extends PubPackageResolutionTest with StrictModeTestCases {
+  test_conditional_isNot() async {
+    await assertNoErrorsInCode(r'''
+int f(num n) {
+  return (n is! int) ? 0 : n & 0x0F;
+}
+''');
+  }
+
+  test_conditional_or_is() async {
+    await assertNoErrorsInCode(r'''
+int f(num n) {
+  return (n is! int || n < 0) ? 0 : n & 0x0F;
+}
+''');
+  }
+
+  test_if_isNot() async {
+    await assertNoErrorsInCode(r'''
+int f(num n) {
+  if (n is! int) {
+    return 0;
+  } else {
+    return n & 0x0F;
+  }
+}
+''');
+  }
+
+  test_if_isNot_abrupt() async {
+    await assertNoErrorsInCode(r'''
+int f(num n) {
+  if (n is! int) {
+    return 0;
+  }
+  return n & 0x0F;
+}
+''');
+  }
+
+  test_if_or_is() async {
+    await assertNoErrorsInCode(r'''
+int f(num n) {
+  if (n is! int || n < 0) {
+    return 0;
+  } else {
+    return n & 0x0F;
+  }
+}
+''');
+  }
+}
+
+mixin StrictModeTestCases on PubPackageResolutionTest {
   test_assert_is() async {
     await assertErrorsInCode(r'''
 int f(num n) {
@@ -306,48 +357,27 @@
 }''');
   }
 
-  test_conditional_isNot() async {
-    await assertErrorsInCode(r'''
-int f(num n) {
-  return (n is! int) ? 0 : n & 0x0F;
-}''', [
-      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 44, 1),
-    ]);
-  }
-
-  test_conditional_or_is() async {
-    await assertErrorsInCode(r'''
-int f(num n) {
-  return (n is! int || n < 0) ? 0 : n & 0x0F;
-}''', [
-      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 53, 1),
-    ]);
-  }
-
   test_for() async {
-    await assertErrorsInCode(r'''
-int f(List<int> list) {
-  num sum = 0;
-  for (num i = 0; i < list.length; i++) {
+    await assertNoErrorsInCode(r'''
+void f(List<int> list) {
+  num sum = 0; // ignore: unused_local_variable
+  for (int i = 0; i < list.length; i++) {
     sum += list[i];
   }
-}''', [
-      error(HintCode.MISSING_RETURN, 4, 1),
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 30, 3),
-    ]);
+}
+''');
   }
 
   test_forEach() async {
     await assertErrorsInCode(r'''
-int f(List<int> list) {
-  num sum = 0;
+void f(List<int> list) {
+  num sum = 0; // ignore: unused_local_variable
   for (num n in list) {
     sum += n & 0x0F;
   }
-}''', [
-      error(HintCode.MISSING_RETURN, 4, 1),
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 30, 3),
-      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 76, 1),
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 110, 1),
     ]);
   }
 
@@ -371,6 +401,42 @@
 }''');
   }
 
+  test_localVar() async {
+    await assertErrorsInCode(r'''
+int f() {
+  num n = 1234;
+  return n & 0x0F;
+}''', [
+      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 37, 1),
+    ]);
+  }
+}
+
+/// The class `StrictModeTest` contains tests to ensure that the correct errors
+/// and warnings are reported when the analysis engine is run in strict mode.
+@reflectiveTest
+class StrictModeWithoutNullSafetyTest extends PubPackageResolutionTest
+    with StrictModeTestCases, WithoutNullSafetyMixin {
+  test_conditional_isNot() async {
+    await assertErrorsInCode(r'''
+int f(num n) {
+  return (n is! int) ? 0 : n & 0x0F;
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 44, 1),
+    ]);
+  }
+
+  test_conditional_or_is() async {
+    await assertErrorsInCode(r'''
+int f(num n) {
+  return (n is! int || n < 0) ? 0 : n & 0x0F;
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 53, 1),
+    ]);
+  }
+
   test_if_isNot() async {
     await assertErrorsInCode(r'''
 int f(num n) {
@@ -379,7 +445,8 @@
   } else {
     return n & 0x0F;
   }
-}''', [
+}
+''', [
       error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 72, 1),
     ]);
   }
@@ -391,7 +458,8 @@
     return 0;
   }
   return n & 0x0F;
-}''', [
+}
+''', [
       error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 63, 1),
     ]);
   }
@@ -404,27 +472,15 @@
   } else {
     return n & 0x0F;
   }
-}''', [
+}
+''', [
       error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 81, 1),
     ]);
   }
-
-  test_localVar() async {
-    await assertErrorsInCode(r'''
-int f() {
-  num n = 1234;
-  return n & 0x0F;
-}''', [
-      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 37, 1),
-    ]);
-  }
 }
 
 @reflectiveTest
-class TypePropagationTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
-  // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
-  //  test cases.
+class TypePropagationTest extends PubPackageResolutionTest {
   test_assignment_null() async {
     String code = r'''
 main() {
@@ -515,22 +571,23 @@
 }
 
 class C {
-  void f() {
-    Base x = null;
+  void f(Base x) {
+    x = Base();
     if (x is Derived) {
       print(x.y); // BAD
     }
-    x = null;
+    x = Base();
   }
 }
 
-void g() {
-  Base x = null;
+void g(Base x) {
+  x = Base();
   if (x is Derived) {
     print(x.y); // GOOD
   }
-  x = null;
-}''');
+  x = Base();
+}
+''');
   }
 
   test_objectAccessInference_disabled_for_library_prefix() async {
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index e9319da..60d4e16 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -15,9 +15,6 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(MethodInvocationResolutionTest);
     defineReflectiveTests(MethodInvocationResolutionWithNullSafetyTest);
-    defineReflectiveTests(
-      MethodInvocationResolutionWithNonFunctionTypeAliasesTest,
-    );
   });
 }
 
@@ -2447,66 +2444,9 @@
   }
 }
 
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
-@reflectiveTest
-class MethodInvocationResolutionWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest {
-  test_hasReceiver_typeAlias_staticMethod() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  static void foo(int _) {}
-}
-
-typedef B = A;
-
-void f() {
-  B.foo(0);
-}
-''');
-
-    assertMethodInvocation(
-      findNode.methodInvocation('foo(0)'),
-      findElement.method('foo'),
-      'void Function(int)',
-    );
-
-    assertTypeAliasRef(
-      findNode.simple('B.foo'),
-      findElement.typeAlias('B'),
-    );
-  }
-
-  test_hasReceiver_typeAlias_staticMethod_generic() async {
-    await assertNoErrorsInCode(r'''
-class A<T> {
-  static void foo(int _) {}
-}
-
-typedef B<T> = A<T>;
-
-void f() {
-  B.foo(0);
-}
-''');
-
-    assertMethodInvocation(
-      findNode.methodInvocation('foo(0)'),
-      findElement.method('foo'),
-      'void Function(int)',
-    );
-
-    assertTypeAliasRef(
-      findNode.simple('B.foo'),
-      findElement.typeAlias('B'),
-    );
-  }
-}
-
 @reflectiveTest
 class MethodInvocationResolutionWithNullSafetyTest
-    extends PubPackageResolutionTest
-    with WithNullSafetyMixin, MethodInvocationResolutionTestCases {
+    extends PubPackageResolutionTest with MethodInvocationResolutionTestCases {
   test_hasReceiver_deferredImportPrefix_loadLibrary_optIn_fromOptOut() async {
     newFile('$testPackageLibPath/a.dart', content: r'''
 class A {}
@@ -2764,6 +2704,56 @@
     );
   }
 
+  test_hasReceiver_typeAlias_staticMethod() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  static void foo(int _) {}
+}
+
+typedef B = A;
+
+void f() {
+  B.foo(0);
+}
+''');
+
+    assertMethodInvocation(
+      findNode.methodInvocation('foo(0)'),
+      findElement.method('foo'),
+      'void Function(int)',
+    );
+
+    assertTypeAliasRef(
+      findNode.simple('B.foo'),
+      findElement.typeAlias('B'),
+    );
+  }
+
+  test_hasReceiver_typeAlias_staticMethod_generic() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {
+  static void foo(int _) {}
+}
+
+typedef B<T> = A<T>;
+
+void f() {
+  B.foo(0);
+}
+''');
+
+    assertMethodInvocation(
+      findNode.methodInvocation('foo(0)'),
+      findElement.method('foo'),
+      'void Function(int)',
+    );
+
+    assertTypeAliasRef(
+      findNode.simple('B.foo'),
+      findElement.typeAlias('B'),
+    );
+  }
+
   test_hasReceiver_typeParameter_promotedToNonNullable() async {
     await assertNoErrorsInCode('''
 void f<T>(T? t) {
diff --git a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
index 4776e89..17d8a68 100644
--- a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
@@ -10,9 +10,6 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConstInitializedWithNonConstantValueTest);
-    defineReflectiveTests(
-      ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest,
-    );
   });
 }
 
@@ -105,13 +102,7 @@
           1),
     ]);
   }
-}
 
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one above it.
-@reflectiveTest
-class ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest {
   test_typeLiteral_interfaceType() async {
     await assertNoErrorsInCode(r'''
 const a = int;
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
index da6d102..af8e6e3 100644
--- a/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
@@ -13,8 +13,6 @@
   });
 }
 
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one above it.
 @reflectiveTest
 class NullableTypeInOnClauseTest extends PubPackageResolutionTest {
   test_nonNullable() async {
diff --git a/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart b/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
index 831cfaa..68ba41b 100644
--- a/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
@@ -10,9 +10,6 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(TypeAliasCannotReferenceItselfTest);
-    defineReflectiveTests(
-      TypeAliasCannotReferenceItselfWithNonFunctionTypeAliasesTest,
-    );
   });
 }
 
@@ -91,6 +88,56 @@
 ''');
   }
 
+  test_nonFunction_aliasedType_cycleOf2() async {
+    await assertErrorsInCode('''
+typedef T1 = T2;
+typedef T2 = T1;
+''', [
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 16),
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 17, 16),
+    ]);
+  }
+
+  test_nonFunction_aliasedType_directly_functionWithIt() async {
+    await assertErrorsInCode('''
+typedef T = void Function(T);
+''', [
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 29),
+    ]);
+  }
+
+  test_nonFunction_aliasedType_directly_it_none() async {
+    await assertErrorsInCode('''
+typedef T = T;
+''', [
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 14),
+    ]);
+  }
+
+  test_nonFunction_aliasedType_directly_it_question() async {
+    await assertErrorsInCode('''
+typedef T = T?;
+''', [
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 15),
+    ]);
+  }
+
+  test_nonFunction_aliasedType_directly_ListOfIt() async {
+    await assertErrorsInCode('''
+typedef T = List<T>;
+''', [
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 20),
+    ]);
+  }
+
+  test_nonFunction_typeParameterBounds() async {
+    await assertErrorsInCode('''
+typedef T<X extends T<Never>> = List<X>;
+''', [
+      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 40),
+    ]);
+  }
+
   test_parameterType_named() async {
     await assertErrorsInCode('''
 typedef A({A a});
@@ -161,59 +208,3 @@
     ]);
   }
 }
-
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
-@reflectiveTest
-class TypeAliasCannotReferenceItselfWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest {
-  test_nonFunction_aliasedType_cycleOf2() async {
-    await assertErrorsInCode('''
-typedef T1 = T2;
-typedef T2 = T1;
-''', [
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 16),
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 17, 16),
-    ]);
-  }
-
-  test_nonFunction_aliasedType_directly_functionWithIt() async {
-    await assertErrorsInCode('''
-typedef T = void Function(T);
-''', [
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 29),
-    ]);
-  }
-
-  test_nonFunction_aliasedType_directly_it_none() async {
-    await assertErrorsInCode('''
-typedef T = T;
-''', [
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 14),
-    ]);
-  }
-
-  test_nonFunction_aliasedType_directly_it_question() async {
-    await assertErrorsInCode('''
-typedef T = T?;
-''', [
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 15),
-    ]);
-  }
-
-  test_nonFunction_aliasedType_directly_ListOfIt() async {
-    await assertErrorsInCode('''
-typedef T = List<T>;
-''', [
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 20),
-    ]);
-  }
-
-  test_nonFunction_typeParameterBounds() async {
-    await assertErrorsInCode('''
-typedef T<X extends T<Never>> = List<X>;
-''', [
-      error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 40),
-    ]);
-  }
-}
diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
index b33e321..b9850fb 100644
--- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
@@ -13,9 +13,6 @@
     defineReflectiveTests(
       TypeArgumentNotMatchingBoundsWithNullSafetyTest,
     );
-    defineReflectiveTests(
-      TypeArgumentNotMatchingBoundsWithNonFunctionTypeAliasesTest,
-    );
   });
 }
 
@@ -418,44 +415,10 @@
   }
 }
 
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
-@reflectiveTest
-class TypeArgumentNotMatchingBoundsWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest
-    with TypeArgumentNotMatchingBoundsTestCases {
-  test_nonFunctionTypeAlias_interfaceType_parameter() async {
-    await assertErrorsInCode(r'''
-class A {}
-typedef X<T extends A> = Map<int, T>;
-void f(X<String> a) {}
-''', [
-      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6),
-    ]);
-  }
-
-  test_nonFunctionTypeAlias_interfaceType_parameter_regularBounded() async {
-    await assertNoErrorsInCode(r'''
-class A {}
-class B extends A {}
-typedef X<T extends A> = Map<int, T>;
-void f(X<B> a) {}
-''');
-  }
-
-  test_nonFunctionTypeAlias_interfaceType_parameter_superBounded() async {
-    await assertNoErrorsInCode(r'''
-class A {}
-typedef X<T extends A> = Map<int, T>;
-void f(X<Never> a) {}
-''');
-  }
-}
-
 @reflectiveTest
 class TypeArgumentNotMatchingBoundsWithNullSafetyTest
     extends PubPackageResolutionTest
-    with TypeArgumentNotMatchingBoundsTestCases, WithNullSafetyMixin {
+    with TypeArgumentNotMatchingBoundsTestCases {
   test_extends_optIn_fromOptOut_Null() async {
     newFile('$testPackageLibPath/a.dart', content: r'''
 class A<X extends int> {}
@@ -488,6 +451,33 @@
 ''');
   }
 
+  test_nonFunctionTypeAlias_interfaceType_parameter() async {
+    await assertErrorsInCode(r'''
+class A {}
+typedef X<T extends A> = Map<int, T>;
+void f(X<String> a) {}
+''', [
+      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6),
+    ]);
+  }
+
+  test_nonFunctionTypeAlias_interfaceType_parameter_regularBounded() async {
+    await assertNoErrorsInCode(r'''
+class A {}
+class B extends A {}
+typedef X<T extends A> = Map<int, T>;
+void f(X<B> a) {}
+''');
+  }
+
+  test_nonFunctionTypeAlias_interfaceType_parameter_superBounded() async {
+    await assertNoErrorsInCode(r'''
+class A {}
+typedef X<T extends A> = Map<int, T>;
+void f(X<Never> a) {}
+''');
+  }
+
   test_notRegularBounded_notSuperBounded_invariant() async {
     await assertErrorsInCode(r'''
 typedef A<X> = X Function(X);
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index de7dc23..c3ca7ec 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -11,7 +11,6 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(UnusedElementTest);
     defineReflectiveTests(UnusedElementWithNullSafetyTest);
-    defineReflectiveTests(UnusedElementWithNonFunctionTypeAliasesTest);
   });
 }
 
@@ -1578,11 +1577,20 @@
   }
 }
 
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
 @reflectiveTest
-class UnusedElementWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest {
+class UnusedElementWithNullSafetyTest extends PubPackageResolutionTest {
+  test_optionalParameter_isUsed_overrideRequiredNamed() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  void _m({required int a}) {}
+}
+class B implements A {
+  void _m({int a = 0}) {}
+}
+f() => A()._m(a: 0);
+''');
+  }
+
   test_typeAlias_interfaceType_isUsed_typeName_isExpression() async {
     await assertNoErrorsInCode(r'''
 typedef _A = List<int>;
@@ -1619,19 +1627,3 @@
     ]);
   }
 }
-
-@reflectiveTest
-class UnusedElementWithNullSafetyTest extends PubPackageResolutionTest
-    with WithNullSafetyMixin {
-  test_optionalParameter_isUsed_overrideRequiredNamed() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  void _m({required int a}) {}
-}
-class B implements A {
-  void _m({int a = 0}) {}
-}
-f() => A()._m(a: 0);
-''');
-  }
-}
diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml
index 155491d..2a4517c 100644
--- a/pkg/dev_compiler/pubspec.yaml
+++ b/pkg/dev_compiler/pubspec.yaml
@@ -28,8 +28,6 @@
     path: ../vm
 
 dev_dependencies:
-  analyzer:
-    path: ../analyzer
   expect:
     path: ../expect
   js:
@@ -38,7 +36,6 @@
     path: ../modular_test
   package_config: any
   pedantic: ^1.8.0
-  pub_semver: any
   sourcemap_testing:
     path: ../sourcemap_testing
   stack_trace: any
diff --git a/pkg/dev_compiler/tool/check_nnbd_sdk.dart b/pkg/dev_compiler/tool/check_nnbd_sdk.dart
deleted file mode 100644
index e4b9028..0000000
--- a/pkg/dev_compiler/tool/check_nnbd_sdk.dart
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2019, 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.
-
-// @dart = 2.9
-
-/// Command-line tool that runs dartanalyzer on a sdk under the perspective of
-/// one tool.
-// TODO(sigmund): generalize this to support other tools, not just ddc.
-
-import 'dart:io';
-import 'package:args/args.dart';
-import 'package:front_end/src/fasta/resolve_input_uri.dart';
-
-import 'patch_sdk.dart' as patch;
-
-void main(List<String> argv) {
-  if (Platform.isWindows) {
-    print('Golden file does not support Windows.  Skipping.');
-    return;
-  }
-  var args = _parser.parse(argv);
-  if (args['help'] as bool) {
-    print('Apply patch file to the SDK and report analysis errors from the '
-        'resulting libraries.\n\n'
-        'Usage: ${Platform.script.pathSegments.last} [options...]\n\n'
-        '${_parser.usage}');
-    return;
-  }
-  var baseDir = args['out'] as String;
-  if (baseDir == null) {
-    var tmp = Directory.systemTemp.createTempSync('check_sdk-');
-    baseDir = tmp.path;
-  }
-  var baseUri = resolveInputUri(baseDir.endsWith('/') ? baseDir : '$baseDir/');
-  var sdkDir = baseUri.resolve('sdk/').toFilePath();
-  print('Generating a patched sdk at ${baseUri.path}');
-
-  var librariesJson = args['libraries'] != null
-      ? resolveInputUri(args['libraries'] as String)
-      : Platform.script.resolve('../../../sdk/lib/libraries.json');
-  var target = args['target'] as String;
-  patch.main([
-    '--libraries',
-    librariesJson.toFilePath(),
-    '--target',
-    target,
-    '--out',
-    sdkDir,
-    '--merge-parts',
-    '--nnbd',
-  ]);
-
-  var isWeb = false;
-  var isNative = false;
-  switch (target) {
-    case 'dartdevc':
-    case 'dart2js':
-      isWeb = true;
-      break;
-    case 'flutter':
-    case 'vm':
-      isNative = true;
-      break;
-  }
-
-  var core = '''
-import 'dart:async';
-import 'dart:collection';
-import 'dart:convert';
-import 'dart:core';
-import 'dart:developer';
-import 'dart:math';
-import 'dart:typed_data';
-''';
-
-  var web = !isWeb
-      ? ''
-      : '''
-import 'dart:js';
-import 'dart:js_util';
-import 'dart:indexed_db';
-import 'dart:html';
-import 'dart:html_common';
-import 'dart:svg';
-import 'dart:web_audio';
-import 'dart:web_gl';
-import 'dart:web_sql';
-''';
-
-  var native = !isNative
-      ? '''
-import 'dart:io';
-import 'dart:isolate';
-'''
-      : '''
-import 'dart:ffi';
-import 'dart:io';
-import 'dart:isolate';
-import 'dart:mirrors';
-''';
-
-  var emptyProgramUri = baseUri.resolve('empty_program.dart');
-  File.fromUri(emptyProgramUri).writeAsStringSync('''
-$core
-$web
-$native
-
-main() {}
-''');
-
-  print('Running dartanalyzer');
-  var dart = resolveInputUri(Platform.resolvedExecutable);
-  var analyzerSnapshot =
-      dart.resolve('snapshots/dartanalyzer.dart.snapshot').toFilePath();
-  var result = Process.runSync(dart.toFilePath(), [
-    // The NNBD dart binaries / snapshots require this flag to be enabled at
-    // VM level.
-    analyzerSnapshot,
-    '--dart-sdk=${sdkDir}',
-    '--format',
-    'machine',
-    '--sdk-warnings',
-    '--no-hints',
-    emptyProgramUri.toFilePath()
-  ]);
-
-  stdout.write(result.stdout);
-  var errors = result.stderr as String;
-
-  // Trim temporary directory paths and sort errors.
-  errors = errors.replaceAll(sdkDir, '');
-  if (!(args['keep-lines'] as bool)) {
-    // Golden files change frequenty if line numbers are recorded.
-    // We remove them by default but provide an option to show them if
-    // they can be helpful.
-    errors = errors.replaceAll(RegExp(r'\|[0-9]*\|[0-9]*\|[0-9]*\|'), '|');
-  }
-  var errorList = errors.isEmpty ? <String>[] : errors.trim().split('\n');
-  var count = errorList.length;
-  print('$count analyzer errors.');
-  errorList.sort();
-  errors = errorList.join('\n') + '\n';
-  var errorFile = baseUri.resolve('errors.txt');
-  print('Errors emitted to ${errorFile.path}');
-  File.fromUri(errorFile).writeAsStringSync(errors, flush: true);
-
-  // Check against golden file.
-  var goldenFile =
-      Platform.script.resolve('${target}_nnbd_sdk_error_golden.txt');
-  var golden = File.fromUri(goldenFile).readAsStringSync();
-  if (errors != golden) {
-    if (args['update-golden'] as bool) {
-      // Update the golden file.
-      File.fromUri(goldenFile).writeAsStringSync(errors, flush: true);
-      print('Golden file updated.');
-      exit(0);
-    } else {
-      // Fail.
-      print('Golden file does not match.');
-      print('\nTo update the golden file, run:'
-          '\n  ${Platform.executable} ${Platform.script} '
-          '${argv.join(' ')} --update-golden');
-
-      // Compare the two sorted lists to show what errors changed.  Note, we
-      // don't use `diff` as an external tool because it is not available on
-      // windows bots.
-      var toAdd = <String>[];
-      var toRemove = <String>[];
-      var goldenList = golden.trim().split('\n');
-      var i = 0, j = 0;
-      for (; i < errorList.length && j < goldenList.length;) {
-        var compare = errorList[i].compareTo(goldenList[j]);
-        if (compare == 0) {
-          i++;
-          j++;
-        } else if (compare < 0) {
-          toAdd.add(errorList[i]);
-          i++;
-        } else {
-          toRemove.add(goldenList[j]);
-          j++;
-        }
-      }
-      for (; i < errorList.length; i++) {
-        toAdd.add(errorList[i]);
-      }
-      for (; j < goldenList.length; j++) {
-        toRemove.add(goldenList[j]);
-      }
-      print('\nNew errors:');
-      print(toAdd.join('\n'));
-      print('\nErrors that can be removed from the golden file:');
-      print(toRemove.join('\n'));
-      exit(1);
-    }
-  }
-  exit(0);
-}
-
-final _parser = ArgParser()
-  ..addOption('libraries',
-      help: 'Path to the nnbd libraries.json (defaults to the one under '
-          'sdk/lib/libraries.json.')
-  ..addOption('out',
-      help: 'Path to an output folder (defaults to a new tmp folder).')
-  ..addOption('target',
-      help: 'The target tool. '
-          'This name matches one of the possible targets in libraries.json '
-          'and it is used to pick which patch files will be applied.',
-      allowed: ['dartdevc', 'dart2js', 'dart2js_server', 'vm', 'flutter'],
-      defaultsTo: 'dartdevc')
-  ..addFlag('update-golden', help: 'Update the golden file.', defaultsTo: false)
-  ..addFlag('keep-lines',
-      help: 'Show line numbers on errors.', defaultsTo: false)
-  ..addFlag('help', abbr: 'h', help: 'Display this message.');
diff --git a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
deleted file mode 100644
index 98ddc8c..0000000
--- a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '&': JSNumber.& (num Function(num)), int.& (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '<<': JSNumber.<< (num Function(num)), int.<< (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '>>': JSNumber.>> (num Function(num)), int.>> (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '>>>': JSNumber.>>> (num Function(num)), int.>>> (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '\|': JSNumber.\| (num Function(num)), int.\| (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '^': JSNumber.^ (num Function(num)), int.^ (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
diff --git a/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt
deleted file mode 100644
index 3adfa76..0000000
--- a/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
diff --git a/pkg/dev_compiler/tool/kernel_sdk.dart b/pkg/dev_compiler/tool/kernel_sdk.dart
deleted file mode 100755
index 64ace35..0000000
--- a/pkg/dev_compiler/tool/kernel_sdk.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2017, 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.
-
-// @dart = 2.9
-
-import 'dart:async';
-import 'dart:convert' show json;
-import 'dart:io';
-import 'package:args/args.dart' show ArgParser;
-import 'package:build_integration/file_system/multi_root.dart';
-import 'package:dev_compiler/src/compiler/module_builder.dart';
-import 'package:dev_compiler/src/compiler/shared_command.dart'
-    show SharedCompilerOptions;
-import 'package:dev_compiler/src/kernel/target.dart';
-import 'package:dev_compiler/src/kernel/command.dart';
-import 'package:dev_compiler/src/kernel/compiler.dart';
-import 'package:front_end/src/api_unstable/ddc.dart'
-    show
-        CompilerOptions,
-        DiagnosticMessage,
-        Severity,
-        StandardFileSystem,
-        kernelForModule,
-        printDiagnosticMessage;
-import 'package:kernel/kernel.dart';
-import 'package:kernel/target/targets.dart';
-import 'package:path/path.dart' as p;
-
-Future main(List<String> args) async {
-  var ddcPath = p.dirname(p.dirname(p.fromUri(Platform.script)));
-
-  // Parse flags.
-  var parser = ArgParser()
-    ..addOption('output')
-    ..addOption('libraries',
-        defaultsTo: p.join(ddcPath, '../../sdk/lib/libraries.json'))
-    ..addOption('packages', defaultsTo: p.join(ddcPath, '../../.packages'));
-  var parserOptions = parser.parse(args);
-
-  var outputPath = parserOptions['output'] as String;
-  if (outputPath == null) {
-    var sdkRoot = p.absolute(p.dirname(p.dirname(ddcPath)));
-    var buildDir = p.join(sdkRoot, Platform.isMacOS ? 'xcodebuild' : 'out');
-    var genDir = p.join(buildDir, 'ReleaseX64', 'gen', 'utils', 'dartdevc');
-    outputPath = p.join(genDir, 'kernel', 'ddc_sdk.dill');
-  }
-
-  var librarySpecPath = parserOptions['libraries'] as String;
-  var packagesPath = parserOptions['packages'] as String;
-
-  var target = DevCompilerTarget(TargetFlags());
-  void onDiagnostic(DiagnosticMessage message) {
-    printDiagnosticMessage(message, print);
-    if (message.severity == Severity.error ||
-        message.severity == Severity.internalProblem) {
-      exitCode = 1;
-    }
-  }
-
-  var customScheme = 'org-dartlang-sdk';
-  var fileSystem = MultiRootFileSystem(
-      customScheme, [Uri.base], StandardFileSystem.instance);
-  var sdkRoot = Uri.parse('$customScheme:/');
-  var packagesFileUri = sdkRoot
-      .resolve(p.relative(Uri.file(packagesPath).path, from: Uri.base.path));
-  if (packagesFileUri.scheme != customScheme) {
-    throw 'packagesPath has to be under ${Uri.base}';
-  }
-  var librariesSpecificationUri = sdkRoot
-      .resolve(p.relative(Uri.file(librarySpecPath).path, from: Uri.base.path));
-  if (librariesSpecificationUri.scheme != customScheme) {
-    throw 'librarySpecPath has to be under ${Uri.base}';
-  }
-
-  var options = CompilerOptions()
-    ..compileSdk = true
-    ..fileSystem = fileSystem
-    ..sdkRoot = sdkRoot
-    ..packagesFileUri = packagesFileUri
-    ..librariesSpecificationUri = librariesSpecificationUri
-    ..target = target
-    ..onDiagnostic = onDiagnostic
-    ..environmentDefines = {};
-
-  var inputs = target.extraRequiredLibraries.map(Uri.parse).toList();
-
-  var compilerResult = await kernelForModule(inputs, options);
-  var component = compilerResult.component;
-
-  var outputDir = p.dirname(outputPath);
-  await Directory(outputDir).create(recursive: true);
-  await writeComponentToBinary(component, outputPath);
-  File(librarySpecPath)
-      .copySync(p.join(p.dirname(outputDir), p.basename(librarySpecPath)));
-
-  var jsModule = ProgramCompiler(
-      component,
-      compilerResult.classHierarchy,
-      SharedCompilerOptions(moduleName: 'dart_sdk'),
-      const {},
-      const {}).emitModule(component);
-  var moduleFormats = {
-    'amd': ModuleFormat.amd,
-    'common': ModuleFormat.common,
-    'es6': ModuleFormat.es6,
-    'legacy': ModuleFormat.ddc,
-  };
-
-  for (var name in moduleFormats.keys) {
-    var format = moduleFormats[name];
-    var jsDir = p.join(outputDir, name);
-    var jsPath = p.join(jsDir, 'dart_sdk.js');
-    var mapPath = '$jsPath.map';
-    await Directory(jsDir).create();
-    var jsCode = jsProgramToCode(jsModule, format,
-        jsUrl: jsPath,
-        mapUrl: mapPath,
-        buildSourceMap: true,
-        customScheme: customScheme,
-        component: component);
-    await File(jsPath).writeAsString(jsCode.code);
-    await File(mapPath).writeAsString(json.encode(jsCode.sourceMap));
-  }
-}
diff --git a/pkg/dev_compiler/tool/patch_sdk.dart b/pkg/dev_compiler/tool/patch_sdk.dart
deleted file mode 100755
index f1cd943..0000000
--- a/pkg/dev_compiler/tool/patch_sdk.dart
+++ /dev/null
@@ -1,619 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2015, 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.
-
-// @dart = 2.9
-
-/// Command line tool to merge the SDK libraries and our patch files.
-/// This is currently designed as an offline tool, but we could automate it.
-
-import 'dart:io';
-
-import 'package:_fe_analyzer_shared/src/util/relativize.dart';
-import 'package:analyzer/dart/analysis/features.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/utilities.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:args/args.dart';
-import 'package:front_end/src/base/libraries_specification.dart';
-import 'package:front_end/src/fasta/resolve_input_uri.dart';
-import 'package:pub_semver/pub_semver.dart';
-
-void main(List<String> argv) {
-  var args = _parser.parse(argv);
-  if (args['libraries'] == null || args['out'] == null) {
-    var self = relativizeUri(Uri.base, Platform.script, isWindows);
-    var librariesJson = relativizeUri(Uri.base,
-        Platform.script.resolve('../../../sdk/lib/libraries.json'), isWindows);
-    print('Usage: $self [other options]'
-        ' --libraries <libraries.json> --out <output-dir>');
-    print('For example:');
-    print('\$ $self --nnbd --libraries $librariesJson --out patched-sdk-dir');
-    exit(1);
-  }
-
-  var useNnbd = args['nnbd'] as bool;
-  var target = args['target'] as String;
-  var jsonUri = resolveInputUri(args['libraries'] as String);
-  var libRoot = jsonUri.resolve('./');
-  var outPath = args['out'] as String;
-  var outDir = resolveInputUri(outPath.endsWith('/') ? outPath : '$outPath/');
-  var outLibRoot = outDir.resolve('lib/');
-
-  var inputVersion = Uri.file(Platform.executable).resolve('../version');
-  var outVersion = outDir.resolve('version');
-
-  var specification = LibrariesSpecification.parse(
-          jsonUri, File.fromUri(jsonUri).readAsStringSync())
-      .specificationFor(target);
-
-  // Copy libraries.dart and version
-  _writeSync(outVersion, File.fromUri(inputVersion).readAsStringSync());
-
-  // Enumerate sdk libraries and apply patches
-  for (var library in specification.allLibraries) {
-    var libraryFile = File.fromUri(library.uri);
-    var libraryOut =
-        outLibRoot.resolve(relativizeLibraryUri(libRoot, library.uri, useNnbd));
-    if (libraryFile.existsSync()) {
-      var outUris = <Uri>[libraryOut];
-      var libraryContents = libraryFile.readAsStringSync();
-      var contents = <String>[libraryContents];
-
-      for (var part
-          in _parseString(libraryContents, useNnbd: useNnbd).unit.directives) {
-        if (part is PartDirective) {
-          var partPath = part.uri.stringValue;
-          outUris.add(libraryOut.resolve(partPath));
-          contents.add(
-              File.fromUri(library.uri.resolve(partPath)).readAsStringSync());
-        }
-      }
-
-      if (args['merge-parts'] as bool && outUris.length > 1) {
-        outUris.length = 1;
-        contents = [
-          contents
-              .join('\n')
-              .replaceAll(RegExp('^part [^\n]*\$', multiLine: true), '')
-        ];
-      }
-
-      var buffer = StringBuffer();
-      for (var patchUri in library.patches) {
-        // Note: VM targets enumerate more than one patch file, they are
-        // currently written so that the first file is a valid patch file and
-        // all other files can be appended at the end.
-        buffer.write(File.fromUri(patchUri).readAsStringSync());
-      }
-      var patchContents = '$buffer';
-
-      if (patchContents.isNotEmpty) {
-        contents = _patchLibrary(contents, patchContents, useNnbd: useNnbd);
-      }
-
-      if (contents != null) {
-        for (var i = 0; i < outUris.length; i++) {
-          _writeSync(outUris[i], contents[i]);
-        }
-      } else {
-        exitCode = 2;
-      }
-    }
-  }
-
-  var outLibrariesDart =
-      outLibRoot.resolve('_internal/sdk_library_metadata/lib/libraries.dart');
-  _writeSync(outLibrariesDart,
-      _generateLibrariesDart(libRoot, specification, useNnbd));
-
-  var experimentsPath = '_internal/allowed_experiments.json';
-  _writeSync(
-    outLibRoot.resolve(experimentsPath),
-    File.fromUri(libRoot.resolve(experimentsPath)).readAsStringSync(),
-  );
-}
-
-/// Writes a file, creating the directory if needed.
-void _writeSync(Uri fileUri, String contents) {
-  var outDir = Directory.fromUri(fileUri.resolve('.'));
-  if (!outDir.existsSync()) outDir.createSync(recursive: true);
-
-  File.fromUri(fileUri).writeAsStringSync(contents);
-}
-
-final _parser = ArgParser()
-  ..addFlag('nnbd',
-      help: 'Whether to enable the nnbd feature.', defaultsTo: false)
-  ..addFlag('merge-parts',
-      help: 'Whether to merge part files. '
-          'Technically this is not necessary, but dartanalyzer '
-          'produces less warnings when enabling this flag.',
-      defaultsTo: false)
-  ..addOption('libraries',
-      help: 'Path to a libraries.json specification file (required). '
-          'All libary URIs within libraries.json are expected to be somewhere '
-          'under the directory containing the libraries.json file. Reaching '
-          'out above such directory is generally not supported. Today it is '
-          'only allowed for the nnbd sdk to reuse libraries of the non-nnbd '
-          'sdk, in which case the path starts with "../../sdk/lib/".')
-  ..addOption('out', help: 'Path to an output folder (required).')
-  ..addOption('target',
-      help: 'The target tool. '
-          'This name matches one of the possible targets in libraries.json '
-          'and it is used to pick which patch files will be applied.',
-      allowed: ['dartdevc', 'dart2js', 'dart2js_server', 'vm', 'flutter']);
-
-/// Merges dart:* library code with code from *_patch.dart file.
-///
-/// Takes a list of the library's parts contents, with the main library contents
-/// first in the list, and the contents of the patch file.
-///
-/// The result will have `@patch` implementations merged into the correct place
-/// (e.g. the class or top-level function declaration) and all other
-/// declarations introduced by the patch will be placed into the main library
-/// file.
-///
-/// This is purely a syntactic transformation. Unlike dart2js patch files, there
-/// is no semantic meaning given to the *_patch files, and they do not magically
-/// get their own library scope, etc.
-///
-/// Editorializing: the dart2js approach requires a Dart front end such as
-/// package:analyzer to semantically model a feature beyond what is specified
-/// in the Dart language. Since this feature is only for the convenience of
-/// writing the dart:* libraries, and not a tool given to Dart developers, it
-/// seems like a non-ideal situation. Instead we keep the preprocessing simple.
-List<String> _patchLibrary(List<String> partsContents, String patchContents,
-    {bool useNnbd = false}) {
-  var results = <StringEditBuffer>[];
-
-  // Parse the patch first. We'll need to extract bits of this as we go through
-  // the other files.
-  var patchFinder = PatchFinder.parseAndVisit(patchContents, useNnbd: useNnbd);
-
-  // Merge `external` declarations with the corresponding `@patch` code.
-  var failed = false;
-  for (var partContent in partsContents) {
-    var partEdits = StringEditBuffer(partContent);
-    var partUnit = _parseString(partContent, useNnbd: useNnbd).unit;
-    var patcher = PatchApplier(partEdits, patchFinder);
-    partUnit.accept(patcher);
-    if (!failed) failed = patcher.patchWasMissing;
-    results.add(partEdits);
-  }
-  if (failed) return null;
-  return List<String>.from(results.map((e) => e.toString()));
-}
-
-/// Merge `@patch` declarations into `external` declarations.
-class PatchApplier extends GeneralizingAstVisitor<void> {
-  final StringEditBuffer edits;
-  final PatchFinder patch;
-
-  bool _isLibrary = true; // until proven otherwise.
-  bool patchWasMissing = false;
-
-  PatchApplier(this.edits, this.patch);
-
-  @override
-  void visitCompilationUnit(CompilationUnit node) {
-    super.visitCompilationUnit(node);
-    if (_isLibrary) _mergeUnpatched(node);
-  }
-
-  void _merge(AstNode node, int pos) {
-    var code = patch.contents.substring(node.offset, node.end);
-    edits.insert(pos, '\n' + code);
-  }
-
-  /// Merges directives and declarations that are not `@patch` into the library.
-  void _mergeUnpatched(CompilationUnit unit) {
-    // Merge imports from the patch
-    // TODO(jmesserly): remove duplicate imports
-
-    // To patch a library, we must have a library directive
-    var libDir = unit.directives.first as LibraryDirective;
-    var importPos = unit.directives
-        .lastWhere((d) => d is ImportDirective, orElse: () => libDir)
-        .end;
-    for (var d in patch.unit.directives.whereType<ImportDirective>()) {
-      _merge(d, importPos);
-    }
-
-    var partPos = unit.directives.last.end;
-    for (var d in patch.unit.directives.whereType<PartDirective>()) {
-      _merge(d, partPos);
-    }
-
-    // Merge declarations from the patch
-    var declPos = edits.original.length;
-    for (var d in patch.mergeDeclarations) {
-      _merge(d, declPos);
-    }
-  }
-
-  @override
-  void visitPartOfDirective(PartOfDirective node) {
-    _isLibrary = false;
-  }
-
-  @override
-  void visitFunctionDeclaration(FunctionDeclaration node) {
-    _maybePatch(node);
-  }
-
-  /// Merge patches and extensions into the class
-  @override
-  void visitClassDeclaration(ClassDeclaration node) {
-    node.members.forEach(_maybePatch);
-
-    var mergeMembers = patch.mergeMembers[_qualifiedName(node)];
-    if (mergeMembers == null) return;
-
-    // Merge members from the patch
-    var pos = node.members.last.end;
-    for (var member in mergeMembers) {
-      var code = patch.contents.substring(member.offset, member.end);
-      edits.insert(pos, '\n\n  ' + code);
-    }
-  }
-
-  void _maybePatch(Declaration node) {
-    if (node is FieldDeclaration) return;
-
-    var externalKeyword = (node as dynamic).externalKeyword as Token;
-    if (externalKeyword == null) return;
-
-    var name = _qualifiedName(node);
-    var patchNode = patch.patches[name];
-    if (patchNode == null) {
-      // *.fromEnvironment are left unpatched by dart2js and are handled via
-      // codegen.
-      if (name != 'bool.fromEnvironment' &&
-          name != 'int.fromEnvironment' &&
-          name != 'String.fromEnvironment') {
-        print('warning: patch not found for $name: $node');
-        // TODO(sigmund): delete this fail logic? Rather than emit an empty
-        // file, it's more useful to emit a file with missing patches.
-        // patchWasMissing = true;
-      }
-      return;
-    }
-
-    var patchMeta = patchNode.metadata.lastWhere(_isPatchAnnotation);
-    var start = patchMeta.endToken.next.offset;
-    var code = patch.contents.substring(start, patchNode.end);
-
-    // Const factory constructors can't be legally parsed from the patch file,
-    // so we need to omit the "const" there, but still preserve it.
-    if (node is ConstructorDeclaration &&
-        node.constKeyword != null &&
-        patchNode is ConstructorDeclaration &&
-        patchNode.constKeyword == null) {
-      code = 'const $code';
-    }
-
-    // For some node like static fields, the node's offset doesn't include
-    // the external keyword. Also starting from the keyword lets us preserve
-    // documentation comments.
-    edits.replace(externalKeyword.offset, node.end, code);
-  }
-}
-
-class PatchFinder extends GeneralizingAstVisitor<void> {
-  final String contents;
-  final CompilationUnit unit;
-
-  final patches = <String, Declaration>{};
-  final mergeMembers = <String, List<ClassMember>>{};
-  final mergeDeclarations = <CompilationUnitMember>[];
-
-  PatchFinder.parseAndVisit(String contents, {bool useNnbd})
-      : contents = contents,
-        unit = _parseString(contents, useNnbd: useNnbd).unit {
-    visitCompilationUnit(unit);
-  }
-
-  @override
-  void visitCompilationUnitMember(CompilationUnitMember node) {
-    mergeDeclarations.add(node);
-  }
-
-  @override
-  void visitClassDeclaration(ClassDeclaration node) {
-    if (_isPatch(node)) {
-      var members = <ClassMember>[];
-      for (var member in node.members) {
-        if (_isPatch(member)) {
-          patches[_qualifiedName(member)] = member;
-        } else {
-          members.add(member);
-        }
-      }
-      if (members.isNotEmpty) {
-        mergeMembers[_qualifiedName(node)] = members;
-      }
-    } else {
-      mergeDeclarations.add(node);
-    }
-  }
-
-  @override
-  void visitFunctionDeclaration(FunctionDeclaration node) {
-    if (_isPatch(node)) {
-      patches[_qualifiedName(node)] = node;
-    } else {
-      mergeDeclarations.add(node);
-    }
-  }
-
-  @override
-  void visitFunctionBody(node) {} // skip method bodies
-}
-
-String _qualifiedName(Declaration node) {
-  var result = '';
-
-  var parent = node.parent;
-  if (parent is ClassDeclaration) {
-    result = '${parent.name.name}.';
-  }
-
-  var name = (node as dynamic).name as SimpleIdentifier;
-  if (name != null) result += name.name;
-
-  // Make sure setters and getters don't collide.
-  if (node is FunctionDeclaration && node.isSetter ||
-      node is MethodDeclaration && node.isSetter) {
-    result += '=';
-  }
-
-  return result;
-}
-
-bool _isPatch(AnnotatedNode node) => node.metadata.any(_isPatchAnnotation);
-
-bool _isPatchAnnotation(Annotation m) =>
-    m.name.name == 'patch' && m.constructorName == null && m.arguments == null;
-
-/// Editable string buffer.
-///
-/// Applies a series of edits (insertions, removals, replacements) using
-/// original location information, and composes them into the edited string.
-///
-/// For example, starting with a parsed AST with original source locations,
-/// this type allows edits to be made without regards to other edits.
-class StringEditBuffer {
-  final String original;
-  final _edits = <_StringEdit>[];
-
-  /// Creates a new transaction.
-  StringEditBuffer(this.original);
-
-  bool get hasEdits => _edits.isNotEmpty;
-
-  /// Edit the original text, replacing text on the range [begin] and
-  /// exclusive [end] with the [replacement] string.
-  void replace(int begin, int end, String replacement) {
-    _edits.add(_StringEdit(begin, end, replacement));
-  }
-
-  /// Insert [string] at [offset].
-  /// Equivalent to `replace(offset, offset, string)`.
-  void insert(int offset, String string) => replace(offset, offset, string);
-
-  /// Remove text from the range [begin] to exclusive [end].
-  /// Equivalent to `replace(begin, end, '')`.
-  void remove(int begin, int end) => replace(begin, end, '');
-
-  /// Applies all pending [edit]s and returns a new string.
-  ///
-  /// This method is non-destructive: it does not discard existing edits or
-  /// change the [original] string. Further edits can be added and this method
-  /// can be called again.
-  ///
-  /// Throws [UnsupportedError] if the edits were overlapping. If no edits were
-  /// made, the original string will be returned.
-  @override
-  String toString() {
-    var sb = StringBuffer();
-    if (_edits.isEmpty) return original;
-
-    // Sort edits by start location.
-    _edits.sort();
-
-    var consumed = 0;
-    for (var edit in _edits) {
-      if (consumed > edit.begin) {
-        sb = StringBuffer();
-        sb.write('overlapping edits. Insert at offset ');
-        sb.write(edit.begin);
-        sb.write(' but have consumed ');
-        sb.write(consumed);
-        sb.write(' input characters. List of edits:');
-        for (var e in _edits) {
-          sb.write('\n    ');
-          sb.write(e);
-        }
-        throw UnsupportedError(sb.toString());
-      }
-
-      // Add characters from the original string between this edit and the last
-      // one, if any.
-      var betweenEdits = original.substring(consumed, edit.begin);
-      sb.write(betweenEdits);
-      sb.write(edit.replace);
-      consumed = edit.end;
-    }
-
-    // Add any text from the end of the original string that was not replaced.
-    sb.write(original.substring(consumed));
-    return sb.toString();
-  }
-}
-
-class _StringEdit implements Comparable<_StringEdit> {
-  final int begin;
-  final int end;
-  final String replace;
-
-  _StringEdit(this.begin, this.end, this.replace);
-
-  int get length => end - begin;
-
-  @override
-  String toString() => '(Edit @ $begin,$end: "$replace")';
-
-  @override
-  int compareTo(_StringEdit other) {
-    var diff = begin - other.begin;
-    if (diff != 0) return diff;
-    return end - other.end;
-  }
-}
-
-ParseStringResult _parseString(String source, {bool useNnbd}) {
-  var features = FeatureSet.fromEnableFlags2(
-    sdkLanguageVersion: Version.parse('2.10.0'),
-    flags: [if (useNnbd) 'non-nullable', 'triple-shift'],
-  );
-  return parseString(content: source, featureSet: features);
-}
-
-/// Use the data from a libraries.json specification to generate the contents
-/// of `sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart`, which is
-/// needed by dartdevc-legacy and dartanalyzer.
-String _generateLibrariesDart(
-    Uri libBaseUri, TargetLibrariesSpecification specification, bool usdNnbd) {
-  var contents = StringBuffer();
-  contents.write(_LIBRARIES_DART_PREFIX);
-  for (var library in specification.allLibraries) {
-    var path = relativizeLibraryUri(libBaseUri, library.uri, usdNnbd);
-    contents.write('  "${library.name}": \n'
-        '      const LibraryInfo("$path",\n'
-        '          categories: "Client,Server"),\n');
-  }
-  contents.write(_LIBRARIES_DART_SUFFIX);
-  return '$contents';
-}
-
-String relativizeLibraryUri(Uri libRoot, Uri uri, bool useNnbd) {
-  var relativePath = relativizeUri(libRoot, uri, isWindows);
-  // During the nnbd-migration we may have paths that reach out into the
-  // non-nnbd directory.
-  if (relativePath.startsWith('..')) {
-    if (!useNnbd || !relativePath.startsWith('../../sdk/lib/')) {
-      print("error: can't handle libraries that live out of the sdk folder"
-          ': $relativePath');
-      exit(1);
-    }
-    relativePath = relativePath.replaceFirst('../../sdk/lib/', '');
-  }
-  return relativePath;
-}
-
-final _LIBRARIES_DART_PREFIX = r'''
-library libraries;
-
-const int DART2JS_PLATFORM = 1;
-const int VM_PLATFORM = 2;
-
-enum Category { client, server, embedded }
-
-Category parseCategory(String name) {
-  switch (name) {
-    case "Client":
-      return Category.client;
-    case "Server":
-      return Category.server;
-    case "Embedded":
-      return Category.embedded;
-  }
-  return null;
-}
-
-const Map<String, LibraryInfo> libraries = const {
-''';
-
-final _LIBRARIES_DART_SUFFIX = r'''
-};
-
-class LibraryInfo {
-  final String path;
-  final String _categories;
-  final String dart2jsPath;
-  final String dart2jsPatchPath;
-  final bool documented;
-  final int platforms;
-  final bool implementation;
-  final Maturity maturity;
-
-  const LibraryInfo(this.path,
-      {String categories: "",
-      this.dart2jsPath,
-      this.dart2jsPatchPath,
-      this.implementation: false,
-      this.documented: true,
-      this.maturity: Maturity.UNSPECIFIED,
-      this.platforms: DART2JS_PLATFORM | VM_PLATFORM})
-      : _categories = categories;
-
-  bool get isDart2jsLibrary => (platforms & DART2JS_PLATFORM) != 0;
-  bool get isVmLibrary => (platforms & VM_PLATFORM) != 0;
-  List<Category> get categories {
-    // `"".split(,)` returns [""] not [], so we handle that case separately.
-    if (_categories == "") return const <Category>[];
-    return _categories.split(",").map(parseCategory).toList();
-  }
-
-  bool get isInternal => categories.isEmpty;
-  String get categoriesString => _categories;
-}
-
-class Maturity {
-  final int level;
-  final String name;
-  final String description;
-
-  const Maturity(this.level, this.name, this.description);
-
-  String toString() => "$name: $level\n$description\n";
-
-  static const Maturity DEPRECATED = const Maturity(0, "Deprecated",
-      "This library will be remove before next major release.");
-
-  static const Maturity EXPERIMENTAL = const Maturity(
-      1,
-      "Experimental",
-      "This library is experimental and will likely change or be removed\n"
-          "in future versions.");
-
-  static const Maturity UNSTABLE = const Maturity(
-      2,
-      "Unstable",
-      "This library is in still changing and have not yet endured\n"
-          "sufficient real-world testing.\n"
-          "Backwards-compatibility is NOT guaranteed.");
-
-  static const Maturity WEB_STABLE = const Maturity(
-      3,
-      "Web Stable",
-      "This library is tracking the DOM evolution as defined by WC3.\n"
-          "Backwards-compatibility is NOT guaranteed.");
-
-  static const Maturity STABLE = const Maturity(
-      4,
-      "Stable",
-      "The library is stable. API backwards-compatibility is guaranteed.\n"
-          "However implementation details might change.");
-
-  static const Maturity LOCKED = const Maturity(5, "Locked",
-      "This library will not change except when serious bugs are encountered.");
-
-  static const Maturity UNSPECIFIED = const Maturity(-1, "Unspecified",
-      "The maturity for this library has not been specified.");
-}
-''';
diff --git a/pkg/dev_compiler/tool/vm_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/vm_nnbd_sdk_error_golden.txt
deleted file mode 100644
index 45beed5..0000000
--- a/pkg/dev_compiler/tool/vm_nnbd_sdk_error_golden.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ERROR|STATIC_WARNING|NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER|lib/core/core.dart|3894|7|16|Missing concrete implementations of 'getter Error._stackTrace' and 'setter Error._stackTrace'.
-ERROR|STATIC_WARNING|NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER|lib/core/core.dart|3902|7|18|Missing concrete implementations of 'getter Error._stackTrace' and 'setter Error._stackTrace'.
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index af4a2a7..e22f89c 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -224,6 +224,19 @@
   return ShiftOperationHelper(Token::kSHR, value, amount);
 }
 
+DEFINE_NATIVE_ENTRY(Integer_ushrFromInteger, 0, 2) {
+  const Integer& amount =
+      Integer::CheckedHandle(zone, arguments->NativeArgAt(0));
+  GET_NON_NULL_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1));
+  ASSERT(CheckInteger(amount));
+  ASSERT(CheckInteger(value));
+  if (FLAG_trace_intrinsified_natives) {
+    OS::PrintErr("Integer_ushrFromInteger: %s >>> %s\n", value.ToCString(),
+                 amount.ToCString());
+  }
+  return ShiftOperationHelper(Token::kUSHR, value, amount);
+}
+
 DEFINE_NATIVE_ENTRY(Integer_shlFromInteger, 0, 2) {
   const Integer& amount =
       Integer::CheckedHandle(zone, arguments->NativeArgAt(0));
diff --git a/runtime/tests/vm/dart/regress45047_test.dart b/runtime/tests/vm/dart/regress45047_test.dart
new file mode 100644
index 0000000..4d2f083
--- /dev/null
+++ b/runtime/tests/vm/dart/regress45047_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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.
+
+// Verify that socket connection gracefully closes if cancelled.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+void main() async {
+  final task = await Socket.startConnect('google.com', 80);
+  task.cancel();
+  try {
+    await task.socket;
+  } catch (e) {
+    Expect.isTrue(e is SocketException);
+    final socketException = e as SocketException;
+    Expect.isTrue(
+        socketException.message.startsWith('Connection attempt cancelled'));
+  }
+}
diff --git a/runtime/tests/vm/dart/unsigned_shift_right_test.dart b/runtime/tests/vm/dart/unsigned_shift_right_test.dart
new file mode 100644
index 0000000..41c18f9
--- /dev/null
+++ b/runtime/tests/vm/dart/unsigned_shift_right_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2021, 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.
+
+// SharedOptions=--enable-experiment=triple-shift
+// VMOptions=--deterministic --optimization_counter_threshold=50
+// VMOptions=--no-intrinsify
+
+// Test int.operator>>> in case of Smi range overflow and deoptimization.
+
+import "package:expect/expect.dart";
+
+@pragma('vm:never-inline')
+void test1(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test2(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test3(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test4(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test5(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test6(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test7(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test8(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test9(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test10(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test11(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test12(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test13(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test14(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+void testCornerCases() {
+  // Make sure test methods are optimized.
+  for (int i = 0; i < 100; ++i) {
+    test1(1, 1, 0);
+    test2(1, 1, 0);
+    test3(1, 1, 0);
+    test4(1, 1, 0);
+    test5(1, 1, 0);
+    test6(1, 1, 0);
+    test7(1, 1, 0);
+    test8(1, 1, 0);
+    test9(1, 1, 0);
+    test10(1, 1, 0);
+    test11(1, 1, 0);
+    test12(1, 1, 0);
+    test13(1, 1, 0);
+    test14(1, 1, 0);
+  }
+  // Run tests, may trigger deoptimization.
+  for (int i = 0; i < 100; ++i) {
+    test1(0xffffffffffffffff, 1, 0x7fffffffffffffff);
+    test2(0xffffffffffffabcd, 1, 0x7fffffffffffd5e6);
+    test3(0xffffffffffffffff, 31, 0x1ffffffff);
+    test4(0xffffffffffffffff, 32, 0xffffffff);
+    test5(0xfedcba9876543210, 32, 0xfedcba98);
+    test6(0xffffffffffffffff, 34, 0x3fffffff);
+    test7(0xffffffffffffffff, 56, 0xff);
+    test8(0xfeffffffffffabcd, 56, 0xfe);
+    test9(0xffffffffffffffff, 63, 0x1);
+    test10(0xffffffffffffffff, 64, 0);
+    test11(0xffffffffffffffff, 70, 0);
+    test12(0x8000000000000000, 1, 0x4000000000000000);
+    test13(0x7fedcba987654321, 1, 0x3ff6e5d4c3b2a190);
+    test14(0x7fedcba987654321, 40, 0x7fedcb);
+  }
+}
+
+void testSingleOneBit() {
+  for (int i = 0; i <= 63; ++i) {
+    final int x = 1 << i;
+    for (int j = 0; j <= 127; ++j) {
+      Expect.equals((j > i) ? 0 : 1 << (i - j), x >>> j);
+    }
+  }
+}
+
+void testSingleZeroBit() {
+  for (int i = 0; i <= 63; ++i) {
+    final int x = ~(1 << i);
+    for (int j = 0; j <= 127; ++j) {
+      if (j >= 64) {
+        Expect.equals(0, x >>> j);
+      } else {
+        final int mask = (1 << (64 - j)) - 1;
+        if (j > i) {
+          Expect.equals(mask, x >>> j);
+        } else {
+          Expect.equals(mask & ~(1 << (i - j)), x >>> j);
+        }
+      }
+    }
+  }
+}
+
+main() {
+  testCornerCases();
+
+  for (int i = 0; i < 100; ++i) {
+    testSingleOneBit();
+    testSingleZeroBit();
+  }
+}
diff --git a/runtime/tests/vm/dart_2/regress45047_test.dart b/runtime/tests/vm/dart_2/regress45047_test.dart
new file mode 100644
index 0000000..4d2f083
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress45047_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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.
+
+// Verify that socket connection gracefully closes if cancelled.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+void main() async {
+  final task = await Socket.startConnect('google.com', 80);
+  task.cancel();
+  try {
+    await task.socket;
+  } catch (e) {
+    Expect.isTrue(e is SocketException);
+    final socketException = e as SocketException;
+    Expect.isTrue(
+        socketException.message.startsWith('Connection attempt cancelled'));
+  }
+}
diff --git a/runtime/tests/vm/dart_2/unsigned_shift_right_test.dart b/runtime/tests/vm/dart_2/unsigned_shift_right_test.dart
new file mode 100644
index 0000000..41c18f9
--- /dev/null
+++ b/runtime/tests/vm/dart_2/unsigned_shift_right_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2021, 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.
+
+// SharedOptions=--enable-experiment=triple-shift
+// VMOptions=--deterministic --optimization_counter_threshold=50
+// VMOptions=--no-intrinsify
+
+// Test int.operator>>> in case of Smi range overflow and deoptimization.
+
+import "package:expect/expect.dart";
+
+@pragma('vm:never-inline')
+void test1(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test2(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test3(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test4(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test5(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test6(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test7(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test8(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test9(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test10(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test11(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test12(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test13(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test14(int a, int b, int expected) {
+  Expect.equals(expected, a >>> b);
+}
+
+void testCornerCases() {
+  // Make sure test methods are optimized.
+  for (int i = 0; i < 100; ++i) {
+    test1(1, 1, 0);
+    test2(1, 1, 0);
+    test3(1, 1, 0);
+    test4(1, 1, 0);
+    test5(1, 1, 0);
+    test6(1, 1, 0);
+    test7(1, 1, 0);
+    test8(1, 1, 0);
+    test9(1, 1, 0);
+    test10(1, 1, 0);
+    test11(1, 1, 0);
+    test12(1, 1, 0);
+    test13(1, 1, 0);
+    test14(1, 1, 0);
+  }
+  // Run tests, may trigger deoptimization.
+  for (int i = 0; i < 100; ++i) {
+    test1(0xffffffffffffffff, 1, 0x7fffffffffffffff);
+    test2(0xffffffffffffabcd, 1, 0x7fffffffffffd5e6);
+    test3(0xffffffffffffffff, 31, 0x1ffffffff);
+    test4(0xffffffffffffffff, 32, 0xffffffff);
+    test5(0xfedcba9876543210, 32, 0xfedcba98);
+    test6(0xffffffffffffffff, 34, 0x3fffffff);
+    test7(0xffffffffffffffff, 56, 0xff);
+    test8(0xfeffffffffffabcd, 56, 0xfe);
+    test9(0xffffffffffffffff, 63, 0x1);
+    test10(0xffffffffffffffff, 64, 0);
+    test11(0xffffffffffffffff, 70, 0);
+    test12(0x8000000000000000, 1, 0x4000000000000000);
+    test13(0x7fedcba987654321, 1, 0x3ff6e5d4c3b2a190);
+    test14(0x7fedcba987654321, 40, 0x7fedcb);
+  }
+}
+
+void testSingleOneBit() {
+  for (int i = 0; i <= 63; ++i) {
+    final int x = 1 << i;
+    for (int j = 0; j <= 127; ++j) {
+      Expect.equals((j > i) ? 0 : 1 << (i - j), x >>> j);
+    }
+  }
+}
+
+void testSingleZeroBit() {
+  for (int i = 0; i <= 63; ++i) {
+    final int x = ~(1 << i);
+    for (int j = 0; j <= 127; ++j) {
+      if (j >= 64) {
+        Expect.equals(0, x >>> j);
+      } else {
+        final int mask = (1 << (64 - j)) - 1;
+        if (j > i) {
+          Expect.equals(mask, x >>> j);
+        } else {
+          Expect.equals(mask & ~(1 << (i - j)), x >>> j);
+        }
+      }
+    }
+  }
+}
+
+main() {
+  testCornerCases();
+
+  for (int i = 0; i < 100; ++i) {
+    testSingleOneBit();
+    testSingleZeroBit();
+  }
+}
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index db34630..2470e89 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -51,6 +51,7 @@
   V(Integer_parse, 1)                                                          \
   V(Integer_shlFromInteger, 2)                                                 \
   V(Integer_shrFromInteger, 2)                                                 \
+  V(Integer_ushrFromInteger, 2)                                                \
   V(Bool_fromEnvironment, 3)                                                   \
   V(Bool_hasEnvironment, 2)                                                    \
   V(CapabilityImpl_factory, 1)                                                 \
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 95ae6e3..6073695 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -436,6 +436,19 @@
         if (!arguments.IsNull()) {
           type_arg = arguments.TypeAt(i);
           // The parsed type_arg may or may not be finalized.
+          if (type_arg.IsTypeRef()) {
+            // Dereferencing the TypeRef 'rotates' the cycle in the recursive
+            // type argument, so that the top level type arguments of the type
+            // do not start with a TypeRef, for better readability and possibly
+            // fewer later dereferences in various type traversal routines.
+            // This rotation is not required for correctness.
+            // The cycle containing TypeRefs always involves type arguments of
+            // the super class in the flatten argument vector, so it is safe to
+            // remove TypeRefs from type arguments corresponding to the type
+            // parameters of the type class.
+            // Such TypeRefs may appear after instantiation of types at runtime.
+            type_arg = TypeRef::Cast(type_arg).type();
+          }
         }
         full_arguments.SetTypeAt(offset + i, type_arg);
       }
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index 4ea2220..2729e66 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -567,6 +567,8 @@
         FALL_THROUGH;
       case Token::kSHR:
         FALL_THROUGH;
+      case Token::kUSHR:
+        FALL_THROUGH;
       case Token::kBIT_OR:
         FALL_THROUGH;
       case Token::kBIT_XOR:
@@ -579,7 +581,8 @@
         FALL_THROUGH;
       case Token::kMUL: {
         if (FlowGraphCompiler::SupportsUnboxedInt64()) {
-          if (op_kind == Token::kSHR || op_kind == Token::kSHL) {
+          if (op_kind == Token::kSHL || op_kind == Token::kSHR ||
+              op_kind == Token::kUSHR) {
             left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
             right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
             replacement = new (Z) ShiftInt64OpInstr(
@@ -844,6 +847,7 @@
     }
     case Token::kSHL:
     case Token::kSHR:
+    case Token::kUSHR:
     case Token::kBIT_OR:
     case Token::kBIT_XOR:
     case Token::kBIT_AND:
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index f7ebf06..21149ef 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1530,6 +1530,18 @@
     LslImmediate(dst, src, kSmiTagSize);
   }
 
+  void SmiTagAndBranchIfOverflow(Register reg, Label* label) {
+    COMPILE_ASSERT(kSmiTag == 0);
+    adds(reg, reg, compiler::Operand(reg));  // SmiTag
+    // If the value doesn't fit in a smi, the tagging changes the sign,
+    // which causes the overflow flag to be set.
+    b(label, OVERFLOW);
+#if defined(DART_COMPRESSED_POINTERS)
+    cmp(reg, compiler::Operand(reg, SXTW, 0));
+    b(label, NOT_EQUAL);
+#endif  // defined(DART_COMPRESSED_POINTERS)
+  }
+
   // For ARM, the near argument is ignored.
   void BranchIfNotSmi(Register reg,
                       Label* label,
diff --git a/runtime/vm/compiler/backend/evaluator.cc b/runtime/vm/compiler/backend/evaluator.cc
index 5364b28..11b007a 100644
--- a/runtime/vm/compiler/backend/evaluator.cc
+++ b/runtime/vm/compiler/backend/evaluator.cc
@@ -27,6 +27,8 @@
     case Token::kSHL:
       FALL_THROUGH;
     case Token::kSHR:
+      FALL_THROUGH;
+    case Token::kUSHR:
       if (right.AsInt64Value() >= 0) {
         return left.ShiftOp(token_kind, right, Heap::kOld);
       }
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 26198ca..69b2bd0 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -2073,8 +2073,9 @@
   switch (smi_op->op_kind()) {
     case Token::kMUL:
     case Token::kSHR:
-      // For kMUL we save untagging of the argument for kSHR
-      // we save tagging of the result.
+    case Token::kUSHR:
+      // For kMUL we save untagging of the argument.
+      // For kSHR/kUSHR we save tagging of the result.
       return true;
 
     default:
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index f1f8f02..fb55f7d 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -2118,6 +2118,7 @@
     case Token::kSHR:
       return false;
 
+    case Token::kUSHR:
     case Token::kSHL:
       // Currently only shifts by in range constant are supported, see
       // BinaryInt32OpInstr::IsSupported.
@@ -2142,6 +2143,7 @@
     case Token::kSHR:
       return !RangeUtils::IsPositive(right_range());
 
+    case Token::kUSHR:
     case Token::kSHL:
       return can_overflow() || !RangeUtils::IsPositive(right_range());
 
@@ -2344,8 +2346,9 @@
     case Token::kTRUNCDIV:
       if (representation != kTagged) break;
       FALL_THROUGH;
-    case Token::kSHR:
     case Token::kSHL:
+    case Token::kSHR:
+    case Token::kUSHR:
       if (auto const const_def = right->definition()->AsConstant()) {
         right_range = new Range();
         const_def->InferRange(nullptr, right_range);
@@ -2365,7 +2368,8 @@
       op = new BinaryInt32OpInstr(op_kind, left, right, deopt_id);
       break;
     case kUnboxedUint32:
-      if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
+      if ((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+          (op_kind == Token::kUSHR)) {
         if (speculative_mode == kNotSpeculative) {
           op = new ShiftUint32OpInstr(op_kind, left, right, deopt_id,
                                       right_range);
@@ -2378,7 +2382,8 @@
       }
       break;
     case kUnboxedInt64:
-      if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
+      if ((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+          (op_kind == Token::kUSHR)) {
         if (speculative_mode == kNotSpeculative) {
           op = new ShiftInt64OpInstr(op_kind, left, right, deopt_id,
                                      right_range);
@@ -2631,6 +2636,12 @@
       }
       break;
 
+    case Token::kUSHR:
+      if (rhs >= kBitsPerInt64) {
+        return flow_graph->TryCreateConstantReplacementFor(this,
+                                                           Object::smi_zero());
+      }
+      FALL_THROUGH;
     case Token::kSHR:
       if (rhs == 0) {
         return left()->definition();
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 48ba834..8e19b15 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -7828,6 +7828,7 @@
 
       case Token::kSHL:
       case Token::kSHR:
+      case Token::kUSHR:
         if (right->BindsToConstant() && right->BoundConstant().IsSmi()) {
           const intptr_t value = Smi::Cast(right->BoundConstant()).Value();
           return 0 <= value && value < kBitsPerWord;
@@ -7958,7 +7959,8 @@
                       Range* right_range = nullptr)
       : BinaryIntegerOpInstr(op_kind, left, right, deopt_id),
         shift_range_(right_range) {
-    ASSERT((op_kind == Token::kSHR) || (op_kind == Token::kSHL));
+    ASSERT((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+           (op_kind == Token::kUSHR));
     mark_truncating();
   }
 
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 694c7f9..5d9b0e9 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -4070,6 +4070,56 @@
       __ Asr(result, TMP, result);
       __ SmiTag(result);
       break;
+    case Token::kUSHR: {
+      ASSERT(result != left);
+      ASSERT(result != right);
+      __ CompareImmediate(right, compiler::target::ToRawSmi(kBitsPerInt64));
+      __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
+
+      compiler::Label done;
+      __ SmiUntag(result, right);
+      // 64-bit representation of left operand value:
+      //
+      //       ss...sssss  s  s  xxxxxxxxxxxxx
+      //       |        |  |  |  |           |
+      //       63      32  31 30 kSmiBits-1  0
+      //
+      // Where 's' is a sign bit.
+      //
+      // If left operand is negative (sign bit is set), then
+      // result will fit into Smi range if and only if
+      // the shift amount >= 64 - kSmiBits.
+      //
+      // If left operand is non-negative, the result always
+      // fits into Smi range.
+      //
+      __ CompareImmediate(result, 64 - compiler::target::kSmiBits);
+      // Shift amount >= 64 - kSmiBits > 32, but < 64.
+      // Result is guaranteed to fit into Smi range.
+      // Low (Smi) part of the left operand is shifted out.
+      // High part is filled with sign bits.
+      __ sub(result, result, compiler::Operand(32), GE);
+      __ Asr(TMP, left, compiler::Operand(31), GE);
+      __ Lsr(result, TMP, result, GE);
+      __ SmiTag(result, GE);
+      __ b(&done, GE);
+      // Shift amount < 64 - kSmiBits.
+      // If left is negative, then result will not fit into Smi range.
+      // Also deopt in case of negative shift amount.
+      __ tst(left, compiler::Operand(left));
+      __ b(slow_path->entry_label(), MI);
+      // At this point left operand is non-negative, so unsigned shift
+      // can't overflow.
+      __ CompareImmediate(result, compiler::target::kSmiBits);
+      // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
+      __ LoadImmediate(result, 0, GE);
+      // Left operand >= 0, shift amount < kSmiBits < 32.
+      __ SmiUntag(TMP, left, LT);
+      __ Lsr(result, TMP, result, LT);
+      __ SmiTag(result, LT);
+      __ Bind(&done);
+      break;
+    }
     default:
       UNREACHABLE();
   }
@@ -4230,7 +4280,7 @@
   } else if (op_kind() == Token::kMOD) {
     num_temps = 2;
   } else if (((op_kind() == Token::kSHL) && can_overflow()) ||
-             (op_kind() == Token::kSHR)) {
+             (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     num_temps = 1;
   }
   LocationSummary* summary = new (zone)
@@ -4262,7 +4312,7 @@
   summary->set_in(0, Location::RequiresRegister());
   summary->set_in(1, LocationRegisterOrSmiConstant(right()));
   if (((op_kind() == Token::kSHL) && can_overflow()) ||
-      (op_kind() == Token::kSHR)) {
+      (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     summary->set_temp(0, Location::RequiresRegister());
   }
   // We make use of 3-operand instructions by not requiring result register
@@ -4388,6 +4438,52 @@
         __ SmiTag(result);
         break;
       }
+      case Token::kUSHR: {
+        const intptr_t value = compiler::target::SmiValue(constant);
+        ASSERT((value > 0) && (value < 64));
+        COMPILE_ASSERT(compiler::target::kSmiBits < 32);
+        // 64-bit representation of left operand value:
+        //
+        //       ss...sssss  s  s  xxxxxxxxxxxxx
+        //       |        |  |  |  |           |
+        //       63      32  31 30 kSmiBits-1  0
+        //
+        // Where 's' is a sign bit.
+        //
+        // If left operand is negative (sign bit is set), then
+        // result will fit into Smi range if and only if
+        // the shift amount >= 64 - kSmiBits.
+        //
+        // If left operand is non-negative, the result always
+        // fits into Smi range.
+        //
+        if (value < (64 - compiler::target::kSmiBits)) {
+          if (deopt != nullptr) {
+            __ CompareImmediate(left, 0);
+            __ b(deopt, LT);
+          } else {
+            // Operation cannot overflow only if left value is always
+            // non-negative.
+            ASSERT(!can_overflow());
+          }
+          // At this point left operand is non-negative, so unsigned shift
+          // can't overflow.
+          if (value >= compiler::target::kSmiBits) {
+            __ LoadImmediate(result, 0);
+          } else {
+            __ Lsr(result, left, compiler::Operand(value + kSmiTagSize));
+            __ SmiTag(result);
+          }
+        } else {
+          // Shift amount > 32, and the result is guaranteed to fit into Smi.
+          // Low (Smi) part of the left operand is shifted out.
+          // High part is filled with sign bits.
+          __ Asr(result, left, compiler::Operand(31));
+          __ Lsr(result, result, compiler::Operand(value - 32));
+          __ SmiTag(result);
+        }
+        break;
+      }
 
       default:
         UNREACHABLE();
@@ -4516,6 +4612,71 @@
       __ SmiTag(result);
       break;
     }
+    case Token::kUSHR: {
+      compiler::Label done;
+      __ SmiUntag(IP, right);
+      // 64-bit representation of left operand value:
+      //
+      //       ss...sssss  s  s  xxxxxxxxxxxxx
+      //       |        |  |  |  |           |
+      //       63      32  31 30 kSmiBits-1  0
+      //
+      // Where 's' is a sign bit.
+      //
+      // If left operand is negative (sign bit is set), then
+      // result will fit into Smi range if and only if
+      // the shift amount >= 64 - kSmiBits.
+      //
+      // If left operand is non-negative, the result always
+      // fits into Smi range.
+      //
+      if (!RangeUtils::OnlyLessThanOrEqualTo(
+              right_range(), 64 - compiler::target::kSmiBits - 1)) {
+        if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+                                               kBitsPerInt64 - 1)) {
+          __ CompareImmediate(IP, kBitsPerInt64);
+          // If shift amount >= 64, then result is 0.
+          __ LoadImmediate(result, 0, GE);
+          __ b(&done, GE);
+        }
+        __ CompareImmediate(IP, 64 - compiler::target::kSmiBits);
+        // Shift amount >= 64 - kSmiBits > 32, but < 64.
+        // Result is guaranteed to fit into Smi range.
+        // Low (Smi) part of the left operand is shifted out.
+        // High part is filled with sign bits.
+        __ sub(IP, IP, compiler::Operand(32), GE);
+        __ Asr(result, left, compiler::Operand(31), GE);
+        __ Lsr(result, result, IP, GE);
+        __ SmiTag(result, GE);
+        __ b(&done, GE);
+      }
+      // Shift amount < 64 - kSmiBits.
+      // If left is negative, then result will not fit into Smi range.
+      // Also deopt in case of negative shift amount.
+      if (deopt != nullptr) {
+        __ tst(left, compiler::Operand(left));
+        __ tst(right, compiler::Operand(right), PL);
+        __ b(deopt, MI);
+      } else {
+        ASSERT(!can_overflow());
+      }
+      // At this point left operand is non-negative, so unsigned shift
+      // can't overflow.
+      if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+                                             compiler::target::kSmiBits - 1)) {
+        __ CompareImmediate(IP, compiler::target::kSmiBits);
+        // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
+        __ LoadImmediate(result, 0, GE);
+        __ b(&done, GE);
+      }
+      // Left operand >= 0, shift amount < kSmiBits < 32.
+      const Register temp = locs()->temp(0).reg();
+      __ SmiUntag(temp, left);
+      __ Lsr(result, temp, IP);
+      __ SmiTag(result);
+      __ Bind(&done);
+      break;
+    }
     case Token::kDIV: {
       // Dispatches to 'Double./'.
       // TODO(srdjan): Implement as conversion to double and double division.
@@ -4568,7 +4729,7 @@
   // Calculate number of temporaries.
   intptr_t num_temps = 0;
   if (((op_kind() == Token::kSHL) && can_overflow()) ||
-      (op_kind() == Token::kSHR)) {
+      (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     num_temps = 1;
   }
   LocationSummary* summary = new (zone)
@@ -4576,7 +4737,7 @@
   summary->set_in(0, Location::RequiresRegister());
   summary->set_in(1, LocationRegisterOrSmiConstant(right()));
   if (((op_kind() == Token::kSHL) && can_overflow()) ||
-      (op_kind() == Token::kSHR)) {
+      (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     summary->set_temp(0, Location::RequiresRegister());
   }
   // We make use of 3-operand instructions by not requiring result register
@@ -4678,6 +4839,45 @@
                compiler::Operand(Utils::Minimum(value, kCountLimit)));
         break;
       }
+      case Token::kUSHR: {
+        ASSERT((value > 0) && (value < 64));
+        // 64-bit representation of left operand value:
+        //
+        //       ss...sssss  s  xxxxxxxxxxxxx
+        //       |        |  |  |           |
+        //       63      32  31 30          0
+        //
+        // Where 's' is a sign bit.
+        //
+        // If left operand is negative (sign bit is set), then
+        // result will fit into Int32 range if and only if
+        // the shift amount > 32.
+        //
+        if (value <= 32) {
+          if (deopt != nullptr) {
+            __ tst(left, compiler::Operand(left));
+            __ b(deopt, MI);
+          } else {
+            // Operation cannot overflow only if left value is always
+            // non-negative.
+            ASSERT(!can_overflow());
+          }
+          // At this point left operand is non-negative, so unsigned shift
+          // can't overflow.
+          if (value == 32) {
+            __ LoadImmediate(result, 0);
+          } else {
+            __ Lsr(result, left, compiler::Operand(value));
+          }
+        } else {
+          // Shift amount > 32.
+          // Low (Int32) part of the left operand is shifted out.
+          // Shift high part which is filled with sign bits.
+          __ Asr(result, left, compiler::Operand(31));
+          __ Lsr(result, result, compiler::Operand(value - 32));
+        }
+        break;
+      }
 
       default:
         UNREACHABLE();
@@ -6832,6 +7032,22 @@
       }
       break;
     }
+    case Token::kUSHR: {
+      ASSERT(shift < 64);
+      if (shift < 32) {
+        __ Lsl(out_lo, left_hi, compiler::Operand(32 - shift));
+        __ orr(out_lo, out_lo, compiler::Operand(left_lo, LSR, shift));
+        __ Lsr(out_hi, left_hi, compiler::Operand(shift));
+      } else {
+        if (shift == 32) {
+          __ mov(out_lo, compiler::Operand(left_hi));
+        } else {
+          __ Lsr(out_lo, left_hi, compiler::Operand(shift - 32));
+        }
+        __ mov(out_hi, compiler::Operand(0));
+      }
+      break;
+    }
     case Token::kSHL: {
       ASSERT(shift < 64);
       if (shift < 32) {
@@ -6870,6 +7086,15 @@
       __ mov(out_hi, compiler::Operand(left_hi, ASR, right));
       break;
     }
+    case Token::kUSHR: {
+      __ rsbs(IP, right, compiler::Operand(32));
+      __ sub(IP, right, compiler::Operand(32), MI);
+      __ mov(out_lo, compiler::Operand(left_hi, LSR, IP), MI);
+      __ mov(out_lo, compiler::Operand(left_lo, LSR, right), PL);
+      __ orr(out_lo, out_lo, compiler::Operand(left_hi, LSL, IP), PL);
+      __ mov(out_hi, compiler::Operand(left_hi, LSR, right));
+      break;
+    }
     case Token::kSHL: {
       __ rsbs(IP, right, compiler::Operand(32));
       __ sub(IP, right, compiler::Operand(32), MI);
@@ -6896,6 +7121,7 @@
   } else {
     switch (op_kind) {
       case Token::kSHR:
+      case Token::kUSHR:
         __ Lsr(out, left, compiler::Operand(shift));
         break;
       case Token::kSHL:
@@ -6914,6 +7140,7 @@
                                       Register right) {
   switch (op_kind) {
     case Token::kSHR:
+    case Token::kUSHR:
       __ Lsr(out, left, right);
       break;
     case Token::kSHL:
@@ -6951,6 +7178,7 @@
                compiler::Operand(compiler::target::kBitsPerWord - 1), GE);
         __ mov(out_lo, compiler::Operand(out_hi), GE);
         break;
+      case Token::kUSHR:
       case Token::kSHL: {
         __ LoadImmediate(out_lo, 0, GE);
         __ LoadImmediate(out_hi, 0, GE);
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index cc4cc66..5f4a51c 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -3600,15 +3600,22 @@
       __ b(slow_path->entry_label(), NE);  // Overflow.
       break;
     case Token::kSHR:
+    case Token::kUSHR:
       ASSERT(result != left);
       ASSERT(result != right);
-      __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
-      __ b(slow_path->entry_label(), CS);
+      __ CompareImmediate(right, static_cast<int64_t>(Smi::New(kBitsPerInt64)));
+      __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
 
       __ SmiUntag(result, right);
       __ SmiUntag(TMP, left);
-      __ asrv(result, TMP, result);
-      __ SmiTag(result);
+      if (op_kind() == Token::kSHR) {
+        __ asrv(result, TMP, result);
+        __ SmiTag(result);
+      } else {
+        ASSERT(op_kind() == Token::kUSHR);
+        __ lsrv(result, TMP, result);
+        __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
+      }
       break;
     default:
       UNIMPLEMENTED();
@@ -3770,10 +3777,11 @@
 LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
                                                        bool opt) const {
   const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = (((op_kind() == Token::kSHL) && can_overflow()) ||
-                              (op_kind() == Token::kSHR))
-                                 ? 1
-                                 : 0;
+  const intptr_t kNumTemps =
+      (((op_kind() == Token::kSHL) && can_overflow()) ||
+       (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR))
+          ? 1
+          : 0;
   LocationSummary* summary = new (zone)
       LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
   if (op_kind() == Token::kTRUNCDIV) {
@@ -3796,7 +3804,7 @@
   summary->set_in(0, Location::RequiresRegister());
   summary->set_in(1, LocationRegisterOrSmiConstant(right()));
   if (((op_kind() == Token::kSHL) && can_overflow()) ||
-      (op_kind() == Token::kSHR)) {
+      (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     summary->set_temp(0, Location::RequiresRegister());
   }
   // We make use of 3-operand instructions by not requiring result register
@@ -3921,6 +3929,22 @@
         __ SmiTag(result);
         break;
       }
+      case Token::kUSHR: {
+        // Lsr operation masks the count to 6 bits, but
+        // unsigned shifts by >= kBitsPerInt64 are eliminated by
+        // BinaryIntegerOpInstr::Canonicalize.
+        const intptr_t kCountLimit = 0x3F;
+        intptr_t value = Smi::Cast(constant).Value();
+        ASSERT((value >= 0) && (value <= kCountLimit));
+        __ SmiUntag(left);
+        __ LsrImmediate(result, left, value);
+        if (deopt != nullptr) {
+          __ SmiTagAndBranchIfOverflow(result, deopt);
+        } else {
+          __ SmiTag(result);
+        }
+        break;
+      }
       default:
         UNREACHABLE();
         break;
@@ -3998,8 +4022,7 @@
     case Token::kTRUNCDIV: {
       if (RangeUtils::CanBeZero(right_range())) {
         // Handle divide by zero in runtime.
-        __ CompareRegisters(right, ZR);
-        __ b(deopt, EQ);
+        __ cbz(deopt, right);
       }
       const Register temp = TMP2;
       __ SmiUntag(temp, left);
@@ -4022,8 +4045,7 @@
     case Token::kMOD: {
       if (RangeUtils::CanBeZero(right_range())) {
         // Handle divide by zero in runtime.
-        __ CompareRegisters(right, ZR);
-        __ b(deopt, EQ);
+        __ cbz(deopt, right);
       }
       const Register temp = TMP2;
       __ SmiUntag(temp, left);
@@ -4055,11 +4077,10 @@
     }
     case Token::kSHR: {
       if (CanDeoptimize()) {
-        __ CompareRegisters(right, ZR);
-        __ b(deopt, LT);
+        __ tbnz(deopt, right, compiler::target::kSmiBits + kSmiTagSize);
       }
       __ SmiUntag(TMP, right);
-      // sarl operation masks the count to 6 bits.
+      // asrv operation masks the count to 6 bits.
       const intptr_t kCountLimit = 0x3F;
       if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
         __ LoadImmediate(TMP2, kCountLimit);
@@ -4072,6 +4093,32 @@
       __ SmiTag(result);
       break;
     }
+    case Token::kUSHR: {
+      if (CanDeoptimize()) {
+        __ tbnz(deopt, right, compiler::target::kSmiBits + kSmiTagSize);
+      }
+      __ SmiUntag(TMP, right);
+      // lsrv operation masks the count to 6 bits.
+      const intptr_t kCountLimit = 0x3F;
+      COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
+      compiler::Label done;
+      if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
+        __ LoadImmediate(TMP2, kCountLimit);
+        __ CompareRegisters(TMP, TMP2);
+        __ csel(result, ZR, result, GT);
+        __ b(&done, GT);
+      }
+      const Register temp = locs()->temp(0).reg();
+      __ SmiUntag(temp, left);
+      __ lsrv(result, temp, TMP);
+      if (deopt != nullptr) {
+        __ SmiTagAndBranchIfOverflow(result, deopt);
+      } else {
+        __ SmiTag(result);
+      }
+      __ Bind(&done);
+      break;
+    }
     case Token::kDIV: {
       // Dispatches to 'Double./'.
       // TODO(srdjan): Implement as conversion to double and double division.
@@ -6144,6 +6191,11 @@
                       Utils::Minimum<int64_t>(shift, kBitsPerWord - 1));
       break;
     }
+    case Token::kUSHR: {
+      ASSERT(shift < 64);
+      __ LsrImmediate(out, left, shift);
+      break;
+    }
     case Token::kSHL: {
       ASSERT(shift < 64);
       __ LslImmediate(out, left, shift);
@@ -6164,6 +6216,10 @@
       __ asrv(out, left, right);
       break;
     }
+    case Token::kUSHR: {
+      __ lsrv(out, left, right);
+      break;
+    }
     case Token::kSHL: {
       __ lslv(out, left, right);
       break;
@@ -6185,6 +6241,7 @@
   } else {
     switch (op_kind) {
       case Token::kSHR:
+      case Token::kUSHR:
         __ LsrImmediate(out, left, shift, compiler::kFourBytes);
         break;
       case Token::kSHL:
@@ -6203,6 +6260,7 @@
                                       Register right) {
   switch (op_kind) {
     case Token::kSHR:
+    case Token::kUSHR:
       __ lsrvw(out, left, right);
       break;
     case Token::kSHL:
@@ -6235,6 +6293,7 @@
       case Token::kSHR:
         __ AsrImmediate(out, left, kBitsPerWord - 1);
         break;
+      case Token::kUSHR:
       case Token::kSHL:
         __ mov(out, ZR);
         break;
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index a78e47b..e192ce4 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -3280,7 +3280,7 @@
     // Will be used for sign extension and division.
     summary->set_temp(0, Location::RegisterLocation(EAX));
     return summary;
-  } else if (op_kind() == Token::kSHR) {
+  } else if ((op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     const intptr_t kNumTemps = 0;
     LocationSummary* summary = new (zone)
         LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3412,6 +3412,52 @@
         break;
       }
 
+      case Token::kUSHR: {
+        ASSERT((value > 0) && (value < 64));
+        COMPILE_ASSERT(compiler::target::kSmiBits < 32);
+        // 64-bit representation of left operand value:
+        //
+        //       ss...sssss  s  s  xxxxxxxxxxxxx
+        //       |        |  |  |  |           |
+        //       63      32  31 30 kSmiBits-1  0
+        //
+        // Where 's' is a sign bit.
+        //
+        // If left operand is negative (sign bit is set), then
+        // result will fit into Smi range if and only if
+        // the shift amount >= 64 - kSmiBits.
+        //
+        // If left operand is non-negative, the result always
+        // fits into Smi range.
+        //
+        if (value < (64 - compiler::target::kSmiBits)) {
+          if (deopt != nullptr) {
+            __ testl(left, left);
+            __ j(LESS, deopt);
+          } else {
+            // Operation cannot overflow only if left value is always
+            // non-negative.
+            ASSERT(!can_overflow());
+          }
+          // At this point left operand is non-negative, so unsigned shift
+          // can't overflow.
+          if (value >= compiler::target::kSmiBits) {
+            __ xorl(left, left);
+          } else {
+            __ shrl(left, compiler::Immediate(value + kSmiTagSize));
+            __ SmiTag(left);
+          }
+        } else {
+          // Shift amount > 32, and the result is guaranteed to fit into Smi.
+          // Low (Smi) part of the left operand is shifted out.
+          // High part is filled with sign bits.
+          __ sarl(left, compiler::Immediate(31));
+          __ shrl(left, compiler::Immediate(value - 32));
+          __ SmiTag(left);
+        }
+        break;
+      }
+
       default:
         UNREACHABLE();
         break;
@@ -3534,6 +3580,82 @@
       __ SmiTag(left);
       break;
     }
+    case Token::kUSHR: {
+      compiler::Label done;
+      __ SmiUntag(right);
+      // 64-bit representation of left operand value:
+      //
+      //       ss...sssss  s  s  xxxxxxxxxxxxx
+      //       |        |  |  |  |           |
+      //       63      32  31 30 kSmiBits-1  0
+      //
+      // Where 's' is a sign bit.
+      //
+      // If left operand is negative (sign bit is set), then
+      // result will fit into Smi range if and only if
+      // the shift amount >= 64 - kSmiBits.
+      //
+      // If left operand is non-negative, the result always
+      // fits into Smi range.
+      //
+      if (!RangeUtils::OnlyLessThanOrEqualTo(
+              right_range(), 64 - compiler::target::kSmiBits - 1)) {
+        __ cmpl(right, compiler::Immediate(64 - compiler::target::kSmiBits));
+        compiler::Label shift_less_34;
+        __ j(LESS, &shift_less_34, compiler::Assembler::kNearJump);
+        if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+                                               kBitsPerInt64 - 1)) {
+          __ cmpl(right, compiler::Immediate(kBitsPerInt64));
+          compiler::Label shift_less_64;
+          __ j(LESS, &shift_less_64, compiler::Assembler::kNearJump);
+          // Shift amount >= 64. Result is 0.
+          __ xorl(left, left);
+          __ jmp(&done, compiler::Assembler::kNearJump);
+          __ Bind(&shift_less_64);
+        }
+        // Shift amount >= 64 - kSmiBits > 32, but < 64.
+        // Result is guaranteed to fit into Smi range.
+        // Low (Smi) part of the left operand is shifted out.
+        // High part is filled with sign bits.
+        ASSERT(right == ECX);  // Count must be in ECX
+        __ subl(right, compiler::Immediate(32));
+        __ sarl(left, compiler::Immediate(31));
+        __ shrl(left, right);
+        __ SmiTag(left);
+        __ jmp(&done, compiler::Assembler::kNearJump);
+        __ Bind(&shift_less_34);
+      }
+      // Shift amount < 64 - kSmiBits.
+      // If left is negative, then result will not fit into Smi range.
+      // Also deopt in case of negative shift amount.
+      if (deopt != nullptr) {
+        __ testl(left, left);
+        __ j(LESS, deopt);
+        __ testl(right, right);
+        __ j(LESS, deopt);
+      } else {
+        ASSERT(!can_overflow());
+      }
+      // At this point left operand is non-negative, so unsigned shift
+      // can't overflow.
+      if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+                                             compiler::target::kSmiBits - 1)) {
+        __ cmpl(right, compiler::Immediate(compiler::target::kSmiBits));
+        compiler::Label shift_less_30;
+        __ j(LESS, &shift_less_30, compiler::Assembler::kNearJump);
+        // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
+        __ xorl(left, left);
+        __ jmp(&done, compiler::Assembler::kNearJump);
+        __ Bind(&shift_less_30);
+      }
+      // Left operand >= 0, shift amount < kSmiBits < 32.
+      ASSERT(right == ECX);  // Count must be in ECX
+      __ SmiUntag(left);
+      __ shrl(left, right);
+      __ SmiTag(left);
+      __ Bind(&done);
+      break;
+    }
     case Token::kDIV: {
       // Dispatches to 'Double./'.
       // TODO(srdjan): Implement as conversion to double and double division.
@@ -3562,7 +3684,7 @@
   } else if (op_kind() == Token::kMOD) {
     UNREACHABLE();
     return NULL;
-  } else if (op_kind() == Token::kSHR) {
+  } else if ((op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
     const intptr_t kNumTemps = 0;
     LocationSummary* summary = new (zone)
         LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3670,6 +3792,46 @@
         break;
       }
 
+      case Token::kUSHR: {
+        ASSERT((value > 0) && (value < 64));
+        // 64-bit representation of left operand value:
+        //
+        //       ss...sssss  s  xxxxxxxxxxxxx
+        //       |        |  |  |           |
+        //       63      32  31 30          0
+        //
+        // Where 's' is a sign bit.
+        //
+        // If left operand is negative (sign bit is set), then
+        // result will fit into Int32 range if and only if
+        // the shift amount > 32.
+        //
+        if (value <= 32) {
+          if (deopt != nullptr) {
+            __ testl(left, left);
+            __ j(LESS, deopt);
+          } else {
+            // Operation cannot overflow only if left value is always
+            // non-negative.
+            ASSERT(!can_overflow());
+          }
+          // At this point left operand is non-negative, so unsigned shift
+          // can't overflow.
+          if (value == 32) {
+            __ xorl(left, left);
+          } else {
+            __ shrl(left, compiler::Immediate(value));
+          }
+        } else {
+          // Shift amount > 32.
+          // Low (Int32) part of the left operand is shifted out.
+          // Shift high part which is filled with sign bits.
+          __ sarl(left, compiler::Immediate(31));
+          __ shrl(left, compiler::Immediate(value - 32));
+        }
+        break;
+      }
+
       default:
         UNREACHABLE();
         break;
@@ -5850,6 +6012,20 @@
       }
       break;
     }
+    case Token::kUSHR: {
+      ASSERT(shift < 64);
+      if (shift > 31) {
+        __ movl(left_lo, left_hi);  // Shift by 32.
+        __ xorl(left_hi, left_hi);  // Zero extend left hi.
+        if (shift > 32) {
+          __ shrl(left_lo, compiler::Immediate(shift - 32));
+        }
+      } else {
+        __ shrdl(left_lo, left_hi, compiler::Immediate(shift));
+        __ shrl(left_hi, compiler::Immediate(shift));
+      }
+      break;
+    }
     case Token::kSHL: {
       ASSERT(shift < 64);
       if (shift > 31) {
@@ -5892,6 +6068,21 @@
       __ sarl(left_lo, ECX);                      // Shift count: CL % 32.
       break;
     }
+    case Token::kUSHR: {
+      __ cmpl(ECX, compiler::Immediate(31));
+      __ j(ABOVE, &large_shift);
+
+      __ shrdl(left_lo, left_hi, ECX);  // Shift count in CL.
+      __ shrl(left_hi, ECX);            // Shift count in CL.
+      __ jmp(&done, compiler::Assembler::kNearJump);
+
+      __ Bind(&large_shift);
+      // No need to subtract 32 from CL, only 5 bits used by sarl.
+      __ movl(left_lo, left_hi);  // Shift by 32.
+      __ xorl(left_hi, left_hi);  // Zero extend left hi.
+      __ shrl(left_lo, ECX);      // Shift count: CL % 32.
+      break;
+    }
     case Token::kSHL: {
       __ cmpl(ECX, compiler::Immediate(31));
       __ j(ABOVE, &large_shift);
@@ -5922,7 +6113,8 @@
     __ xorl(left, left);
   } else {
     switch (op_kind) {
-      case Token::kSHR: {
+      case Token::kSHR:
+      case Token::kUSHR: {
         __ shrl(left, compiler::Immediate(shift));
         break;
       }
@@ -5940,7 +6132,8 @@
                                  Token::Kind op_kind,
                                  Register left) {
   switch (op_kind) {
-    case Token::kSHR: {
+    case Token::kSHR:
+    case Token::kUSHR: {
       __ shrl(left, ECX);
       break;
     }
@@ -5986,6 +6179,7 @@
         __ sarl(out_hi, compiler::Immediate(31));
         __ movl(out_lo, out_hi);
         break;
+      case Token::kUSHR:
       case Token::kSHL: {
         __ xorl(out_lo, out_lo);
         __ xorl(out_hi, out_hi);
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index bd87d93..2616a66 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -3563,7 +3563,8 @@
 
 LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
                                                         bool opt) const {
-  bool is_shift = (op_kind() == Token::kSHL) || (op_kind() == Token::kSHR);
+  bool is_shift = (op_kind() == Token::kSHL) || (op_kind() == Token::kSHR) ||
+                  (op_kind() == Token::kUSHR);
   const intptr_t kNumInputs = 2;
   const intptr_t kNumTemps = is_shift ? 1 : 0;
   LocationSummary* summary = new (zone) LocationSummary(
@@ -3576,6 +3577,7 @@
     case Token::kMUL:
     case Token::kSHL:
     case Token::kSHR:
+    case Token::kUSHR:
       summary->set_out(0, Location::RequiresRegister());
       break;
     case Token::kBIT_OR:
@@ -3686,19 +3688,33 @@
       __ movsxd(result, result);
 #endif
       break;
-    case Token::kSHR: {
+    case Token::kSHR:
+    case Token::kUSHR: {
       compiler::Label shift_count_ok;
       ASSERT(result != right);
       ASSERT(locs()->temp(0).reg() == RCX);
-      __ cmpq(right, compiler::Immediate(Smi::RawValue(Smi::kBits)));
+      __ cmpq(right, compiler::Immediate(Smi::RawValue(kBitsPerInt64)));
       __ j(ABOVE_EQUAL, slow_path->entry_label());
 
       __ movq(RCX, right);
       __ SmiUntag(RCX);
       __ movq(result, left);
       __ SmiUntag(result);
-      __ sarq(result, RCX);
-      __ SmiTag(result);
+      if (op_kind() == Token::kSHR) {
+        __ sarq(result, RCX);
+        __ SmiTag(result);
+      } else {
+        ASSERT(op_kind() == Token::kUSHR);
+        __ shrq(result, RCX);
+        __ SmiTag(result);
+        __ j(OVERFLOW, slow_path->entry_label());
+#if defined(DART_COMPRESSED_POINTERS)
+        const Register temp = locs()->temp(0).reg();
+        __ movsxd(temp, result);
+        __ cmpq(temp, result);
+        __ j(NOT_EQUAL, slow_path->entry_label());
+#endif  // defined(DART_COMPRESSED_POINTERS)
+      }
       break;
     }
     default:
@@ -3874,8 +3890,12 @@
 
   ConstantInstr* right_constant = right()->definition()->AsConstant();
   if ((right_constant != NULL) && (op_kind() != Token::kTRUNCDIV) &&
-      (op_kind() != Token::kSHL) && (op_kind() != Token::kMUL) &&
-      (op_kind() != Token::kMOD) && CanBeImmediate(right_constant->value())) {
+      (op_kind() != Token::kSHL) &&
+#if defined(DART_COMPRESSED_POINTERS)
+      (op_kind() != Token::kUSHR) &&
+#endif
+      (op_kind() != Token::kMUL) && (op_kind() != Token::kMOD) &&
+      CanBeImmediate(right_constant->value())) {
     const intptr_t kNumTemps = 0;
     LocationSummary* summary = new (zone)
         LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3915,7 +3935,11 @@
     // Will be used for sign extension and division.
     summary->set_temp(0, Location::RegisterLocation(RAX));
     return summary;
-  } else if (op_kind() == Token::kSHR) {
+  } else if ((op_kind() == Token::kSHR)
+#if !defined(DART_COMPRESSED_POINTERS)
+             || (op_kind() == Token::kUSHR)
+#endif  // !defined(DART_COMPRESSED_POINTERS)
+  ) {
     const intptr_t kNumTemps = 0;
     LocationSummary* summary = new (zone)
         LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3923,6 +3947,22 @@
     summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
     summary->set_out(0, Location::SameAsFirstInput());
     return summary;
+#if defined(DART_COMPRESSED_POINTERS)
+  } else if (op_kind() == Token::kUSHR) {
+    const intptr_t kNumTemps = 1;
+    LocationSummary* summary = new (zone)
+        LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
+    summary->set_in(0, Location::RequiresRegister());
+    if ((right_constant != nullptr) &&
+        CanBeImmediate(right_constant->value())) {
+      summary->set_in(1, Location::Constant(right_constant));
+    } else {
+      summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
+    }
+    summary->set_out(0, Location::SameAsFirstInput());
+    summary->set_temp(0, Location::RequiresRegister());
+    return summary;
+#endif  // defined(DART_COMPRESSED_POINTERS)
   } else if (op_kind() == Token::kSHL) {
     // Shift-by-1 overflow checking can use flags, otherwise we need a temp.
     const bool shiftBy1 =
@@ -4082,6 +4122,28 @@
         break;
       }
 
+      case Token::kUSHR: {
+        // shrq operation masks the count to 6 bits, but
+        // unsigned shifts by >= kBitsPerInt64 are eliminated by
+        // BinaryIntegerOpInstr::Canonicalize.
+        const intptr_t kCountLimit = 0x3F;
+        const intptr_t value = Smi::Cast(constant).Value();
+        ASSERT((value >= 0) && (value <= kCountLimit));
+        __ SmiUntag(left);
+        __ shrq(left, compiler::Immediate(value));
+        __ SmiTag(left);
+        if (deopt != nullptr) {
+          __ j(OVERFLOW, deopt);
+#if defined(DART_COMPRESSED_POINTERS)
+          const Register temp = locs()->temp(0).reg();
+          __ movsxd(temp, left);
+          __ cmpq(temp, left);
+          __ j(NOT_EQUAL, deopt);
+#endif  // defined(DART_COMPRESSED_POINTERS)
+        }
+        break;
+      }
+
       default:
         UNREACHABLE();
         break;
@@ -4398,6 +4460,40 @@
       __ SmiTag(left);
       break;
     }
+    case Token::kUSHR: {
+      if (deopt != nullptr) {
+        __ CompareImmediate(right, compiler::Immediate(0));
+        __ j(LESS, deopt);
+      }
+      __ SmiUntag(right);
+      // shrq operation masks the count to 6 bits.
+      const intptr_t kCountLimit = 0x3F;
+      COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
+      compiler::Label done;
+      if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
+        __ CompareImmediate(right, compiler::Immediate(kCountLimit));
+        compiler::Label count_ok;
+        __ j(LESS_EQUAL, &count_ok, compiler::Assembler::kNearJump);
+        __ xorq(left, left);
+        __ jmp(&done, compiler::Assembler::kNearJump);
+        __ Bind(&count_ok);
+      }
+      ASSERT(right == RCX);  // Count must be in RCX
+      __ SmiUntag(left);
+      __ shrq(left, right);
+      __ SmiTag(left);
+      if (deopt != nullptr) {
+        __ j(OVERFLOW, deopt);
+#if defined(DART_COMPRESSED_POINTERS)
+        const Register temp = locs()->temp(0).reg();
+        __ movsxd(temp, left);
+        __ cmpq(temp, left);
+        __ j(NOT_EQUAL, deopt);
+#endif  // defined(DART_COMPRESSED_POINTERS)
+      }
+      __ Bind(&done);
+      break;
+    }
     case Token::kDIV: {
       // Dispatches to 'Double./'.
       // TODO(srdjan): Implement as conversion to double and double division.
@@ -6660,6 +6756,10 @@
       __ sarq(left, compiler::Immediate(
                         Utils::Minimum<int64_t>(shift, kBitsPerWord - 1)));
       break;
+    case Token::kUSHR:
+      ASSERT(shift < 64);
+      __ shrq(left, compiler::Immediate(shift));
+      break;
     case Token::kSHL: {
       ASSERT(shift < 64);
       __ shlq(left, compiler::Immediate(shift));
@@ -6678,6 +6778,10 @@
       __ sarq(left, RCX);
       break;
     }
+    case Token::kUSHR: {
+      __ shrq(left, RCX);
+      break;
+    }
     case Token::kSHL: {
       __ shlq(left, RCX);
       break;
@@ -6697,7 +6801,8 @@
     __ xorl(left, left);
   } else {
     switch (op_kind) {
-      case Token::kSHR: {
+      case Token::kSHR:
+      case Token::kUSHR: {
         __ shrl(left, compiler::Immediate(shift));
         break;
       }
@@ -6715,7 +6820,8 @@
                                  Token::Kind op_kind,
                                  Register left) {
   switch (op_kind) {
-    case Token::kSHR: {
+    case Token::kSHR:
+    case Token::kUSHR: {
       __ shrl(left, RCX);
       break;
     }
@@ -6749,6 +6855,7 @@
       case Token::kSHR:
         __ sarq(out, compiler::Immediate(kBitsPerInt64 - 1));
         break;
+      case Token::kUSHR:
       case Token::kSHL:
         __ xorq(out, out);
         break;
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index febd184..5f0ebc3 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -1580,7 +1580,7 @@
   // because we need the high bits.
   if (def->IsShiftInt64Op() || def->IsSpeculativeShiftInt64Op()) {
     ShiftIntegerOpInstr* op = def->AsShiftIntegerOp();
-    if (op->op_kind() == Token::kSHR) {
+    if ((op->op_kind() == Token::kSHR) || (op->op_kind() == Token::kUSHR)) {
       Definition* shift_input = op->left()->definition();
       ASSERT(shift_input != NULL);
       Range* range = shift_input->range();
@@ -2186,6 +2186,75 @@
       left_max, left_max.ConstantValue() > 0 ? right_min : right_max);
 }
 
+static void ConvertRangeToUnsigned(int64_t a,
+                                   int64_t b,
+                                   uint64_t* ua,
+                                   uint64_t* ub) {
+  ASSERT(a <= b);
+  if ((a < 0) && (b >= 0)) {
+    // Range contains -1 and 0 and wraps-around as unsigned.
+    *ua = 0;
+    *ub = kMaxUint64;
+  } else {
+    // Range is fully in the negative or non-negative part
+    // and doesn't wrap-around if interpreted as unsigned.
+    *ua = static_cast<uint64_t>(a);
+    *ub = static_cast<uint64_t>(b);
+  }
+}
+
+static void ConvertRangeToSigned(uint64_t a,
+                                 uint64_t b,
+                                 int64_t* sa,
+                                 int64_t* sb) {
+  ASSERT(a <= b);
+  if ((a <= static_cast<uint64_t>(kMaxInt64)) &&
+      (b >= static_cast<uint64_t>(kMinInt64))) {
+    // Range contains kMinInt64 and kMaxInt64 and wraps-around as signed.
+    *sa = kMinInt64;
+    *sb = kMaxInt64;
+  } else {
+    // Range is fully in the negative or non-negative part
+    // and doesn't wrap-around if interpreted as signed.
+    *sa = static_cast<int64_t>(a);
+    *sb = static_cast<int64_t>(b);
+  }
+}
+
+void Range::Ushr(const Range* left,
+                 const Range* right,
+                 RangeBoundary* result_min,
+                 RangeBoundary* result_max) {
+  const int64_t left_max = Range::ConstantMax(left).ConstantValue();
+  const int64_t left_min = Range::ConstantMin(left).ConstantValue();
+  // A negative shift count always deoptimizes (and throws), so the minimum
+  // shift count is zero.
+  const int64_t right_max = Utils::Maximum(
+      Range::ConstantMax(right).ConstantValue(), static_cast<int64_t>(0));
+  const int64_t right_min = Utils::Maximum(
+      Range::ConstantMin(right).ConstantValue(), static_cast<int64_t>(0));
+
+  uint64_t unsigned_left_min, unsigned_left_max;
+  ConvertRangeToUnsigned(left_min, left_max, &unsigned_left_min,
+                         &unsigned_left_max);
+
+  const uint64_t unsigned_result_min =
+      (right_max >= kBitsPerInt64)
+          ? 0
+          : unsigned_left_min >> static_cast<uint64_t>(right_max);
+  const uint64_t unsigned_result_max =
+      (right_min >= kBitsPerInt64)
+          ? 0
+          : unsigned_left_max >> static_cast<uint64_t>(right_min);
+
+  int64_t signed_result_min, signed_result_max;
+  ConvertRangeToSigned(unsigned_result_min, unsigned_result_max,
+                       &signed_result_min, &signed_result_max);
+
+  *result_min = RangeBoundary(signed_result_min);
+  *result_max = RangeBoundary(signed_result_max);
+}
+
 void Range::And(const Range* left_range,
                 const Range* right_range,
                 RangeBoundary* result_min,
@@ -2464,6 +2533,10 @@
       Range::Shr(left_range, right_range, &min, &max);
       break;
 
+    case Token::kUSHR:
+      Range::Ushr(left_range, right_range, &min, &max);
+      break;
+
     case Token::kBIT_AND:
       Range::And(left_range, right_range, &min, &max);
       break;
@@ -2902,8 +2975,9 @@
 void BinarySmiOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
   const Range* right_smi_range = analysis->GetSmiRange(right());
   // TODO(vegorov) completely remove this once GetSmiRange is eliminated.
-  if (op_kind() == Token::kSHR || op_kind() == Token::kSHL ||
-      op_kind() == Token::kMOD || op_kind() == Token::kTRUNCDIV) {
+  if (op_kind() == Token::kSHL || op_kind() == Token::kSHR ||
+      op_kind() == Token::kUSHR || op_kind() == Token::kMOD ||
+      op_kind() == Token::kTRUNCDIV) {
     CacheRange(&right_range_, right_smi_range,
                RangeBoundary::kRangeBoundarySmi);
   }
diff --git a/runtime/vm/compiler/backend/range_analysis.h b/runtime/vm/compiler/backend/range_analysis.h
index 0f8cc42..9a8971e 100644
--- a/runtime/vm/compiler/backend/range_analysis.h
+++ b/runtime/vm/compiler/backend/range_analysis.h
@@ -476,6 +476,11 @@
                   RangeBoundary* min,
                   RangeBoundary* max);
 
+  static void Ushr(const Range* left_range,
+                   const Range* right_range,
+                   RangeBoundary* min,
+                   RangeBoundary* max);
+
   static void Shl(const Range* left_range,
                   const Range* right_range,
                   RangeBoundary* min,
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index f81fd02..4791955 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -571,10 +571,11 @@
         return false;
       }
       break;
-    case Token::kSHR:
     case Token::kSHL:
+    case Token::kSHR:
+    case Token::kUSHR:
       if (binary_feedback.OperandsAre(kSmiCid)) {
-        // Left shift may overflow from smi into mint or big ints.
+        // Left shift may overflow from smi into mint.
         // Don't generate smi code if the IC data is marked because
         // of an overflow.
         if (call->ic_data()->HasDeoptReason(ICData::kDeoptBinaryInt64Op)) {
@@ -638,7 +639,8 @@
     ReplaceCall(call, double_bin_op);
   } else if (operands_type == kMintCid) {
     if (!FlowGraphCompiler::SupportsUnboxedInt64()) return false;
-    if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
+    if ((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+        (op_kind == Token::kUSHR)) {
       SpeculativeShiftInt64OpInstr* shift_op = new (Z)
           SpeculativeShiftInt64OpInstr(op_kind, new (Z) Value(left),
                                        new (Z) Value(right), call->deopt_id());
diff --git a/runtime/vm/compiler/method_recognizer.cc b/runtime/vm/compiler/method_recognizer.cc
index 6a6bd41..873f9e0 100644
--- a/runtime/vm/compiler/method_recognizer.cc
+++ b/runtime/vm/compiler/method_recognizer.cc
@@ -315,6 +315,8 @@
     return Token::kSHL;
   } else if (name.ptr() == Symbols::RightShiftOperator().ptr()) {
     return Token::kSHR;
+  } else if (name.ptr() == Symbols::UnsignedRightShiftOperator().ptr()) {
+    return Token::kUSHR;
   } else if (name.ptr() == Symbols::Tilde().ptr()) {
     return Token::kBIT_NOT;
   } else if (name.ptr() == Symbols::UnaryMinus().ptr()) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index bb608ff..1d8c7ac 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5952,6 +5952,21 @@
     if (type.IsNull() || type.IsNullTypeRef()) {
       return 0;  // Do not cache hash, since it will still change.
     }
+    if (type.IsTypeRef()) {
+      // Unwrapping the TypeRef here cannot lead to infinite recursion, because
+      // traversal during hash computation stops at the TypeRef. Indeed,
+      // unwrapping the TypeRef does not always remove it completely, but may
+      // only rotate the cycle. The same TypeRef can be encountered when calling
+      // type.Hash() below after traversing the whole cycle. The class id of the
+      // referenced type is used and the traversal stops.
+      // By dereferencing the TypeRef, we maximize the information reflected by
+      // the hash value. Two equal vectors may have some of their type arguments
+      // 'oriented' differently, i.e. pointing to identical (TypeRef containing)
+      // cyclic type graphs, but to two different nodes in the cycle, thereby
+      // breaking the hash computation earlier for one vector and yielding two
+      // different hash values for identical type graphs.
+      type = TypeRef::Cast(type).type();
+    }
     result = CombineHashes(result, type.Hash());
   }
   result = FinalizeHash(result, kHashBits);
@@ -20515,6 +20530,10 @@
     // Only include hashes of type arguments corresponding to type parameters.
     // This prevents obtaining different hashes depending on the location of
     // TypeRefs in the super class type argument vector.
+    // Note that TypeRefs can also appear as type arguments corresponding to
+    // type parameters, typically after an instantiation at runtime.
+    // These are dealt with in TypeArguments::HashForRange, which is also called
+    // to compute the hash of a full standalone TypeArguments.
     const TypeArguments& type_args = TypeArguments::Handle(arguments());
     const Class& cls = Class::Handle(type_class());
     const intptr_t num_type_params = cls.NumTypeParameters();
@@ -21842,6 +21861,9 @@
       return Integer::New(Utils::ShiftLeftWithTruncation(a, b), space);
     case Token::kSHR:
       return Integer::New(a >> Utils::Minimum<int64_t>(b, Mint::kBits), space);
+    case Token::kUSHR:
+      return Integer::New(
+          (b >= kBitsPerInt64) ? 0 : static_cast<uint64_t>(a) >> b, space);
     default:
       UNIMPLEMENTED();
       return Integer::null();
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 631c7a8..1646c69 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -288,6 +288,7 @@
   V(Uint8ClampedList, "Uint8ClampedList")                                      \
   V(Uint8List, "Uint8List")                                                    \
   V(UnaryMinus, "unary-")                                                      \
+  V(UnsignedRightShiftOperator, ">>>")                                         \
   V(UnhandledException, "UnhandledException")                                  \
   V(UnlinkedCall, "UnlinkedCall")                                              \
   V(UnsafeCast, "unsafeCast")                                                  \
diff --git a/runtime/vm/token.cc b/runtime/vm/token.cc
index 3038486..5208384 100644
--- a/runtime/vm/token.cc
+++ b/runtime/vm/token.cc
@@ -55,6 +55,7 @@
     case Token::kBIT_AND:
     case Token::kSHL:
     case Token::kSHR:
+    case Token::kUSHR:
       return true;
     default:
       return false;
@@ -72,6 +73,7 @@
     case Token::kBIT_AND:
     case Token::kSHL:
     case Token::kSHR:
+    case Token::kUSHR:
       return true;
     default:
       return false;
diff --git a/runtime/vm/token.h b/runtime/vm/token.h
index 844f9b9..bae0743 100644
--- a/runtime/vm/token.h
+++ b/runtime/vm/token.h
@@ -14,7 +14,7 @@
 //
 //  14  multiplicative  * / ~/ %
 //  13  additive        + -
-//  12  shift           << >>
+//  12  shift           << >> >>>
 //  11  bitwise and     &
 //  10  bitwise xor     ^
 //   9  bitwise or      |
@@ -24,7 +24,7 @@
 //   5  logical or      ||
 //   4  null check      ??
 //   3  conditional     ?
-//   2  assignment      = *= /= ~/= %= += -= <<= >>= &= ^= |= ??=
+//   2  assignment      = *= /= ~/= %= += -= <<= >>= >>>= &= ^= |= ??=
 //   1  comma           ,
 
 // Token definitions.
@@ -57,6 +57,7 @@
   TOK(kASSIGN_AND, "&=", 2, kNoAttribute)                                      \
   TOK(kASSIGN_SHL, "<<=", 2, kNoAttribute)                                     \
   TOK(kASSIGN_SHR, ">>=", 2, kNoAttribute)                                     \
+  TOK(kASSIGN_USHR, ">>>=", 2, kNoAttribute)                                   \
   TOK(kASSIGN_ADD, "+=", 2, kNoAttribute)                                      \
   TOK(kASSIGN_SUB, "-=", 2, kNoAttribute)                                      \
   TOK(kASSIGN_MUL, "*=", 2, kNoAttribute)                                      \
@@ -79,6 +80,7 @@
   /* Shift operators. */                                                       \
   TOK(kSHL, "<<", 12, kNoAttribute)                                            \
   TOK(kSHR, ">>", 12, kNoAttribute)                                            \
+  TOK(kUSHR, ">>>", 12, kNoAttribute)                                          \
                                                                                \
   /* Additive operators. */                                                    \
   TOK(kADD, "+", 13, kNoAttribute)                                             \
@@ -285,8 +287,8 @@
   static bool CanBeOverloaded(Kind tok) {
     ASSERT(tok < kNumTokens);
     return IsRelationalOperator(tok) || (tok == kEQ) ||
-           (tok >= kADD && tok <= kMOD) ||     // Arithmetic operations.
-           (tok >= kBIT_OR && tok <= kSHR) ||  // Bit operations.
+           (tok >= kADD && tok <= kMOD) ||      // Arithmetic operations.
+           (tok >= kBIT_OR && tok <= kUSHR) ||  // Bit operations.
            (tok == kINDEX) || (tok == kASSIGN_INDEX);
   }
 
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index fb9e7a9..339d9a7 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -844,7 +844,10 @@
         }
         connecting.clear();
         addressesSubscription.cancel();
-        result.complete(socket);
+        if (!result.isCompleted) {
+          // Might be already completed via onCancel
+          result.complete(socket);
+        }
       }, error: (e, st) {
         connecting.remove(socket);
         socket.close();
@@ -864,6 +867,7 @@
         s.setHandlers();
         s.setListening(read: false, write: false);
       }
+      addressesSubscription.cancel();
       connecting.clear();
       if (!result.isCompleted) {
         error ??= createError(
diff --git a/sdk/lib/_internal/vm/lib/integers.dart b/sdk/lib/_internal/vm/lib/integers.dart
index f53e439..aefb957 100644
--- a/sdk/lib/_internal/vm/lib/integers.dart
+++ b/sdk/lib/_internal/vm/lib/integers.dart
@@ -80,6 +80,8 @@
   @pragma("vm:non-nullable-result-type")
   int _shrFromInteger(int other) native "Integer_shrFromInteger";
   @pragma("vm:non-nullable-result-type")
+  int _ushrFromInteger(int other) native "Integer_ushrFromInteger";
+  @pragma("vm:non-nullable-result-type")
   int _shlFromInteger(int other) native "Integer_shlFromInteger";
   @pragma("vm:recognized", "asm-intrinsic")
   @pragma("vm:non-nullable-result-type")
@@ -106,8 +108,7 @@
   @pragma("vm:never-inline")
   int operator >>(int other) => other._shrFromInteger(this);
   @pragma("vm:never-inline")
-  int operator >>>(int other) =>
-    throw UnimplementedError('int.>>> is not implemented yet');
+  int operator >>>(int other) => other._ushrFromInteger(this);
   @pragma("vm:recognized", "asm-intrinsic")
   @pragma("vm:non-nullable-result-type")
   @pragma("vm:never-inline")
diff --git a/sdk/lib/_internal/vm/lib/integers_patch.dart b/sdk/lib/_internal/vm/lib/integers_patch.dart
index fa9cf80..8848974 100644
--- a/sdk/lib/_internal/vm/lib/integers_patch.dart
+++ b/sdk/lib/_internal/vm/lib/integers_patch.dart
@@ -15,8 +15,9 @@
   int _bitAndFromInteger(int other);
   int _bitOrFromInteger(int other);
   int _bitXorFromInteger(int other);
-  int _shrFromInteger(int other);
   int _shlFromInteger(int other);
+  int _shrFromInteger(int other);
+  int _ushrFromInteger(int other);
 
   static int? _tryParseSmi(String str, int first, int last) {
     assert(first <= last);
diff --git a/tests/corelib/unsigned_shift_test.dart b/tests/corelib/unsigned_shift_test.dart
index 457bc7c..fd52016 100644
--- a/tests/corelib/unsigned_shift_test.dart
+++ b/tests/corelib/unsigned_shift_test.dart
@@ -55,25 +55,21 @@
   }
 }
 
-int testConstantShifts() {
+void testConstantShifts() {
   const c = C();
   // >>> is a constant operation on integers.
   const c1 = 2 >>> 1;
   const c2 = (1 >>> 0) >>> 0;
-  // >>> is a potentially constant operation independent of type.
-  // The type must still type-check.
-  const c3 = false ? 1 : c >>> c;
+  const c3 = 1 >>> 65;
 
-  // It's an error if it doesn't type-check.
-  const c4 = true || c >>> c; //# 02: compile-time error
-  const c5 = true || "string" >>> 1;  //# 03: compile-time error
-
-  // Or if the shift isn't on integers and it is evaluated.
-  const c6 = c >>> c; //# 04: compile-time error
+  // >>> is a non-constant operation on other types.
+  const c4 = false ? 1 : c >>> c; //# 02: compile-time error
+  const c5 = true || c >>> c; //# 03: compile-time error
+  const c6 = true || "string" >>> 1;  //# 04: compile-time error
+  const c7 = c >>> c; //# 05: compile-time error
 
   // Or if shifting throws
-  const c7 = 1 >>> -1; //# 05: compile-time error
-  const c8 = 1 >>> 65; //# 06: compile-time error
+  const c8 = 1 >>> -1; //# 06: compile-time error
 
   Expect.isNotNull(c1 + c2 + c3);  // Avoid "unused variable" warnings.
 }
@@ -85,12 +81,7 @@
   var title = "0x${value.toRadixString(16)} >>> $shift$jsFlag";
   if (shift < 0) {
     // No platform allows shifting a negative.
-    Expect.throws(() => value >>> shift, "$title: shift < 0");
-    return;
-  }
-  if (!isJSBitOps && shift > 64) {
-    // Native 64-bit integers do not allow shifts above 64.
-    Expect.throws(() => value >>> shift, "$title: shift > 64");
+    Expect.throwsArgumentError(() => value >>> shift, "$title: shift < 0");
     return;
   }
   var expected;
@@ -98,7 +89,9 @@
     // TODO: Check that this is the desired behavior for JS >>>.
     expected = value.toUnsigned(32) >> shift;
   } else if (value < 0) {
-    if (shift > 0) {
+    if (shift >= 64) {
+      expected = 0;
+    } else if (shift > 0) {
       expected = (value >> shift).toUnsigned(64 - shift);
     } else {
       expected = value;
diff --git a/tests/corelib_2/unsigned_shift_test.dart b/tests/corelib_2/unsigned_shift_test.dart
index 457bc7c..fd52016 100644
--- a/tests/corelib_2/unsigned_shift_test.dart
+++ b/tests/corelib_2/unsigned_shift_test.dart
@@ -55,25 +55,21 @@
   }
 }
 
-int testConstantShifts() {
+void testConstantShifts() {
   const c = C();
   // >>> is a constant operation on integers.
   const c1 = 2 >>> 1;
   const c2 = (1 >>> 0) >>> 0;
-  // >>> is a potentially constant operation independent of type.
-  // The type must still type-check.
-  const c3 = false ? 1 : c >>> c;
+  const c3 = 1 >>> 65;
 
-  // It's an error if it doesn't type-check.
-  const c4 = true || c >>> c; //# 02: compile-time error
-  const c5 = true || "string" >>> 1;  //# 03: compile-time error
-
-  // Or if the shift isn't on integers and it is evaluated.
-  const c6 = c >>> c; //# 04: compile-time error
+  // >>> is a non-constant operation on other types.
+  const c4 = false ? 1 : c >>> c; //# 02: compile-time error
+  const c5 = true || c >>> c; //# 03: compile-time error
+  const c6 = true || "string" >>> 1;  //# 04: compile-time error
+  const c7 = c >>> c; //# 05: compile-time error
 
   // Or if shifting throws
-  const c7 = 1 >>> -1; //# 05: compile-time error
-  const c8 = 1 >>> 65; //# 06: compile-time error
+  const c8 = 1 >>> -1; //# 06: compile-time error
 
   Expect.isNotNull(c1 + c2 + c3);  // Avoid "unused variable" warnings.
 }
@@ -85,12 +81,7 @@
   var title = "0x${value.toRadixString(16)} >>> $shift$jsFlag";
   if (shift < 0) {
     // No platform allows shifting a negative.
-    Expect.throws(() => value >>> shift, "$title: shift < 0");
-    return;
-  }
-  if (!isJSBitOps && shift > 64) {
-    // Native 64-bit integers do not allow shifts above 64.
-    Expect.throws(() => value >>> shift, "$title: shift > 64");
+    Expect.throwsArgumentError(() => value >>> shift, "$title: shift < 0");
     return;
   }
   var expected;
@@ -98,7 +89,9 @@
     // TODO: Check that this is the desired behavior for JS >>>.
     expected = value.toUnsigned(32) >> shift;
   } else if (value < 0) {
-    if (shift > 0) {
+    if (shift >= 64) {
+      expected = 0;
+    } else if (shift > 0) {
       expected = (value >> shift).toUnsigned(64 - shift);
     } else {
       expected = value;
diff --git a/tests/language/regress/regress45060_test.dart b/tests/language/regress/regress45060_test.dart
new file mode 100644
index 0000000..1681580
--- /dev/null
+++ b/tests/language/regress/regress45060_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, 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.
+
+import "package:expect/expect.dart";
+
+void main() {
+  var executor = new Executor();
+  new Command1().execute(executor);
+  executor.execute<Command1>(new Command1());
+}
+
+class Command1 extends CommandBase<Command1> {}
+
+abstract class Command {}
+
+abstract class CommandBase<TSelf extends CommandBase<TSelf>> extends Command {
+  void execute(Executor e) {
+    TSelf self = this as TSelf;
+    e.execute<TSelf>(self);
+  }
+}
+
+abstract class CommandHandler<T extends Command> {
+  void handle(T command);
+}
+
+class Executor {
+  void execute<T extends Command>(T action) {
+    testTypeEquality<CommandHandler<T>, CommandHandler<Command1>>();
+  }
+}
+
+void testTypeEquality<T1, T2>() {
+  Expect.equals(T1.hashCode, T2.hashCode);
+  Expect.equals(T1, T2);
+  Expect.identical(T1, T2);
+}
diff --git a/tests/language_2/regress/regress45060_test.dart b/tests/language_2/regress/regress45060_test.dart
new file mode 100644
index 0000000..1681580
--- /dev/null
+++ b/tests/language_2/regress/regress45060_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, 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.
+
+import "package:expect/expect.dart";
+
+void main() {
+  var executor = new Executor();
+  new Command1().execute(executor);
+  executor.execute<Command1>(new Command1());
+}
+
+class Command1 extends CommandBase<Command1> {}
+
+abstract class Command {}
+
+abstract class CommandBase<TSelf extends CommandBase<TSelf>> extends Command {
+  void execute(Executor e) {
+    TSelf self = this as TSelf;
+    e.execute<TSelf>(self);
+  }
+}
+
+abstract class CommandHandler<T extends Command> {
+  void handle(T command);
+}
+
+class Executor {
+  void execute<T extends Command>(T action) {
+    testTypeEquality<CommandHandler<T>, CommandHandler<Command1>>();
+  }
+}
+
+void testTypeEquality<T1, T2>() {
+  Expect.equals(T1.hashCode, T2.hashCode);
+  Expect.equals(T1, T2);
+  Expect.identical(T1, T2);
+}
diff --git a/tests/lib/html/js_util_test.dart b/tests/lib/html/js_util_test.dart
index f06e729..a3d5092 100644
--- a/tests/lib/html/js_util_test.dart
+++ b/tests/lib/html/js_util_test.dart
@@ -2,75 +2,19 @@
 // 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.
 
-@JS()
-library js_native_test;
+// Tests the functionality of js_util with HTML objects.
 
-import 'dart:async';
+@JS()
+library js_util_test;
+
 import 'dart:html';
-import 'dart:typed_data' show ByteBuffer, Int32List;
-import 'dart:indexed_db' show IdbFactory, KeyRange;
 
 import 'package:js/js.dart';
 import 'package:js/js_util.dart' as js_util;
 import 'package:expect/minitest.dart';
-import 'package:async_helper/async_helper.dart';
-
-_injectJs() {
-  final script = new ScriptElement();
-  script.type = 'text/javascript';
-  script.innerHtml = r"""
-var x = 42;
-
-var _x = 123;
-
-var myArray = ["value1"];
-
-function returnThis() {
-  return this;
-}
-
-function getTypeOf(o) {
-  return typeof(o);
-}
-
-function Foo(a) {
-  this.a = a;
-}
-
-Foo.b = 38;
-
-Foo.prototype.bar = function() {
-  return this.a;
-}
-Foo.prototype.toString = function() {
-  return "I'm a Foo a=" + this.a;
-}
-
-var container = new Object();
-container.Foo = Foo;
-
-function checkMap(m, key, value) {
-  if (m.hasOwnProperty(key))
-    return m[key] == value;
-  else
-    return false;
-}
-
-var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
-var resolvedPromise = new Promise(resolve => resolve('resolved'));
-function getResolvedPromise() {
-  return resolvedPromise;
-}
-
-""";
-  document.body!.append(script);
-}
 
 @JS()
-external bool checkMap(m, String, value);
-
-@JS('JSON.stringify')
-external String stringify(o);
+external void eval(String code);
 
 @JS('Node')
 external get JSNodeType;
@@ -86,302 +30,61 @@
 
 @JS()
 class Foo {
-  external Foo(num a);
-
-  external num get a;
-  external num bar();
-}
-
-@JS('Foo')
-external get JSFooType;
-
-@JS()
-@anonymous
-class ExampleTypedLiteral {
-  external factory ExampleTypedLiteral({a, b, JS$_c, JS$class});
-
-  external get a;
-  external get b;
-  external get JS$_c;
-  external set JS$_c(v);
-  // Identical to JS$_c but only accessible within the library.
-  external get _c;
-  external get JS$class;
-  external set JS$class(v);
-}
-
-@JS()
-abstract class Promise<T> {}
-
-@JS()
-external Promise get resolvedPromise;
-
-@JS()
-external Promise get rejectedPromise;
-
-@JS()
-external Promise getResolvedPromise();
-
-@JS("Object.prototype.hasOwnProperty")
-external get _hasOwnProperty;
-
-bool hasOwnProperty(o, String name) {
-  return js_util.callMethod(_hasOwnProperty, 'call', [o, name]);
+  external Foo();
 }
 
 main() {
-  _injectJs();
+  eval(r""" function Foo() {} """);
 
-  group('js_util.jsify()', () {
-    test('convert a List', () {
-      final list = [1, 2, 3, 4, 5, 6, 7, 8];
-      var array = js_util.jsify(list);
-      expect(array is List, isTrue);
-      expect(identical(array, list), isFalse);
-      expect(array.length, equals(list.length));
-      for (var i = 0; i < list.length; i++) {
-        expect(array[i], equals(list[i]));
-      }
-    });
-
-    test('convert an Iterable', () {
-      final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
-      var array = js_util.jsify(set);
-      expect(array is List, isTrue);
-      expect(array.length, equals(set.length));
-      for (var i = 0; i < array.length; i++) {
-        expect(set.contains(array[i]), isTrue);
-      }
-    });
-
-    test('convert a Map', () {
-      var map = {'a': 1, 'b': 2, 'c': 3};
-      var jsMap = js_util.jsify(map);
-      expect(jsMap is! List, isTrue);
-      for (var key in map.keys) {
-        expect(checkMap(jsMap, key, map[key]), isTrue);
-      }
-    });
-
-    test('deep convert a complex object', () {
-      dynamic object = {
-        'a': [
-          1,
-          [2, 3]
-        ],
-        'b': {'c': 3, 'd': new Foo(42)},
-        'e': null
-      };
-      var jsObject = js_util.jsify(object);
-      expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
-      expect(
-          js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
-      expect(
-          js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
-      expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'c'),
-          equals(object['b']['c']));
-      expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
-          equals(object['b']['d']));
-      expect(
-          js_util.callMethod(
-              js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
-              'bar', []),
-          equals(42));
-      expect(js_util.getProperty(jsObject, 'e'), isNull);
-    });
-
-    test('throws if object is not a Map or Iterable', () {
-      expect(() => js_util.jsify('a'), throwsArgumentError);
-    });
+  test('hasProperty', () {
+    var textElement = new Text('foo');
+    expect(js_util.hasProperty(textElement, 'data'), isTrue);
   });
 
-  group('js_util.newObject', () {
-    test('create', () {
-      expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
-    });
-
-    test('callMethod', () {
-      var o = js_util.newObject();
-      expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
-      expect(stringify(o), equals('{}'));
-    });
-
-    test('properties', () {
-      var o = js_util.newObject();
-      expect(js_util.hasProperty(o, 'foo bar'), isFalse);
-      expect(js_util.hasProperty(o, 'toString'), isTrue);
-      expect(hasOwnProperty(o, 'toString'), isFalse);
-      expect(hasOwnProperty(o, 'foo bar'), isFalse);
-      js_util.setProperty(o, 'foo bar', 42);
-      expect(hasOwnProperty(o, 'foo bar'), isTrue);
-      expect(js_util.getProperty(o, 'foo bar'), equals(42));
-      expect(js_util.hasProperty(o, 'foo bar'), isTrue);
-      expect(stringify(o), equals('{"foo bar":42}'));
-    });
+  test('getProperty', () {
+    var textElement = new Text('foo');
+    expect(js_util.getProperty(textElement, 'data'), equals('foo'));
   });
 
-  group('hasProperty', () {
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.hasProperty(f, 'a'), isTrue);
-      expect(js_util.hasProperty(f, 'toString'), isTrue);
-      js_util.setProperty(f, '__proto__', null);
-      expect(js_util.hasProperty(f, 'toString'), isFalse);
-    });
-    test('typed literal', () {
-      var l =
-          new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: null, JS$class: true);
-      expect(js_util.hasProperty(l, 'a'), isTrue);
-      expect(js_util.hasProperty(l, 'b'), isTrue);
-      expect(js_util.hasProperty(l, '_c'), isTrue);
-      expect(l.JS$_c, isNull);
-      expect(js_util.hasProperty(l, 'class'), isTrue);
-      // JS$_c escapes to _c so the property JS$_c will not exist on the object.
-      expect(js_util.hasProperty(l, r'JS$_c'), isFalse);
-      expect(js_util.hasProperty(l, r'JS$class'), isFalse);
-      expect(l.JS$class, isTrue);
-
-      l = new ExampleTypedLiteral(a: null);
-      expect(js_util.hasProperty(l, 'a'), isTrue);
-      expect(js_util.hasProperty(l, 'b'), isFalse);
-      expect(js_util.hasProperty(l, '_c'), isFalse);
-      expect(js_util.hasProperty(l, 'class'), isFalse);
-
-      l = new ExampleTypedLiteral(JS$_c: 74);
-      expect(js_util.hasProperty(l, '_c'), isTrue);
-      expect(l.JS$_c, equals(74));
-    });
+  test('setProperty', () {
+    var textElement = new Text('foo');
+    js_util.setProperty(textElement, 'data', 'bar');
+    expect(textElement.text, equals('bar'));
   });
 
-  group('getProperty', () {
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.getProperty(f, 'a'), equals(42));
-      expect(js_util.getProperty(f, 'toString') is Function, isTrue);
-      js_util.setProperty(f, '__proto__', null);
-      expect(js_util.getProperty(f, 'toString'), isNull);
-    });
-
-    test('typed literal', () {
-      var l = new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: 7, JS$class: true);
-      expect(js_util.getProperty(l, 'a'), equals('x'));
-      expect(js_util.getProperty(l, 'b'), equals(42));
-      expect(js_util.getProperty(l, '_c'), equals(7));
-      expect(l.JS$_c, equals(7));
-      expect(js_util.getProperty(l, 'class'), isTrue);
-      expect(js_util.getProperty(l, r'JS$_c'), isNull);
-      expect(js_util.getProperty(l, r'JS$class'), isNull);
-    });
+  test('callMethod', () {
+    var canvas = new CanvasElement();
+    expect(
+        identical(canvas.getContext('2d'),
+            js_util.callMethod(canvas, 'getContext', ['2d'])),
+        isTrue);
   });
 
-  group('setProperty', () {
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.getProperty(f, 'a'), equals(42));
-      js_util.setProperty(f, 'a', 100);
-      expect(f.a, equals(100));
-      expect(js_util.getProperty(f, 'a'), equals(100));
-    });
+  test('instanceof', () {
+    var canvas = new Element.tag('canvas');
+    expect(js_util.instanceof(canvas, JSNodeType), isTrue);
+    expect(js_util.instanceof(canvas, JSTextType), isFalse);
+    expect(js_util.instanceof(canvas, JSElementType), isTrue);
+    expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
+    var div = new Element.tag('div');
+    expect(js_util.instanceof(div, JSNodeType), isTrue);
+    expect(js_util.instanceof(div, JSTextType), isFalse);
+    expect(js_util.instanceof(div, JSElementType), isTrue);
+    expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
 
-    test('typed literal', () {
-      var l = new ExampleTypedLiteral();
-      js_util.setProperty(l, 'a', 'foo');
-      expect(js_util.getProperty(l, 'a'), equals('foo'));
-      expect(l.a, equals('foo'));
-      js_util.setProperty(l, 'a', l);
-      expect(identical(l.a, l), isTrue);
-      var list = ['arr'];
-      js_util.setProperty(l, 'a', list);
-      expect(identical(l.a, list), isTrue);
-      l.JS$class = 42;
-      expect(l.JS$class, equals(42));
-      js_util.setProperty(l, 'class', 100);
-      expect(l.JS$class, equals(100));
-    });
+    var text = new Text('foo');
+    expect(js_util.instanceof(text, JSNodeType), isTrue);
+    expect(js_util.instanceof(text, JSTextType), isTrue);
+    expect(js_util.instanceof(text, JSElementType), isFalse);
+
+    var f = new Foo();
+    expect(js_util.instanceof(f, JSNodeType), isFalse);
   });
 
-  group('callMethod', () {
-    test('html object', () {
-      var canvas = new CanvasElement();
-      expect(
-          identical(canvas.getContext('2d'),
-              js_util.callMethod(canvas, 'getContext', ['2d'])),
-          isTrue);
-    });
-
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.callMethod(f, 'bar', []), equals(42));
-    });
-  });
-
-  group('instanceof', () {
-    test('html object', () {
-      var canvas = new Element.tag('canvas');
-      expect(js_util.instanceof(canvas, JSNodeType), isTrue);
-      expect(js_util.instanceof(canvas, JSTextType), isFalse);
-      expect(js_util.instanceof(canvas, JSElementType), isTrue);
-      expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
-      var div = new Element.tag('div');
-      expect(js_util.instanceof(div, JSNodeType), isTrue);
-      expect(js_util.instanceof(div, JSTextType), isFalse);
-      expect(js_util.instanceof(div, JSElementType), isTrue);
-      expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
-
-      var text = new Text('foo');
-      expect(js_util.instanceof(text, JSNodeType), isTrue);
-      expect(js_util.instanceof(text, JSTextType), isTrue);
-      expect(js_util.instanceof(text, JSElementType), isFalse);
-    });
-
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.instanceof(f, JSFooType), isTrue);
-      expect(js_util.instanceof(f, JSNodeType), isFalse);
-    });
-
-    test('typed literal', () {
-      var l = new ExampleTypedLiteral();
-      expect(js_util.instanceof(l, JSFooType), isFalse);
-    });
-  });
-
-  group('callConstructor', () {
-    test('html object', () {
-      var textNode = js_util.callConstructor(JSTextType, ['foo']);
-      expect(js_util.instanceof(textNode, JSTextType), isTrue);
-      expect(textNode is Text, isTrue);
-      expect(textNode.text, equals('foo'));
-    });
-
-    test('typed object', () {
-      Foo f = js_util.callConstructor(JSFooType, [42]);
-      expect(f.a, equals(42));
-    });
-  });
-
-  Future<void> testResolvedPromise() async {
-    final String result = await js_util.promiseToFuture(resolvedPromise);
-    expect(result, equals('resolved'));
-  }
-
-  Future<void> testRejectedPromise() async {
-    try {
-      final String result = await promiseToFuture(rejectedPromise);
-      fail('expected Future to throw an error');
-    } catch (error) {
-      expect(error, equals('rejected'));
-    }
-  }
-
-  Future<void> testReturnRejectedPromise() async {
-    final String result = await promiseToFuture(getResolvedPromise());
-    expect(result, equals('resolved'));
-  }
-
-  asyncTest(() async {
-    await testResolvedPromise();
-    await testRejectedPromise();
-    await testReturnRejectedPromise();
+  test('callConstructor', () {
+    var textNode = js_util.callConstructor(JSTextType, ['foo']);
+    expect(js_util.instanceof(textNode, JSTextType), isTrue);
+    expect(textNode is Text, isTrue);
+    expect(textNode.text, equals('foo'));
   });
 }
diff --git a/tests/lib/js/js_util/async_test.dart b/tests/lib/js/js_util/async_test.dart
new file mode 100644
index 0000000..9172fe7
--- /dev/null
+++ b/tests/lib/js/js_util/async_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2021, 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.
+
+@JS()
+library js_util_async_test;
+
+import 'dart:async';
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+import 'package:async_helper/async_helper.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+abstract class Promise<T> {}
+
+@JS()
+external Promise get resolvedPromise;
+
+@JS()
+external Promise get rejectedPromise;
+
+@JS()
+external Promise getResolvedPromise();
+
+main() {
+  eval(r"""
+    var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
+    var resolvedPromise = new Promise(resolve => resolve('resolved'));
+    function getResolvedPromise() {
+      return resolvedPromise;
+    }
+    """);
+
+  Future<void> testResolvedPromise() async {
+    final String result = await js_util.promiseToFuture(resolvedPromise);
+    expect(result, equals('resolved'));
+  }
+
+  Future<void> testRejectedPromise() async {
+    asyncExpectThrows<String>(() => js_util.promiseToFuture(rejectedPromise),
+        (String error) => error == 'rejected');
+  }
+
+  Future<void> testReturnResolvedPromise() async {
+    final String result = await js_util.promiseToFuture(getResolvedPromise());
+    expect(result, equals('resolved'));
+  }
+
+  asyncTest(() async {
+    await testResolvedPromise();
+    await testRejectedPromise();
+    await testReturnResolvedPromise();
+  });
+}
diff --git a/tests/lib/js/js_util/js_prefix_test.dart b/tests/lib/js/js_util/js_prefix_test.dart
new file mode 100644
index 0000000..47205cb
--- /dev/null
+++ b/tests/lib/js/js_util/js_prefix_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2021, 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 the functionality of the JS$ prefix for escaping keywords in JS names.
+// Currently only implemented in dart2js, expected to fail in ddc.
+
+@JS()
+library js_prefix_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+  external factory ExampleTypedLiteral({JS$_c, JS$class});
+
+  external get JS$_c;
+  external set JS$_c(v);
+  // Identical to JS$_c but only accessible within the library.
+  external get _c;
+  external get JS$class;
+  external set JS$class(v);
+}
+
+main() {
+  test('hasProperty', () {
+    var literal = new ExampleTypedLiteral(JS$_c: null, JS$class: true);
+    expect(js_util.hasProperty(literal, '_c'), isTrue);
+    expect(literal.JS$_c, isNull);
+    expect(js_util.hasProperty(literal, 'class'), isTrue);
+    // JS$_c escapes to _c so the property JS$_c will not exist on the object.
+    expect(js_util.hasProperty(literal, r'JS$_c'), isFalse);
+    expect(js_util.hasProperty(literal, r'JS$class'), isFalse);
+    expect(literal.JS$class, isTrue);
+
+    literal = new ExampleTypedLiteral();
+    expect(js_util.hasProperty(literal, '_c'), isFalse);
+    expect(js_util.hasProperty(literal, 'class'), isFalse);
+
+    literal = new ExampleTypedLiteral(JS$_c: 74);
+    expect(js_util.hasProperty(literal, '_c'), isTrue);
+    expect(literal.JS$_c, equals(74));
+  });
+
+  test('getProperty', () {
+    var literal = new ExampleTypedLiteral(JS$_c: 7, JS$class: true);
+    expect(js_util.getProperty(literal, '_c'), equals(7));
+    expect(literal.JS$_c, equals(7));
+    expect(js_util.getProperty(literal, 'class'), isTrue);
+    expect(js_util.getProperty(literal, r'JS$_c'), isNull);
+    expect(js_util.getProperty(literal, r'JS$class'), isNull);
+  });
+
+  test('setProperty', () {
+    var literal = new ExampleTypedLiteral();
+    literal.JS$class = 42;
+    expect(literal.JS$class, equals(42));
+    js_util.setProperty(literal, 'class', 100);
+    expect(literal.JS$class, equals(100));
+  });
+}
diff --git a/tests/lib/js/js_util/jsify_test.dart b/tests/lib/js/js_util/jsify_test.dart
new file mode 100644
index 0000000..5ed916d
--- /dev/null
+++ b/tests/lib/js/js_util/jsify_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, 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 the jsify functionality of the js_util library.
+
+@JS()
+library js_util_jsify_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+external bool checkMap(m, String, value);
+
+@JS()
+class Foo {
+  external Foo(num a);
+
+  external num get a;
+  external num bar();
+}
+
+main() {
+  eval(r"""
+    function Foo(a) {
+      this.a = a;
+    }
+
+    Foo.prototype.bar = function() {
+      return this.a;
+    }
+
+    function checkMap(m, key, value) {
+      if (m.hasOwnProperty(key))
+        return m[key] == value;
+      else
+        return false;
+    }
+    """);
+
+  test('convert a List', () {
+    final list = [1, 2, 3, 4, 5, 6, 7, 8];
+    var array = js_util.jsify(list);
+    expect(array is List, isTrue);
+    expect(identical(array, list), isFalse);
+    expect(array.length, equals(list.length));
+    for (var i = 0; i < list.length; i++) {
+      expect(array[i], equals(list[i]));
+    }
+  });
+
+  test('convert an Iterable', () {
+    final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
+    var array = js_util.jsify(set);
+    expect(array is List, isTrue);
+    expect(array.length, equals(set.length));
+    for (var i = 0; i < array.length; i++) {
+      expect(set.contains(array[i]), isTrue);
+    }
+  });
+
+  test('convert a Map', () {
+    var map = {'a': 1, 'b': 2, 'c': 3};
+    var jsMap = js_util.jsify(map);
+    expect(jsMap is List, isFalse);
+    expect(jsMap is Map, isFalse);
+    for (var key in map.keys) {
+      expect(checkMap(jsMap, key, map[key]), isTrue);
+    }
+  });
+
+  test('deep convert a complex object', () {
+    dynamic object = {
+      'a': [
+        1,
+        [2, 3]
+      ],
+      'b': {'c': 3, 'd': new Foo(42)},
+      'e': null
+    };
+    var jsObject = js_util.jsify(object);
+    expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
+    expect(js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
+    expect(js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
+
+    var b = js_util.getProperty(jsObject, 'b');
+    expect(js_util.getProperty(b, 'c'), equals(object['b']['c']));
+    var d = js_util.getProperty(b, 'd');
+    expect(d, equals(object['b']['d']));
+    expect(js_util.getProperty(d, 'a'), equals(42));
+    expect(js_util.callMethod(d, 'bar', []), equals(42));
+
+    expect(js_util.getProperty(jsObject, 'e'), isNull);
+  });
+
+  test('throws if object is not a Map or Iterable', () {
+    expect(() => js_util.jsify('a'), throwsArgumentError);
+  });
+}
diff --git a/tests/lib/js/js_util/properties_test.dart b/tests/lib/js/js_util/properties_test.dart
new file mode 100644
index 0000000..986c431
--- /dev/null
+++ b/tests/lib/js/js_util/properties_test.dart
@@ -0,0 +1,368 @@
+// Copyright (c) 2021, 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 the functionality of object properties with the js_util library. For
+// js_util tests with HTML objects see tests/lib/html/js_util_test.dart.
+
+@JS()
+library js_util_properties_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external String jsFunction();
+
+@JS()
+external void eval(String code);
+
+@JS('JSON.stringify')
+external String stringify(o);
+
+@JS('ArrayBuffer')
+external get JSArrayBufferType;
+
+@JS()
+class Foo {
+  external Foo(num a);
+
+  external num get a;
+  external num bar();
+  external Object get objectProperty;
+}
+
+@JS('Foo')
+external get JSFooType;
+
+String dartFunction() {
+  return 'Dart Function';
+}
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+  external factory ExampleTypedLiteral({a, b});
+
+  external get a;
+  external get b;
+}
+
+String _getBarWithSideEffect() {
+  var x = 5;
+  expect(x, equals(5));
+  return 'bar';
+}
+
+main() {
+  eval(r"""
+    function Foo(a) {
+      this.a = a;
+    }
+
+    Foo.b = 38;
+
+    Foo.prototype.list = [2, 4, 6];
+
+    Foo.prototype.bar = function() {
+      return this.a;
+    }
+
+    Foo.prototype.toString = function() {
+      return "I'm a Foo a=" + this.a;
+    }
+
+    Foo.prototype.fnList = [Foo.prototype.bar, Foo.prototype.toString];
+
+    Foo.prototype.objectProperty = {
+      'c': 1,
+      'list': [10, 20, 30],
+      'functionProperty': function() { return 'Function Property'; }
+    }
+
+    function jsFunction() {
+      return "JS Function";
+    }
+
+    Foo.prototype.nestedFunction = function() {
+      return function() {
+        return 'Nested Function';
+      };
+    }
+
+    Foo.prototype.getFirstEl = function(list) {
+      return list[0];
+    }
+
+    Foo.prototype.sumFn = function(a, b) {
+      return a + b;
+    }
+
+    Foo.prototype.getA = function(obj) {
+      return obj.a;
+    }
+
+    Foo.prototype.callFn = function(fn) {
+      return fn();
+    }
+    """);
+
+  group('newObject', () {
+    test('create', () {
+      expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
+    });
+
+    test('callMethod', () {
+      var o = js_util.newObject();
+      expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
+      expect(stringify(o), equals('{}'));
+    });
+
+    test('properties', () {
+      var o = js_util.newObject();
+      expect(js_util.hasProperty(o, 'foo bar'), isFalse);
+      expect(js_util.hasProperty(o, 'toString'), isTrue);
+      expect(js_util.callMethod(o, 'hasOwnProperty', ['toString']), isFalse);
+      expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isFalse);
+      js_util.setProperty(o, 'foo bar', 42);
+      expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isTrue);
+      expect(js_util.getProperty(o, 'foo bar'), equals(42));
+      expect(js_util.hasProperty(o, 'foo bar'), isTrue);
+      expect(stringify(o), equals('{"foo bar":42}'));
+    });
+
+    test('nested properties calls', () {
+      var o = js_util.newObject();
+      var f = new Foo(42);
+      js_util.setProperty(o, 'foo', f);
+      var foo = js_util.getProperty(o, 'foo');
+      expect(foo, equals(f));
+      expect(js_util.hasProperty(foo, 'a'), isTrue);
+      expect(js_util.getProperty(foo, 'a'), equals(42));
+      js_util.setProperty(foo, 'a', 24);
+      expect(js_util.getProperty(foo, 'a'), equals(24));
+    });
+  });
+
+  group('hasProperty', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.hasProperty(f, 'a'), isTrue);
+      expect(js_util.hasProperty(f, 'bar'), isTrue);
+      expect(js_util.hasProperty(f, 'b'), isFalse);
+      expect(js_util.hasProperty(f, 'toString'), isTrue);
+      js_util.setProperty(f, '__proto__', null);
+      expect(js_util.hasProperty(f, 'toString'), isFalse);
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+      expect(js_util.hasProperty(literal, 'a'), isTrue);
+      expect(js_util.hasProperty(literal, 'b'), isTrue);
+      expect(js_util.hasProperty(literal, 'anything'), isFalse);
+
+      literal = new ExampleTypedLiteral(a: null);
+      expect(js_util.hasProperty(literal, 'a'), isTrue);
+      expect(js_util.hasProperty(literal, 'b'), isFalse);
+      expect(js_util.hasProperty(literal, 'anything'), isFalse);
+    });
+
+    test('complex hasProperty calls', () {
+      var f = new Foo(42);
+      expect(js_util.hasProperty(f.objectProperty, 'c'), isTrue);
+      expect(js_util.hasProperty(f.objectProperty, 'nonexistent'), isFalse);
+
+      // Using a variable for the property name.
+      String propertyName = 'bar';
+      expect(js_util.hasProperty(f, propertyName), isTrue);
+    });
+  });
+
+  group('getProperty', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.getProperty(f, 'a'), equals(42));
+      expect(js_util.getProperty(f, 'b'), isNull);
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.getProperty(f, 'list') is List, isTrue);
+      expect(js_util.getProperty(f, 'objectProperty') is Object, isTrue);
+      expect(js_util.getProperty(f, 'toString') is Function, isTrue);
+      js_util.setProperty(f, '__proto__', null);
+      expect(js_util.getProperty(f, 'toString'), isNull);
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+      expect(js_util.getProperty(literal, 'a'), equals('x'));
+      expect(js_util.getProperty(literal, 'b'), equals(42));
+      expect(js_util.getProperty(literal, 'anything'), isNull);
+    });
+
+    test('complex getProperty calls', () {
+      var f = new Foo(42);
+
+      // Accessing a method property.
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.callMethod(f, 'bar', []), equals(42));
+
+      // Accessing list properties.
+      expect(js_util.getProperty(f, 'list')[0], equals(2));
+      expect(js_util.getProperty(f, 'fnList')[0] is Function, isTrue);
+      expect(
+          js_util.callMethod(
+              js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]),
+          equals(42));
+
+      // Accessing nested object properites.
+      var objectProperty = js_util.getProperty(f, 'objectProperty');
+      expect(js_util.getProperty(objectProperty, 'c'), equals(1));
+      expect(js_util.getProperty(objectProperty, 'list') is List, isTrue);
+      expect(js_util.getProperty(objectProperty, 'list')[1], equals(20));
+      expect(
+          js_util.getProperty(objectProperty, 'functionProperty') is Function,
+          isTrue);
+
+      // Using a variable for the property name.
+      String propertyName = 'a';
+      expect(js_util.getProperty(f, propertyName), equals(42));
+      String bar = _getBarWithSideEffect();
+      expect(js_util.getProperty(f, bar) is Function, isTrue);
+      expect(js_util.callMethod(f, bar, []), equals(42));
+    });
+  });
+
+  group('setProperty', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.getProperty(f, 'a'), equals(42));
+      js_util.setProperty(f, 'a', 100);
+      expect(f.a, equals(100));
+      expect(js_util.getProperty(f, 'a'), equals(100));
+
+      expect(js_util.getProperty(f, 'list') is List, isTrue);
+      js_util.setProperty(f, 'list', [8]);
+      expect(js_util.getProperty(f, 'list') is List, isTrue);
+      expect(js_util.getProperty(f, 'list')[0], equals(8));
+
+      js_util.setProperty(f, 'newProperty', 'new');
+      expect(js_util.getProperty(f, 'newProperty'), equals('new'));
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral();
+      js_util.setProperty(literal, 'a', 'foo');
+      expect(js_util.getProperty(literal, 'a'), equals('foo'));
+      expect(literal.a, equals('foo'));
+      js_util.setProperty(literal, 'a', literal);
+      expect(identical(literal.a, literal), isTrue);
+      var list = ['arr'];
+      js_util.setProperty(literal, 'a', list);
+      expect(identical(literal.a, list), isTrue);
+    });
+
+    test('complex setProperty calls', () {
+      var f = new Foo(42);
+
+      // Set function property to a num.
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      js_util.setProperty(f, 'bar', 5);
+      expect(js_util.getProperty(f, 'bar') is Function, isFalse);
+      expect(js_util.getProperty(f, 'bar'), equals(5));
+
+      // Set property to a Dart function.
+      js_util.setProperty(f, 'bar', allowInterop(dartFunction));
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.callMethod(f, 'bar', []), equals('Dart Function'));
+      js_util.setProperty(f, 'bar', allowInterop(() {
+        return 'Inline';
+      }));
+      expect(js_util.callMethod(f, 'bar', []), equals('Inline'));
+
+      // Set property to a JS function.
+      js_util.setProperty(f, 'bar', allowInterop(jsFunction));
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.callMethod(f, 'bar', []), equals('JS Function'));
+
+      // Set property with nested object properties.
+      js_util.setProperty(f.objectProperty, 'c', 'new val');
+      expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val'));
+      js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]);
+      expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2));
+
+      // Using a variable for the property name.
+      String propertyName = 'bar';
+      js_util.setProperty(f, propertyName, 'foo');
+      expect(js_util.getProperty(f, 'bar'), equals('foo'));
+      String bar = _getBarWithSideEffect();
+      js_util.setProperty(f, bar, 'baz');
+      expect(js_util.getProperty(f, bar), equals('baz'));
+    });
+  });
+
+  group('callMethod', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.callMethod(f, 'bar', []), equals(42));
+    });
+
+    test('complex callMethod calls', () {
+      var f = new Foo(42);
+
+      // Call a method that returns an unbound function.
+      expect(js_util.callMethod(f, 'nestedFunction', []) is Function, isTrue);
+      expect(js_util.callMethod(f, 'nestedFunction', [])(),
+          equals('Nested Function'));
+
+      // Call method on a nested function property.
+      expect(js_util.callMethod(f.objectProperty, 'functionProperty', []),
+          equals('Function Property'));
+
+      // Call method with different args.
+      expect(
+          js_util.callMethod(f, 'getFirstEl', [
+            [25, 50]
+          ]),
+          equals(25));
+      expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5));
+      expect(js_util.callMethod(f, 'getA', [f]), equals(42));
+      expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]),
+          equals("JS Function"));
+      expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]),
+          equals("Dart Function"));
+      expect(
+          js_util.callMethod(f, 'callFn', [
+            allowInterop(() {
+              return "inline";
+            })
+          ]),
+          equals("inline"));
+
+      // Using a variable for the method name.
+      String methodName = 'bar';
+      expect(js_util.callMethod(f, methodName, []), equals(42));
+      String bar = _getBarWithSideEffect();
+      expect(js_util.callMethod(f, bar, []), equals(42));
+    });
+  });
+
+  group('instanceof', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.instanceof(f, JSFooType), isTrue);
+      expect(js_util.instanceof(f, JSArrayBufferType), isFalse);
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral();
+      expect(js_util.instanceof(literal, JSFooType), isFalse);
+    });
+  });
+
+  group('callConstructor', () {
+    test('typed object', () {
+      Foo f = js_util.callConstructor(JSFooType, [42]);
+      expect(f.a, equals(42));
+    });
+  });
+}
diff --git a/tests/lib/lib_dartdevc.status b/tests/lib/lib_dartdevc.status
index a8a3790..1fb6c51 100644
--- a/tests/lib/lib_dartdevc.status
+++ b/tests/lib/lib_dartdevc.status
@@ -37,6 +37,7 @@
 html/custom_elements_test: Skip # Issue 29922
 html/notification_permission_test: Skip # Issue 32002
 isolate/*: SkipByDesign # No support for dart:isolate in dart4web (http://dartbug.com/30538)
+js/js_util/js_prefix_test: SkipByDesign # JS$ prefix not implemented on ddc.
 mirrors/*: SkipByDesign # Mirrors not supported on web in Dart 2.0.
 typed_data/int64_list_load_store_test: SkipByDesign # dartdevk/c does not support Int64List
 typed_data/typed_data_hierarchy_int64_test: SkipByDesign # dartdevk/c does not support Int64List
diff --git a/tests/lib_2/html/js_util_test.dart b/tests/lib_2/html/js_util_test.dart
index d32df10..a3d5092 100644
--- a/tests/lib_2/html/js_util_test.dart
+++ b/tests/lib_2/html/js_util_test.dart
@@ -2,75 +2,19 @@
 // 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.
 
-@JS()
-library js_native_test;
+// Tests the functionality of js_util with HTML objects.
 
-import 'dart:async';
+@JS()
+library js_util_test;
+
 import 'dart:html';
-import 'dart:typed_data' show ByteBuffer, Int32List;
-import 'dart:indexed_db' show IdbFactory, KeyRange;
 
 import 'package:js/js.dart';
 import 'package:js/js_util.dart' as js_util;
 import 'package:expect/minitest.dart';
-import 'package:async_helper/async_helper.dart';
-
-_injectJs() {
-  final script = new ScriptElement();
-  script.type = 'text/javascript';
-  script.innerHtml = r"""
-var x = 42;
-
-var _x = 123;
-
-var myArray = ["value1"];
-
-function returnThis() {
-  return this;
-}
-
-function getTypeOf(o) {
-  return typeof(o);
-}
-
-function Foo(a) {
-  this.a = a;
-}
-
-Foo.b = 38;
-
-Foo.prototype.bar = function() {
-  return this.a;
-}
-Foo.prototype.toString = function() {
-  return "I'm a Foo a=" + this.a;
-}
-
-var container = new Object();
-container.Foo = Foo;
-
-function checkMap(m, key, value) {
-  if (m.hasOwnProperty(key))
-    return m[key] == value;
-  else
-    return false;
-}
-
-var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
-var resolvedPromise = new Promise(resolve => resolve('resolved'));
-function getResolvedPromise() {
-  return resolvedPromise;
-}
-
-""";
-  document.body.append(script);
-}
 
 @JS()
-external bool checkMap(m, String, value);
-
-@JS('JSON.stringify')
-external String stringify(o);
+external void eval(String code);
 
 @JS('Node')
 external get JSNodeType;
@@ -86,302 +30,61 @@
 
 @JS()
 class Foo {
-  external Foo(num a);
-
-  external num get a;
-  external num bar();
-}
-
-@JS('Foo')
-external get JSFooType;
-
-@JS()
-@anonymous
-class ExampleTypedLiteral {
-  external factory ExampleTypedLiteral({a, b, JS$_c, JS$class});
-
-  external get a;
-  external get b;
-  external get JS$_c;
-  external set JS$_c(v);
-  // Identical to JS$_c but only accessible within the library.
-  external get _c;
-  external get JS$class;
-  external set JS$class(v);
-}
-
-@JS()
-abstract class Promise<T> {}
-
-@JS()
-external Promise get resolvedPromise;
-
-@JS()
-external Promise get rejectedPromise;
-
-@JS()
-external Promise getResolvedPromise();
-
-@JS("Object.prototype.hasOwnProperty")
-external get _hasOwnProperty;
-
-bool hasOwnProperty(o, String name) {
-  return js_util.callMethod(_hasOwnProperty, 'call', [o, name]);
+  external Foo();
 }
 
 main() {
-  _injectJs();
+  eval(r""" function Foo() {} """);
 
-  group('js_util.jsify()', () {
-    test('convert a List', () {
-      final list = [1, 2, 3, 4, 5, 6, 7, 8];
-      var array = js_util.jsify(list);
-      expect(array is List, isTrue);
-      expect(identical(array, list), isFalse);
-      expect(array.length, equals(list.length));
-      for (var i = 0; i < list.length; i++) {
-        expect(array[i], equals(list[i]));
-      }
-    });
-
-    test('convert an Iterable', () {
-      final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
-      var array = js_util.jsify(set);
-      expect(array is List, isTrue);
-      expect(array.length, equals(set.length));
-      for (var i = 0; i < array.length; i++) {
-        expect(set.contains(array[i]), isTrue);
-      }
-    });
-
-    test('convert a Map', () {
-      var map = {'a': 1, 'b': 2, 'c': 3};
-      var jsMap = js_util.jsify(map);
-      expect(jsMap is! List, isTrue);
-      for (var key in map.keys) {
-        expect(checkMap(jsMap, key, map[key]), isTrue);
-      }
-    });
-
-    test('deep convert a complex object', () {
-      dynamic object = {
-        'a': [
-          1,
-          [2, 3]
-        ],
-        'b': {'c': 3, 'd': new Foo(42)},
-        'e': null
-      };
-      var jsObject = js_util.jsify(object);
-      expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
-      expect(
-          js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
-      expect(
-          js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
-      expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'c'),
-          equals(object['b']['c']));
-      expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
-          equals(object['b']['d']));
-      expect(
-          js_util.callMethod(
-              js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
-              'bar', []),
-          equals(42));
-      expect(js_util.getProperty(jsObject, 'e'), isNull);
-    });
-
-    test('throws if object is not a Map or Iterable', () {
-      expect(() => js_util.jsify('a'), throwsArgumentError);
-    });
+  test('hasProperty', () {
+    var textElement = new Text('foo');
+    expect(js_util.hasProperty(textElement, 'data'), isTrue);
   });
 
-  group('js_util.newObject', () {
-    test('create', () {
-      expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
-    });
-
-    test('callMethod', () {
-      var o = js_util.newObject();
-      expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
-      expect(stringify(o), equals('{}'));
-    });
-
-    test('properties', () {
-      var o = js_util.newObject();
-      expect(js_util.hasProperty(o, 'foo bar'), isFalse);
-      expect(js_util.hasProperty(o, 'toString'), isTrue);
-      expect(hasOwnProperty(o, 'toString'), isFalse);
-      expect(hasOwnProperty(o, 'foo bar'), isFalse);
-      js_util.setProperty(o, 'foo bar', 42);
-      expect(hasOwnProperty(o, 'foo bar'), isTrue);
-      expect(js_util.getProperty(o, 'foo bar'), equals(42));
-      expect(js_util.hasProperty(o, 'foo bar'), isTrue);
-      expect(stringify(o), equals('{"foo bar":42}'));
-    });
+  test('getProperty', () {
+    var textElement = new Text('foo');
+    expect(js_util.getProperty(textElement, 'data'), equals('foo'));
   });
 
-  group('hasProperty', () {
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.hasProperty(f, 'a'), isTrue);
-      expect(js_util.hasProperty(f, 'toString'), isTrue);
-      js_util.setProperty(f, '__proto__', null);
-      expect(js_util.hasProperty(f, 'toString'), isFalse);
-    });
-    test('typed literal', () {
-      var l =
-          new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: null, JS$class: true);
-      expect(js_util.hasProperty(l, 'a'), isTrue);
-      expect(js_util.hasProperty(l, 'b'), isTrue);
-      expect(js_util.hasProperty(l, '_c'), isTrue);
-      expect(l.JS$_c, isNull);
-      expect(js_util.hasProperty(l, 'class'), isTrue);
-      // JS$_c escapes to _c so the property JS$_c will not exist on the object.
-      expect(js_util.hasProperty(l, r'JS$_c'), isFalse);
-      expect(js_util.hasProperty(l, r'JS$class'), isFalse);
-      expect(l.JS$class, isTrue);
-
-      l = new ExampleTypedLiteral(a: null);
-      expect(js_util.hasProperty(l, 'a'), isTrue);
-      expect(js_util.hasProperty(l, 'b'), isFalse);
-      expect(js_util.hasProperty(l, '_c'), isFalse);
-      expect(js_util.hasProperty(l, 'class'), isFalse);
-
-      l = new ExampleTypedLiteral(JS$_c: 74);
-      expect(js_util.hasProperty(l, '_c'), isTrue);
-      expect(l.JS$_c, equals(74));
-    });
+  test('setProperty', () {
+    var textElement = new Text('foo');
+    js_util.setProperty(textElement, 'data', 'bar');
+    expect(textElement.text, equals('bar'));
   });
 
-  group('getProperty', () {
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.getProperty(f, 'a'), equals(42));
-      expect(js_util.getProperty(f, 'toString') is Function, isTrue);
-      js_util.setProperty(f, '__proto__', null);
-      expect(js_util.getProperty(f, 'toString'), isNull);
-    });
-
-    test('typed literal', () {
-      var l = new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: 7, JS$class: true);
-      expect(js_util.getProperty(l, 'a'), equals('x'));
-      expect(js_util.getProperty(l, 'b'), equals(42));
-      expect(js_util.getProperty(l, '_c'), equals(7));
-      expect(l.JS$_c, equals(7));
-      expect(js_util.getProperty(l, 'class'), isTrue);
-      expect(js_util.getProperty(l, r'JS$_c'), isNull);
-      expect(js_util.getProperty(l, r'JS$class'), isNull);
-    });
+  test('callMethod', () {
+    var canvas = new CanvasElement();
+    expect(
+        identical(canvas.getContext('2d'),
+            js_util.callMethod(canvas, 'getContext', ['2d'])),
+        isTrue);
   });
 
-  group('setProperty', () {
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.getProperty(f, 'a'), equals(42));
-      js_util.setProperty(f, 'a', 100);
-      expect(f.a, equals(100));
-      expect(js_util.getProperty(f, 'a'), equals(100));
-    });
+  test('instanceof', () {
+    var canvas = new Element.tag('canvas');
+    expect(js_util.instanceof(canvas, JSNodeType), isTrue);
+    expect(js_util.instanceof(canvas, JSTextType), isFalse);
+    expect(js_util.instanceof(canvas, JSElementType), isTrue);
+    expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
+    var div = new Element.tag('div');
+    expect(js_util.instanceof(div, JSNodeType), isTrue);
+    expect(js_util.instanceof(div, JSTextType), isFalse);
+    expect(js_util.instanceof(div, JSElementType), isTrue);
+    expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
 
-    test('typed literal', () {
-      var l = new ExampleTypedLiteral();
-      js_util.setProperty(l, 'a', 'foo');
-      expect(js_util.getProperty(l, 'a'), equals('foo'));
-      expect(l.a, equals('foo'));
-      js_util.setProperty(l, 'a', l);
-      expect(identical(l.a, l), isTrue);
-      var list = ['arr'];
-      js_util.setProperty(l, 'a', list);
-      expect(identical(l.a, list), isTrue);
-      l.JS$class = 42;
-      expect(l.JS$class, equals(42));
-      js_util.setProperty(l, 'class', 100);
-      expect(l.JS$class, equals(100));
-    });
+    var text = new Text('foo');
+    expect(js_util.instanceof(text, JSNodeType), isTrue);
+    expect(js_util.instanceof(text, JSTextType), isTrue);
+    expect(js_util.instanceof(text, JSElementType), isFalse);
+
+    var f = new Foo();
+    expect(js_util.instanceof(f, JSNodeType), isFalse);
   });
 
-  group('callMethod', () {
-    test('html object', () {
-      var canvas = new CanvasElement();
-      expect(
-          identical(canvas.getContext('2d'),
-              js_util.callMethod(canvas, 'getContext', ['2d'])),
-          isTrue);
-    });
-
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.callMethod(f, 'bar', []), equals(42));
-    });
-  });
-
-  group('instanceof', () {
-    test('html object', () {
-      var canvas = new Element.tag('canvas');
-      expect(js_util.instanceof(canvas, JSNodeType), isTrue);
-      expect(js_util.instanceof(canvas, JSTextType), isFalse);
-      expect(js_util.instanceof(canvas, JSElementType), isTrue);
-      expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
-      var div = new Element.tag('div');
-      expect(js_util.instanceof(div, JSNodeType), isTrue);
-      expect(js_util.instanceof(div, JSTextType), isFalse);
-      expect(js_util.instanceof(div, JSElementType), isTrue);
-      expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
-
-      var text = new Text('foo');
-      expect(js_util.instanceof(text, JSNodeType), isTrue);
-      expect(js_util.instanceof(text, JSTextType), isTrue);
-      expect(js_util.instanceof(text, JSElementType), isFalse);
-    });
-
-    test('typed object', () {
-      var f = new Foo(42);
-      expect(js_util.instanceof(f, JSFooType), isTrue);
-      expect(js_util.instanceof(f, JSNodeType), isFalse);
-    });
-
-    test('typed literal', () {
-      var l = new ExampleTypedLiteral();
-      expect(js_util.instanceof(l, JSFooType), isFalse);
-    });
-  });
-
-  group('callConstructor', () {
-    test('html object', () {
-      var textNode = js_util.callConstructor(JSTextType, ['foo']);
-      expect(js_util.instanceof(textNode, JSTextType), isTrue);
-      expect(textNode is Text, isTrue);
-      expect(textNode.text, equals('foo'));
-    });
-
-    test('typed object', () {
-      Foo f = js_util.callConstructor(JSFooType, [42]);
-      expect(f.a, equals(42));
-    });
-  });
-
-  Future<void> testResolvedPromise() async {
-    final String result = await js_util.promiseToFuture(resolvedPromise);
-    expect(result, equals('resolved'));
-  }
-
-  Future<void> testRejectedPromise() async {
-    try {
-      final String result = await promiseToFuture(rejectedPromise);
-      fail('expected Future to throw an error');
-    } catch (error) {
-      expect(error, equals('rejected'));
-    }
-  }
-
-  Future<void> testReturnRejectedPromise() async {
-    final String result = await promiseToFuture(getResolvedPromise());
-    expect(result, equals('resolved'));
-  }
-
-  asyncTest(() async {
-    await testResolvedPromise();
-    await testRejectedPromise();
-    await testReturnRejectedPromise();
+  test('callConstructor', () {
+    var textNode = js_util.callConstructor(JSTextType, ['foo']);
+    expect(js_util.instanceof(textNode, JSTextType), isTrue);
+    expect(textNode is Text, isTrue);
+    expect(textNode.text, equals('foo'));
   });
 }
diff --git a/tests/lib_2/js/js_util/async_test.dart b/tests/lib_2/js/js_util/async_test.dart
new file mode 100644
index 0000000..9172fe7
--- /dev/null
+++ b/tests/lib_2/js/js_util/async_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2021, 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.
+
+@JS()
+library js_util_async_test;
+
+import 'dart:async';
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+import 'package:async_helper/async_helper.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+abstract class Promise<T> {}
+
+@JS()
+external Promise get resolvedPromise;
+
+@JS()
+external Promise get rejectedPromise;
+
+@JS()
+external Promise getResolvedPromise();
+
+main() {
+  eval(r"""
+    var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
+    var resolvedPromise = new Promise(resolve => resolve('resolved'));
+    function getResolvedPromise() {
+      return resolvedPromise;
+    }
+    """);
+
+  Future<void> testResolvedPromise() async {
+    final String result = await js_util.promiseToFuture(resolvedPromise);
+    expect(result, equals('resolved'));
+  }
+
+  Future<void> testRejectedPromise() async {
+    asyncExpectThrows<String>(() => js_util.promiseToFuture(rejectedPromise),
+        (String error) => error == 'rejected');
+  }
+
+  Future<void> testReturnResolvedPromise() async {
+    final String result = await js_util.promiseToFuture(getResolvedPromise());
+    expect(result, equals('resolved'));
+  }
+
+  asyncTest(() async {
+    await testResolvedPromise();
+    await testRejectedPromise();
+    await testReturnResolvedPromise();
+  });
+}
diff --git a/tests/lib_2/js/js_util/js_prefix_test.dart b/tests/lib_2/js/js_util/js_prefix_test.dart
new file mode 100644
index 0000000..47205cb
--- /dev/null
+++ b/tests/lib_2/js/js_util/js_prefix_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2021, 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 the functionality of the JS$ prefix for escaping keywords in JS names.
+// Currently only implemented in dart2js, expected to fail in ddc.
+
+@JS()
+library js_prefix_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+  external factory ExampleTypedLiteral({JS$_c, JS$class});
+
+  external get JS$_c;
+  external set JS$_c(v);
+  // Identical to JS$_c but only accessible within the library.
+  external get _c;
+  external get JS$class;
+  external set JS$class(v);
+}
+
+main() {
+  test('hasProperty', () {
+    var literal = new ExampleTypedLiteral(JS$_c: null, JS$class: true);
+    expect(js_util.hasProperty(literal, '_c'), isTrue);
+    expect(literal.JS$_c, isNull);
+    expect(js_util.hasProperty(literal, 'class'), isTrue);
+    // JS$_c escapes to _c so the property JS$_c will not exist on the object.
+    expect(js_util.hasProperty(literal, r'JS$_c'), isFalse);
+    expect(js_util.hasProperty(literal, r'JS$class'), isFalse);
+    expect(literal.JS$class, isTrue);
+
+    literal = new ExampleTypedLiteral();
+    expect(js_util.hasProperty(literal, '_c'), isFalse);
+    expect(js_util.hasProperty(literal, 'class'), isFalse);
+
+    literal = new ExampleTypedLiteral(JS$_c: 74);
+    expect(js_util.hasProperty(literal, '_c'), isTrue);
+    expect(literal.JS$_c, equals(74));
+  });
+
+  test('getProperty', () {
+    var literal = new ExampleTypedLiteral(JS$_c: 7, JS$class: true);
+    expect(js_util.getProperty(literal, '_c'), equals(7));
+    expect(literal.JS$_c, equals(7));
+    expect(js_util.getProperty(literal, 'class'), isTrue);
+    expect(js_util.getProperty(literal, r'JS$_c'), isNull);
+    expect(js_util.getProperty(literal, r'JS$class'), isNull);
+  });
+
+  test('setProperty', () {
+    var literal = new ExampleTypedLiteral();
+    literal.JS$class = 42;
+    expect(literal.JS$class, equals(42));
+    js_util.setProperty(literal, 'class', 100);
+    expect(literal.JS$class, equals(100));
+  });
+}
diff --git a/tests/lib_2/js/js_util/jsify_test.dart b/tests/lib_2/js/js_util/jsify_test.dart
new file mode 100644
index 0000000..5ed916d
--- /dev/null
+++ b/tests/lib_2/js/js_util/jsify_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, 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 the jsify functionality of the js_util library.
+
+@JS()
+library js_util_jsify_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+external bool checkMap(m, String, value);
+
+@JS()
+class Foo {
+  external Foo(num a);
+
+  external num get a;
+  external num bar();
+}
+
+main() {
+  eval(r"""
+    function Foo(a) {
+      this.a = a;
+    }
+
+    Foo.prototype.bar = function() {
+      return this.a;
+    }
+
+    function checkMap(m, key, value) {
+      if (m.hasOwnProperty(key))
+        return m[key] == value;
+      else
+        return false;
+    }
+    """);
+
+  test('convert a List', () {
+    final list = [1, 2, 3, 4, 5, 6, 7, 8];
+    var array = js_util.jsify(list);
+    expect(array is List, isTrue);
+    expect(identical(array, list), isFalse);
+    expect(array.length, equals(list.length));
+    for (var i = 0; i < list.length; i++) {
+      expect(array[i], equals(list[i]));
+    }
+  });
+
+  test('convert an Iterable', () {
+    final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
+    var array = js_util.jsify(set);
+    expect(array is List, isTrue);
+    expect(array.length, equals(set.length));
+    for (var i = 0; i < array.length; i++) {
+      expect(set.contains(array[i]), isTrue);
+    }
+  });
+
+  test('convert a Map', () {
+    var map = {'a': 1, 'b': 2, 'c': 3};
+    var jsMap = js_util.jsify(map);
+    expect(jsMap is List, isFalse);
+    expect(jsMap is Map, isFalse);
+    for (var key in map.keys) {
+      expect(checkMap(jsMap, key, map[key]), isTrue);
+    }
+  });
+
+  test('deep convert a complex object', () {
+    dynamic object = {
+      'a': [
+        1,
+        [2, 3]
+      ],
+      'b': {'c': 3, 'd': new Foo(42)},
+      'e': null
+    };
+    var jsObject = js_util.jsify(object);
+    expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
+    expect(js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
+    expect(js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
+
+    var b = js_util.getProperty(jsObject, 'b');
+    expect(js_util.getProperty(b, 'c'), equals(object['b']['c']));
+    var d = js_util.getProperty(b, 'd');
+    expect(d, equals(object['b']['d']));
+    expect(js_util.getProperty(d, 'a'), equals(42));
+    expect(js_util.callMethod(d, 'bar', []), equals(42));
+
+    expect(js_util.getProperty(jsObject, 'e'), isNull);
+  });
+
+  test('throws if object is not a Map or Iterable', () {
+    expect(() => js_util.jsify('a'), throwsArgumentError);
+  });
+}
diff --git a/tests/lib_2/js/js_util/properties_test.dart b/tests/lib_2/js/js_util/properties_test.dart
new file mode 100644
index 0000000..986c431
--- /dev/null
+++ b/tests/lib_2/js/js_util/properties_test.dart
@@ -0,0 +1,368 @@
+// Copyright (c) 2021, 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 the functionality of object properties with the js_util library. For
+// js_util tests with HTML objects see tests/lib/html/js_util_test.dart.
+
+@JS()
+library js_util_properties_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external String jsFunction();
+
+@JS()
+external void eval(String code);
+
+@JS('JSON.stringify')
+external String stringify(o);
+
+@JS('ArrayBuffer')
+external get JSArrayBufferType;
+
+@JS()
+class Foo {
+  external Foo(num a);
+
+  external num get a;
+  external num bar();
+  external Object get objectProperty;
+}
+
+@JS('Foo')
+external get JSFooType;
+
+String dartFunction() {
+  return 'Dart Function';
+}
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+  external factory ExampleTypedLiteral({a, b});
+
+  external get a;
+  external get b;
+}
+
+String _getBarWithSideEffect() {
+  var x = 5;
+  expect(x, equals(5));
+  return 'bar';
+}
+
+main() {
+  eval(r"""
+    function Foo(a) {
+      this.a = a;
+    }
+
+    Foo.b = 38;
+
+    Foo.prototype.list = [2, 4, 6];
+
+    Foo.prototype.bar = function() {
+      return this.a;
+    }
+
+    Foo.prototype.toString = function() {
+      return "I'm a Foo a=" + this.a;
+    }
+
+    Foo.prototype.fnList = [Foo.prototype.bar, Foo.prototype.toString];
+
+    Foo.prototype.objectProperty = {
+      'c': 1,
+      'list': [10, 20, 30],
+      'functionProperty': function() { return 'Function Property'; }
+    }
+
+    function jsFunction() {
+      return "JS Function";
+    }
+
+    Foo.prototype.nestedFunction = function() {
+      return function() {
+        return 'Nested Function';
+      };
+    }
+
+    Foo.prototype.getFirstEl = function(list) {
+      return list[0];
+    }
+
+    Foo.prototype.sumFn = function(a, b) {
+      return a + b;
+    }
+
+    Foo.prototype.getA = function(obj) {
+      return obj.a;
+    }
+
+    Foo.prototype.callFn = function(fn) {
+      return fn();
+    }
+    """);
+
+  group('newObject', () {
+    test('create', () {
+      expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
+    });
+
+    test('callMethod', () {
+      var o = js_util.newObject();
+      expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
+      expect(stringify(o), equals('{}'));
+    });
+
+    test('properties', () {
+      var o = js_util.newObject();
+      expect(js_util.hasProperty(o, 'foo bar'), isFalse);
+      expect(js_util.hasProperty(o, 'toString'), isTrue);
+      expect(js_util.callMethod(o, 'hasOwnProperty', ['toString']), isFalse);
+      expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isFalse);
+      js_util.setProperty(o, 'foo bar', 42);
+      expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isTrue);
+      expect(js_util.getProperty(o, 'foo bar'), equals(42));
+      expect(js_util.hasProperty(o, 'foo bar'), isTrue);
+      expect(stringify(o), equals('{"foo bar":42}'));
+    });
+
+    test('nested properties calls', () {
+      var o = js_util.newObject();
+      var f = new Foo(42);
+      js_util.setProperty(o, 'foo', f);
+      var foo = js_util.getProperty(o, 'foo');
+      expect(foo, equals(f));
+      expect(js_util.hasProperty(foo, 'a'), isTrue);
+      expect(js_util.getProperty(foo, 'a'), equals(42));
+      js_util.setProperty(foo, 'a', 24);
+      expect(js_util.getProperty(foo, 'a'), equals(24));
+    });
+  });
+
+  group('hasProperty', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.hasProperty(f, 'a'), isTrue);
+      expect(js_util.hasProperty(f, 'bar'), isTrue);
+      expect(js_util.hasProperty(f, 'b'), isFalse);
+      expect(js_util.hasProperty(f, 'toString'), isTrue);
+      js_util.setProperty(f, '__proto__', null);
+      expect(js_util.hasProperty(f, 'toString'), isFalse);
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+      expect(js_util.hasProperty(literal, 'a'), isTrue);
+      expect(js_util.hasProperty(literal, 'b'), isTrue);
+      expect(js_util.hasProperty(literal, 'anything'), isFalse);
+
+      literal = new ExampleTypedLiteral(a: null);
+      expect(js_util.hasProperty(literal, 'a'), isTrue);
+      expect(js_util.hasProperty(literal, 'b'), isFalse);
+      expect(js_util.hasProperty(literal, 'anything'), isFalse);
+    });
+
+    test('complex hasProperty calls', () {
+      var f = new Foo(42);
+      expect(js_util.hasProperty(f.objectProperty, 'c'), isTrue);
+      expect(js_util.hasProperty(f.objectProperty, 'nonexistent'), isFalse);
+
+      // Using a variable for the property name.
+      String propertyName = 'bar';
+      expect(js_util.hasProperty(f, propertyName), isTrue);
+    });
+  });
+
+  group('getProperty', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.getProperty(f, 'a'), equals(42));
+      expect(js_util.getProperty(f, 'b'), isNull);
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.getProperty(f, 'list') is List, isTrue);
+      expect(js_util.getProperty(f, 'objectProperty') is Object, isTrue);
+      expect(js_util.getProperty(f, 'toString') is Function, isTrue);
+      js_util.setProperty(f, '__proto__', null);
+      expect(js_util.getProperty(f, 'toString'), isNull);
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+      expect(js_util.getProperty(literal, 'a'), equals('x'));
+      expect(js_util.getProperty(literal, 'b'), equals(42));
+      expect(js_util.getProperty(literal, 'anything'), isNull);
+    });
+
+    test('complex getProperty calls', () {
+      var f = new Foo(42);
+
+      // Accessing a method property.
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.callMethod(f, 'bar', []), equals(42));
+
+      // Accessing list properties.
+      expect(js_util.getProperty(f, 'list')[0], equals(2));
+      expect(js_util.getProperty(f, 'fnList')[0] is Function, isTrue);
+      expect(
+          js_util.callMethod(
+              js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]),
+          equals(42));
+
+      // Accessing nested object properites.
+      var objectProperty = js_util.getProperty(f, 'objectProperty');
+      expect(js_util.getProperty(objectProperty, 'c'), equals(1));
+      expect(js_util.getProperty(objectProperty, 'list') is List, isTrue);
+      expect(js_util.getProperty(objectProperty, 'list')[1], equals(20));
+      expect(
+          js_util.getProperty(objectProperty, 'functionProperty') is Function,
+          isTrue);
+
+      // Using a variable for the property name.
+      String propertyName = 'a';
+      expect(js_util.getProperty(f, propertyName), equals(42));
+      String bar = _getBarWithSideEffect();
+      expect(js_util.getProperty(f, bar) is Function, isTrue);
+      expect(js_util.callMethod(f, bar, []), equals(42));
+    });
+  });
+
+  group('setProperty', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.getProperty(f, 'a'), equals(42));
+      js_util.setProperty(f, 'a', 100);
+      expect(f.a, equals(100));
+      expect(js_util.getProperty(f, 'a'), equals(100));
+
+      expect(js_util.getProperty(f, 'list') is List, isTrue);
+      js_util.setProperty(f, 'list', [8]);
+      expect(js_util.getProperty(f, 'list') is List, isTrue);
+      expect(js_util.getProperty(f, 'list')[0], equals(8));
+
+      js_util.setProperty(f, 'newProperty', 'new');
+      expect(js_util.getProperty(f, 'newProperty'), equals('new'));
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral();
+      js_util.setProperty(literal, 'a', 'foo');
+      expect(js_util.getProperty(literal, 'a'), equals('foo'));
+      expect(literal.a, equals('foo'));
+      js_util.setProperty(literal, 'a', literal);
+      expect(identical(literal.a, literal), isTrue);
+      var list = ['arr'];
+      js_util.setProperty(literal, 'a', list);
+      expect(identical(literal.a, list), isTrue);
+    });
+
+    test('complex setProperty calls', () {
+      var f = new Foo(42);
+
+      // Set function property to a num.
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      js_util.setProperty(f, 'bar', 5);
+      expect(js_util.getProperty(f, 'bar') is Function, isFalse);
+      expect(js_util.getProperty(f, 'bar'), equals(5));
+
+      // Set property to a Dart function.
+      js_util.setProperty(f, 'bar', allowInterop(dartFunction));
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.callMethod(f, 'bar', []), equals('Dart Function'));
+      js_util.setProperty(f, 'bar', allowInterop(() {
+        return 'Inline';
+      }));
+      expect(js_util.callMethod(f, 'bar', []), equals('Inline'));
+
+      // Set property to a JS function.
+      js_util.setProperty(f, 'bar', allowInterop(jsFunction));
+      expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+      expect(js_util.callMethod(f, 'bar', []), equals('JS Function'));
+
+      // Set property with nested object properties.
+      js_util.setProperty(f.objectProperty, 'c', 'new val');
+      expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val'));
+      js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]);
+      expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2));
+
+      // Using a variable for the property name.
+      String propertyName = 'bar';
+      js_util.setProperty(f, propertyName, 'foo');
+      expect(js_util.getProperty(f, 'bar'), equals('foo'));
+      String bar = _getBarWithSideEffect();
+      js_util.setProperty(f, bar, 'baz');
+      expect(js_util.getProperty(f, bar), equals('baz'));
+    });
+  });
+
+  group('callMethod', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.callMethod(f, 'bar', []), equals(42));
+    });
+
+    test('complex callMethod calls', () {
+      var f = new Foo(42);
+
+      // Call a method that returns an unbound function.
+      expect(js_util.callMethod(f, 'nestedFunction', []) is Function, isTrue);
+      expect(js_util.callMethod(f, 'nestedFunction', [])(),
+          equals('Nested Function'));
+
+      // Call method on a nested function property.
+      expect(js_util.callMethod(f.objectProperty, 'functionProperty', []),
+          equals('Function Property'));
+
+      // Call method with different args.
+      expect(
+          js_util.callMethod(f, 'getFirstEl', [
+            [25, 50]
+          ]),
+          equals(25));
+      expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5));
+      expect(js_util.callMethod(f, 'getA', [f]), equals(42));
+      expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]),
+          equals("JS Function"));
+      expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]),
+          equals("Dart Function"));
+      expect(
+          js_util.callMethod(f, 'callFn', [
+            allowInterop(() {
+              return "inline";
+            })
+          ]),
+          equals("inline"));
+
+      // Using a variable for the method name.
+      String methodName = 'bar';
+      expect(js_util.callMethod(f, methodName, []), equals(42));
+      String bar = _getBarWithSideEffect();
+      expect(js_util.callMethod(f, bar, []), equals(42));
+    });
+  });
+
+  group('instanceof', () {
+    test('typed object', () {
+      var f = new Foo(42);
+      expect(js_util.instanceof(f, JSFooType), isTrue);
+      expect(js_util.instanceof(f, JSArrayBufferType), isFalse);
+    });
+
+    test('typed literal', () {
+      var literal = new ExampleTypedLiteral();
+      expect(js_util.instanceof(literal, JSFooType), isFalse);
+    });
+  });
+
+  group('callConstructor', () {
+    test('typed object', () {
+      Foo f = js_util.callConstructor(JSFooType, [42]);
+      expect(f.a, equals(42));
+    });
+  });
+}
diff --git a/tests/lib_2/lib_2_dartdevc.status b/tests/lib_2/lib_2_dartdevc.status
index d6a6dd7..1b058f5 100644
--- a/tests/lib_2/lib_2_dartdevc.status
+++ b/tests/lib_2/lib_2_dartdevc.status
@@ -37,4 +37,5 @@
 html/custom_elements_test: Skip # Issue 29922
 html/notification_permission_test: Skip # Issue 32002
 isolate/*: SkipByDesign # No support for dart:isolate in dart4web (http://dartbug.com/30538)
+js/js_util/js_prefix_test: SkipByDesign # JS$ prefix not implemented on ddc.
 mirrors/*: SkipByDesign # Mirrors not supported on web in Dart 2.0.
diff --git a/tools/VERSION b/tools/VERSION
index 2a1c088..c50233a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 70
+PRERELEASE 71
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index e1800a4..ae306a9 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2068,13 +2068,6 @@
           ]
         },
         {
-          "name": "check dart nnbd sdk for static errors",
-          "script": "out/ReleaseX64/dart-sdk/bin/dart",
-          "arguments": [
-            "pkg/dev_compiler/tool/check_nnbd_sdk.dart"
-          ]
-        },
-        {
           "name": "ddc weak modular tests",
           "script": "out/ReleaseX64/dart-sdk/bin/dart",
           "testRunner": true,
@@ -2621,15 +2614,6 @@
           ]
         },
         {
-          "name": "check dart nnbd sdk for static errors",
-          "script": "out/ReleaseX64/dart-sdk/bin/dart",
-          "arguments": [
-            "pkg/dev_compiler/tool/check_nnbd_sdk.dart",
-            "--target",
-            "dart2js"
-          ]
-        },
-        {
           "name": "dart2js nnbd weak d8 tests",
           "arguments": [
             "-ndart2js-hostasserts-weak-linux-x64-d8",
@@ -2953,22 +2937,6 @@
             "-nanalyzer-asserts-${system}",
             "co19_2"
           ]
-        },
-        {
-          "name": "check nnbd sdk (ddc patch) for static errors",
-          "script": "out/ReleaseX64/dart-sdk/bin/dart",
-          "arguments": [
-            "pkg/dev_compiler/tool/check_nnbd_sdk.dart"
-          ]
-        },
-        {
-          "name": "check nnbd sdk (dart2js patch) for static errors",
-          "script": "out/ReleaseX64/dart-sdk/bin/dart",
-          "arguments": [
-            "pkg/dev_compiler/tool/check_nnbd_sdk.dart",
-            "--target",
-            "dart2js"
-          ]
         }
       ]
     },