Version 2.10.0-95.0.dev

Merge commit '910d1d7e0541c41f14c65cc8a37c79c9013e7c7d' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 8ca0596..27e4114 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -44,6 +44,7 @@
 import 'package:analysis_server/src/services/correction/dart/replace_with_is_empty.dart';
 import 'package:analysis_server/src/services/correction/dart/replace_with_tear_off.dart';
 import 'package:analysis_server/src/services/correction/dart/replace_with_var.dart';
+import 'package:analysis_server/src/services/correction/dart/sort_child_property_last.dart';
 import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart';
 import 'package:analysis_server/src/services/correction/dart/use_is_not_empty.dart';
 import 'package:analysis_server/src/services/correction/dart/use_rethrow.dart';
@@ -103,6 +104,7 @@
     LintNames.prefer_single_quotes: ConvertToSingleQuotes.newInstance,
     LintNames.prefer_spread_collections: ConvertAddAllToSpread.newInstance,
     LintNames.slash_for_doc_comments: ConvertDocumentationIntoLine.newInstance,
+    LintNames.sort_child_properties_last: SortChildPropertyLast.newInstance,
     LintNames.type_init_formals: RemoveTypeAnnotation.newInstance,
     LintNames.unawaited_futures: AddAwait.newInstance,
     LintNames.unnecessary_brace_in_string_interps:
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/sort_child_properties_last.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/sort_child_properties_last.dart
new file mode 100644
index 0000000..80c02dd
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/sort_child_properties_last.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, 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:analysis_server/src/services/linter/lint_names.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'bulk_fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(SortChildPropertyLastTest);
+  });
+}
+
+@reflectiveTest
+class SortChildPropertyLastTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.sort_child_properties_last;
+
+  Future<void> test_singleFile() async {
+    addFlutterPackage();
+    await resolveTestUnit('''
+import 'package:flutter/material.dart';
+main() {
+  Column(
+    children: [
+      Column(
+        children: [
+          Text('a'),
+        ],
+        crossAxisAlignment: CrossAxisAlignment.center,
+      ),
+      Text('b'),
+      Text('c'),
+      Text('d'),
+    ],
+    crossAxisAlignment: CrossAxisAlignment.center,
+  );
+}
+''');
+    // todo (pq): two diagnostics are produced but only the first is fixed.
+    // see: linter/test/rules/sort_child_properties_last.dart:nestedChildren()
+    await assertHasFix('''
+import 'package:flutter/material.dart';
+main() {
+  Column(
+    crossAxisAlignment: CrossAxisAlignment.center,
+    children: [
+      Column(
+        children: [
+          Text('a'),
+        ],
+        crossAxisAlignment: CrossAxisAlignment.center,
+      ),
+      Text('b'),
+      Text('c'),
+      Text('d'),
+    ],
+  );
+}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
index 009ee47..36033cf 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
@@ -46,6 +46,7 @@
 import 'replace_with_is_empty_test.dart' as replace_with_is_empty;
 import 'replace_with_tear_off_test.dart' as replace_with_tear_off;
 import 'replace_with_var_test.dart' as replace_with_var;
+import 'sort_child_properties_last.dart' as sort_child_properties_last;
 import 'use_curly_braces_test.dart' as use_curly_braces;
 import 'use_is_not_empty_test.dart' as use_is_not_empty;
 import 'use_rethrow_test.dart' as use_rethrow;
@@ -88,6 +89,7 @@
     replace_with_is_empty.main();
     replace_with_tear_off.main();
     replace_with_var.main();
+    sort_child_properties_last.main();
     use_curly_braces.main();
     use_is_not_empty.main();
     use_rethrow.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
index 1744e2f..a296cb0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/add_type_parameter_test.dart
@@ -13,35 +13,17 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(AddTypeParameterToClassTest);
-    defineReflectiveTests(AddTypeParameterToConstructorTest);
     defineReflectiveTests(AddTypeParameterToExtensionTest);
     defineReflectiveTests(AddTypeParameterToMethodTest);
+    defineReflectiveTests(AddTypeParameterToMixinTest);
+    defineReflectiveTests(AddTypeParameterToTopLevelFunctionTest);
+    defineReflectiveTests(AddTypeParameterToTypedefTest);
   });
 }
 
 @reflectiveTest
 class AddTypeParameterToClassTest extends _AddTypeParameterChange {
-  Future<void> test_class_removed() async {
-    setPackageContent('''
-class C<S, T> {}
-''');
-    setPackageData(_add(0, components: ['C']));
-    await resolveTestUnit('''
-import '$importUri';
-
-void f(C<int> c) {}
-''');
-    await assertHasFix('''
-import '$importUri';
-
-void f(C<String, int> c) {}
-''');
-  }
-}
-
-@reflectiveTest
-class AddTypeParameterToConstructorTest extends _AddTypeParameterChange {
-  Future<void> test_constructor_removed() async {
+  Future<void> test_constructorInvocation_removed() async {
     setPackageContent('''
 class C<S, T> {
   void C() {}
@@ -63,11 +45,96 @@
 }
 ''');
   }
+
+  Future<void> test_inExtends_removed() async {
+    setPackageContent('''
+class A<S, T> {}
+''');
+    setPackageData(_add(0, components: ['A']));
+    await resolveTestUnit('''
+import '$importUri';
+
+class B extends A<int> {}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+class B extends A<String, int> {}
+''');
+  }
+
+  Future<void> test_inImplements_removed() async {
+    setPackageContent('''
+class A<S, T> {}
+''');
+    setPackageData(_add(0, components: ['A']));
+    await resolveTestUnit('''
+import '$importUri';
+
+class B implements A<int> {}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+class B implements A<String, int> {}
+''');
+  }
+
+  Future<void> test_inOn_removed() async {
+    setPackageContent('''
+class A<S, T> {}
+''');
+    setPackageData(_add(0, components: ['A']));
+    await resolveTestUnit('''
+import '$importUri';
+
+extension E on A<int> {}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+extension E on A<String, int> {}
+''');
+  }
+
+  Future<void> test_inTypeAnnotation_removed() async {
+    setPackageContent('''
+class C<S, T> {}
+''');
+    setPackageData(_add(0, components: ['C']));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C<int> c) {}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C<String, int> c) {}
+''');
+  }
+
+  Future<void> test_inWith_removed() async {
+    setPackageContent('''
+class A<S, T> {}
+''');
+    setPackageData(_add(0, components: ['A']));
+    await resolveTestUnit('''
+import '$importUri';
+
+class B with A<int> {}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+class B with A<String, int> {}
+''');
+  }
 }
 
 @reflectiveTest
 class AddTypeParameterToExtensionTest extends _AddTypeParameterChange {
-  Future<void> test_extension_removed() async {
+  Future<void> test_override_removed() async {
     setPackageContent('''
 class C {}
 extension E<S, T> on C {
@@ -94,7 +161,126 @@
 
 @reflectiveTest
 class AddTypeParameterToMethodTest extends _AddTypeParameterChange {
-  Future<void> test_method_bound_removed() async {
+  Future<void> test_first_deprecated() async {
+    setPackageContent('''
+class C {
+  @deprecated
+  void m<T>() {}
+}
+''');
+    setPackageData(_add(0));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m<int>();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m<String, int>();
+}
+''');
+  }
+
+  Future<void> test_first_removed() async {
+    setPackageContent('''
+class C {
+  void m<S, T>() {}
+}
+''');
+    setPackageData(_add(0));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m<int>();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m<String, int>();
+}
+''');
+  }
+
+  Future<void> test_last_deprecated() async {
+    setPackageContent('''
+class C {
+  @deprecated
+  void m<S, T>() {}
+}
+''');
+    setPackageData(_add(2));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m<int, double>();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m<int, double, String>();
+}
+''');
+  }
+
+  Future<void> test_middle_deprecated() async {
+    setPackageContent('''
+class C {
+  @deprecated
+  void m<S, U>() {}
+}
+''');
+    setPackageData(_add(1));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m<int, double>();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m<int, String, double>();
+}
+''');
+  }
+
+  Future<void> test_only_deprecated() async {
+    setPackageContent('''
+class C {
+  @deprecated
+  void m() {}
+}
+''');
+    setPackageData(_add(0));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m<String>();
+}
+''');
+  }
+
+  Future<void> test_override_withBound_removed() async {
     setPackageContent('''
 class C {
   void m<T extends num>() {}
@@ -119,79 +305,7 @@
 ''');
   }
 
-  Future<void> test_method_first_deprecated() async {
-    setPackageContent('''
-class C {
-  @deprecated
-  void m<T>() {}
-}
-''');
-    setPackageData(_add(0));
-    await resolveTestUnit('''
-import '$importUri';
-
-void f(C c) {
-  c.m<int>();
-}
-''');
-    await assertHasFix('''
-import '$importUri';
-
-void f(C c) {
-  c.m<String, int>();
-}
-''');
-  }
-
-  Future<void> test_method_last_deprecated() async {
-    setPackageContent('''
-class C {
-  @deprecated
-  void m<S, T>() {}
-}
-''');
-    setPackageData(_add(2));
-    await resolveTestUnit('''
-import '$importUri';
-
-void f(C c) {
-  c.m<int, double>();
-}
-''');
-    await assertHasFix('''
-import '$importUri';
-
-void f(C c) {
-  c.m<int, double, String>();
-}
-''');
-  }
-
-  Future<void> test_method_middle_deprecated() async {
-    setPackageContent('''
-class C {
-  @deprecated
-  void m<S, U>() {}
-}
-''');
-    setPackageData(_add(1));
-    await resolveTestUnit('''
-import '$importUri';
-
-void f(C c) {
-  c.m<int, double>();
-}
-''');
-    await assertHasFix('''
-import '$importUri';
-
-void f(C c) {
-  c.m<int, String, double>();
-}
-''');
-  }
-
-  Future<void> test_method_noBound_removed() async {
+  Future<void> test_override_withoutBound_removed() async {
     setPackageContent('''
 class C {
   void m<T>() {}
@@ -215,50 +329,126 @@
 }
 ''');
   }
-
-  Future<void> test_method_only_deprecated() async {
-    setPackageContent('''
-class C {
-  @deprecated
-  void m() {}
 }
+
+@reflectiveTest
+class AddTypeParameterToMixinTest extends _AddTypeParameterChange {
+  Future<void> test_inWith_removed() async {
+    setPackageContent('''
+mixin M<S, T> {}
 ''');
-    setPackageData(_add(0));
+    setPackageData(_add(0, components: ['M']));
     await resolveTestUnit('''
 import '$importUri';
 
-void f(C c) {
-  c.m();
+class B with M<int> {}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+class B with M<String, int> {}
+''');
+  }
+}
+
+@reflectiveTest
+class AddTypeParameterToTopLevelFunctionTest extends _AddTypeParameterChange {
+  Future<void> test_only_deprecated() async {
+    setPackageContent('''
+@deprecated
+void f() {}
+''');
+    setPackageData(_add(0, components: ['f']));
+    await resolveTestUnit('''
+import '$importUri';
+
+void g() {
+  f();
 }
 ''');
     await assertHasFix('''
 import '$importUri';
 
-void f(C c) {
-  c.m<String>();
+void g() {
+  f<String>();
+}
+''');
+  }
+}
+
+@reflectiveTest
+class AddTypeParameterToTypedefTest extends _AddTypeParameterChange {
+  @failingTest
+  Future<void> test_functionType_removed() async {
+    // The test fails because the change is to the typedef `F`, not to the
+    // parameter `f`, so we don't see that the change might apply.
+    //
+    // Note, however, that there isn't currently any way to specify that the
+    // type parameter belongs to the function type being declared rather than to
+    // the typedef, so even if we fixed the problem above we would apply the
+    // change in the wrong places.
+    setPackageContent('''
+typedef F = T Function<S, T>();
+''');
+    setPackageData(_add(0, components: ['F']));
+    await resolveTestUnit('''
+import '$importUri';
+
+void g(F f) {
+  f<int>();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void g(F f) {
+  f<String, int>();
 }
 ''');
   }
 
-  Future<void> test_method_removed() async {
+  Future<void> test_typedef_first_removed() async {
     setPackageContent('''
-class C {
-  void m<S, T>() {}
-}
+typedef F<S, T> = T Function();
 ''');
-    setPackageData(_add(0));
+    setPackageData(_add(0, components: ['F']));
     await resolveTestUnit('''
 import '$importUri';
 
-void f(C c) {
-  c.m<int>();
+void g(F<int> f) {
+  f();
 }
 ''');
     await assertHasFix('''
 import '$importUri';
 
-void f(C c) {
-  c.m<String, int>();
+void g(F<String, int> f) {
+  f();
+}
+''');
+  }
+
+  @failingTest
+  Future<void> test_typedef_only_removed() async {
+    // The test fails because there is no diagnostic generated when there were
+    // no type arguments before the change; the type arguments are silently
+    // inferred to be `dynamic`.
+    setPackageContent('''
+typedef F<T> = T Function();
+''');
+    setPackageData(_add(0, components: ['F']));
+    await resolveTestUnit('''
+import '$importUri';
+
+void g(F f) {
+  f();
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void g(F<String> f) {
+  f();
 }
 ''');
   }
diff --git a/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart b/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
index d8c7d05..45de3c8 100644
--- a/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
@@ -96,8 +96,16 @@
   }
 
   /// Return the package corresponding to the [uri] or [path], `null` if none.
+  ///
+  /// For `package` and `asset` schemes the package name is retrieved from the
+  /// first path segment of [uri].
+  ///
+  /// For `file` schemes this tries to look up by the normalized [uri] path.
+  ///
+  /// If unable to find a package through other mechanisms mechanisms, or it is
+  /// an unrecognized uri scheme, then the package is looked up by [path].
   Package _findPackage(Uri uri, String path) {
-    if (uri.isScheme('package')) {
+    if (uri.isScheme('package') || uri.isScheme('asset')) {
       var pathSegments = uri.pathSegments;
       if (pathSegments.isNotEmpty) {
         var packageName = pathSegments.first;
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 0cacd29..a0b22fb 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -265,25 +265,15 @@
           CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
           left,
         );
-      } else if (variable.setter == null) {
-        if (variable is FieldElement) {
-          if (variable.isSynthetic) {
-            _errorReporter.reportErrorForNode(
-              CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
-              left,
-              [variable.name, variable.enclosingElement.displayName],
-            );
-          } else {
-            _errorReporter.reportErrorForNode(
-              CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
-              left,
-              [variable.name],
-            );
-          }
-          return;
-        }
+      } else if (variable is FieldElement && variable.isSynthetic) {
         _errorReporter.reportErrorForNode(
-          CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
+          CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
+          left,
+          [variable.name, variable.enclosingElement.displayName],
+        );
+      } else {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
           left,
           [variable.name],
         );
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 81b0968..8abc401 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1671,25 +1671,15 @@
           CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
           expression,
         );
-      } else if (variable.setter == null) {
-        if (variable is FieldElement) {
-          if (variable.isSynthetic) {
-            _errorReporter.reportErrorForNode(
-              CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
-              highlightedNode,
-              [variable.name, variable.enclosingElement.displayName],
-            );
-          } else {
-            _errorReporter.reportErrorForNode(
-              CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
-              highlightedNode,
-              [variable.name],
-            );
-          }
-          return;
-        }
+      } else if (variable is FieldElement && variable.isSynthetic) {
         _errorReporter.reportErrorForNode(
-          CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
+          CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
+          highlightedNode,
+          [variable.name, variable.enclosingElement.displayName],
+        );
+      } else {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
           highlightedNode,
           [variable.name],
         );
diff --git a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
index c499ea1..d8b9392 100644
--- a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
@@ -1459,7 +1459,7 @@
   x = 2;
 }
 ''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 30, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 30, 1),
     ]);
 
     var assignment = findNode.assignment('x = 2');
@@ -2214,7 +2214,7 @@
   }
 }
 ''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 86, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 86, 1),
     ]);
 
     var assignment = findNode.assignment('x = 2');
@@ -2416,7 +2416,7 @@
   x = 2;
 }
 ''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 31, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 31, 1),
     ]);
 
     var assignment = findNode.assignment('x = 2');
diff --git a/pkg/analyzer/test/src/diagnostics/assignment_to_final_local_test.dart b/pkg/analyzer/test/src/diagnostics/assignment_to_final_local_test.dart
index bd8e7d6..7c95b50 100644
--- a/pkg/analyzer/test/src/diagnostics/assignment_to_final_local_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assignment_to_final_local_test.dart
@@ -125,14 +125,6 @@
       error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 23, 1),
     ]);
   }
-
-  test_topLevelVariable() async {
-    await assertErrorsInCode('''
-final x = 0;
-f() { x = 1; }''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 19, 1),
-    ]);
-  }
 }
 
 @reflectiveTest
@@ -147,28 +139,4 @@
 }
 ''');
   }
-
-  test_set_external_variable_final_invalid() async {
-    await assertErrorsInCode('''
-external final int x;
-void f(int value) {
-  x = value;
-}
-''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 44, 1),
-    ]);
-  }
-
-  test_topLevelVariable_late() async {
-    await assertErrorsInCode('''
-late final int a;
-late final int b = 0;
-void f() {
-  a = 1;
-  b = 1;
-}
-''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 62, 1),
-    ]);
-  }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/assignment_to_final_no_setter_test.dart b/pkg/analyzer/test/src/diagnostics/assignment_to_final_no_setter_test.dart
index fe06f6f..0fb1b14 100644
--- a/pkg/analyzer/test/src/diagnostics/assignment_to_final_no_setter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assignment_to_final_no_setter_test.dart
@@ -15,44 +15,103 @@
 
 @reflectiveTest
 class AssignmentToFinalNoSetterTest extends PubPackageResolutionTest {
-  test_instance_undefined_hasGetter() async {
+  test_prefixedIdentifier_class_instanceGetter() async {
     await assertErrorsInCode('''
-extension E on int {
-  int get foo => 0;
+class A {
+  int get x => 0;
 }
-f() {
-  0.foo = 1;
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
 }
 ''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 53, 3),
-    ]);
-  }
-
-  test_prefixedIdentifier() async {
-    await assertErrorsInCode('''
-class A {
-  int get x => 0;
-}
-main() {
-  A a = new A();
-  a.x = 0;
-}''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 49, 1),
       error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 60, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 74, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 81, 1),
     ]);
   }
 
-  test_propertyAccess() async {
+  test_propertyAccess_class_instanceGetter() async {
     await assertErrorsInCode('''
 class A {
   int get x => 0;
 }
-class B {
-  static A a;
+
+void f(A a) {
+  (a).x = 0;
+  (a).x += 0;
+  ++(a).x;
+  (a).x++;
 }
-main() {
-  B.a.x = 0;
-}''', [
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 51, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 64, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 80, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 89, 1),
+    ]);
+  }
+
+  test_propertyAccess_extension_instanceGetter() async {
+    await assertErrorsInCode('''
+extension E on int {
+  int get x => 0;
+}
+
+void f() {
+  0.x = 0;
+  0.x += 0;
+  ++0.x;
+  0.x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 57, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 68, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 82, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 89, 1),
+    ]);
+  }
+
+  test_simpleIdentifier_class_instanceGetter() async {
+    await assertErrorsInCode('''
+class A {
+  int get x => 0;
+
+  void f() {
+    x = 0;
+    x += 0;
+    ++x;
+    x++;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 46, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 57, 1),
       error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 71, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 78, 1),
+    ]);
+  }
+
+  test_simpleIdentifier_class_staticGetter() async {
+    await assertErrorsInCode('''
+class A {
+  static int get x => 0;
+
+  void f() {
+    x = 0;
+    x += 0;
+    ++x;
+    x++;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 53, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 64, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 78, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 85, 1),
     ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/assignment_to_final_test.dart b/pkg/analyzer/test/src/diagnostics/assignment_to_final_test.dart
index bc8888d..6b1537c 100644
--- a/pkg/analyzer/test/src/diagnostics/assignment_to_final_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assignment_to_final_test.dart
@@ -16,29 +16,87 @@
 
 @reflectiveTest
 class AssignmentToFinalTest extends PubPackageResolutionTest {
-  test_instanceVariable() async {
+  test_prefixedIdentifier_instanceField() async {
+    await assertNoErrorsInCode('''
+class A {
+  var x = 0;
+}
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
+}
+''');
+  }
+
+  test_prefixedIdentifier_instanceField_final() async {
     await assertErrorsInCode('''
 class A {
-  final v = 0;
+  final x = 0;
 }
-f() {
-  A a = new A();
-  a.v = 1;
-}''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 54, 1),
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 46, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 57, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 71, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 78, 1),
     ]);
   }
 
-  test_instanceVariable_plusEq() async {
+  test_simpleIdentifier_topLevelGetter() async {
     await assertErrorsInCode('''
-class A {
-  final v = 0;
+int get x => 0;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
 }
-f() {
-  A a = new A();
-  a.v += 1;
-}''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 54, 1),
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 30, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 39, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 51, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 56, 1),
+    ]);
+  }
+
+  test_simpleIdentifier_topLevelVariable() async {
+    await assertNoErrorsInCode('''
+var x = 0;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
+}
+''');
+  }
+
+  test_simpleIdentifier_topLevelVariable_final() async {
+    await assertErrorsInCode('''
+final x = 0;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 27, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 36, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 48, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 53, 1),
     ]);
   }
 }
@@ -46,131 +104,330 @@
 @reflectiveTest
 class AssignmentToFinalWithNullSafetyTest extends AssignmentToFinalTest
     with WithNullSafetyMixin {
-  test_field_late_propertyAccess() async {
-    await assertErrorsInCode('''
-class A {
-  late final int a;
-  late final int b = 0;
-  void m() {
-    this.a = 1;
-    this.b = 1;
-  }
-}
-''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 92, 1),
-    ]);
-  }
-
-  test_field_late_simpleIdentifier() async {
-    await assertErrorsInCode('''
-class A {
-  late final int a;
-  late final int b = 0;
-  void m() {
-    a = 1;
-    b = 1;
-  }
-}
-''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 82, 1),
-    ]);
-  }
-
-  test_field_static_final_late_prefixedIdentifier() async {
-    await assertErrorsInCode('''
-class A {
-  static late final int a;
-  static late final int b = 0;
+  test_prefixedIdentifier_instanceField_abstract() async {
+    await assertNoErrorsInCode('''
+abstract class A {
+  abstract int x;
 }
 
-void f() {
-  A.a = 1;
-  A.b = 1;
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
 }
-''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 97, 1),
-    ]);
+''');
   }
 
-  test_field_static_final_late_simpleIdentifier() async {
+  test_prefixedIdentifier_instanceField_abstractFinal() async {
     await assertErrorsInCode('''
-class A {
-  static late final int a;
-  static late final int b = 0;
-  void m() {
-    a = 1;
-    b = 1;
-  }
+abstract class A {
+  abstract final int x;
+}
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
 }
 ''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 64, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 75, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 89, 1),
       error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 96, 1),
     ]);
   }
 
-  test_set_abstract_field_final_invalid() async {
-    await assertErrorsInCode('''
-abstract class A {
-  abstract final int x;
-}
-void f(A a, int x) {
-  a.x = x;
-}
-''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 70, 1),
-    ]);
-  }
-
-  test_set_abstract_field_final_overridden_valid() async {
+  test_prefixedIdentifier_instanceField_external() async {
     await assertNoErrorsInCode('''
 abstract class A {
-  abstract final int x;
+  external int x;
 }
-abstract class B extends A {
-  void set x(int value);
-}
-void f(B b, int x) {
-  b.x = x; // ok because setter provided in derived class
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
 }
 ''');
   }
 
-  test_set_external_field_final_invalid() async {
+  test_prefixedIdentifier_instanceField_externalFinal() async {
     await assertErrorsInCode('''
-class A {
+abstract class A {
   external final int x;
 }
-void f(A a, int x) {
-  a.x = x;
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 64, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 75, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 89, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 96, 1),
+    ]);
+  }
+
+  test_prefixedIdentifier_instanceField_lateFinal() async {
+    await assertNoErrorsInCode('''
+abstract class A {
+  late final int x;
+}
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
+}
+''');
+  }
+
+  test_prefixedIdentifier_instanceField_lateFinal_hasInitializer() async {
+    await assertErrorsInCode('''
+abstract class A {
+  late final int x = 0;
+}
+
+void f(A a) {
+  a.x = 0;
+  a.x += 0;
+  ++a.x;
+  a.x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 64, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 75, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 89, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 96, 1),
+    ]);
+  }
+
+  test_prefixedIdentifier_staticField_externalFinal() async {
+    await assertErrorsInCode('''
+abstract class A {
+  external static final int x;
+}
+
+void f() {
+  A.x = 0;
+  A.x += 0;
+  ++A.x;
+  A.x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 68, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 79, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 93, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 100, 1),
+    ]);
+  }
+
+  test_prefixedIdentifier_staticField_lateFinal() async {
+    await assertNoErrorsInCode('''
+abstract class A {
+  static late final int x;
+}
+
+void f() {
+  A.x = 0;
+  A.x += 0;
+  ++A.x;
+  A.x++;
+}
+''');
+  }
+
+  test_prefixedIdentifier_staticField_lateFinal_hasInitializer() async {
+    await assertErrorsInCode('''
+abstract class A {
+  static late final int x = 0;
+}
+
+void f() {
+  A.x = 0;
+  A.x += 0;
+  ++A.x;
+  A.x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 68, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 79, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 93, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 100, 1),
+    ]);
+  }
+
+  test_propertyAccess_instanceField_lateFinal() async {
+    await assertNoErrorsInCode('''
+abstract class A {
+  late final int x;
+}
+
+void f(A a) {
+  (a).x = 0;
+  (a).x += 0;
+  ++(a).x;
+  (a).x++;
+}
+''');
+  }
+
+  test_propertyAccess_instanceField_lateFinal_hasInitializer() async {
+    await assertErrorsInCode('''
+abstract class A {
+  late final int x = 0;
+}
+
+void f(A a) {
+  (a).x = 0;
+  (a).x += 0;
+  ++(a).x;
+  (a).x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 66, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 79, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 95, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 104, 1),
+    ]);
+  }
+
+  test_simpleIdentifier_instanceField_lateFinal() async {
+    await assertNoErrorsInCode('''
+abstract class A {
+  late final int x;
+
+  void f() {
+    x = 0;
+    x += 0;
+    ++x;
+    x++;
+  }
+}
+''');
+  }
+
+  test_simpleIdentifier_instanceField_lateFinal_hasInitializer() async {
+    await assertErrorsInCode('''
+abstract class A {
+  late final int x = 0;
+
+  void f() {
+    x = 0;
+    x += 0;
+    ++x;
+    x++;
+  }
 }
 ''', [
       error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 61, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 72, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 86, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 93, 1),
     ]);
   }
 
-  test_set_external_field_final_overridden_valid() async {
+  test_simpleIdentifier_staticField_lateFinal() async {
     await assertNoErrorsInCode('''
-class A {
-  external final int x;
-}
-abstract class B extends A {
-  void set x(int value);
-}
-void f(B b, int x) {
-  b.x = x; // ok because setter provided in derived class
+abstract class A {
+  static late final int x;
+
+  void f() {
+    x = 0;
+    x += 0;
+    ++x;
+    x++;
+  }
 }
 ''');
   }
 
-  test_set_external_static_field_final_invalid() async {
+  test_simpleIdentifier_staticField_lateFinal_hasInitializer() async {
     await assertErrorsInCode('''
-class A {
-  external static final int x;
-}
-void f(int x) {
-  A.x = x;
+abstract class A {
+  static late final int x = 0;
+
+  void f() {
+    x = 0;
+    x += 0;
+    ++x;
+    x++;
+  }
 }
 ''', [
-      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 63, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 68, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 79, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 93, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 100, 1),
+    ]);
+  }
+
+  test_simpleIdentifier_topLevelVariable_external() async {
+    await assertNoErrorsInCode('''
+external int x;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
+}
+''');
+  }
+
+  test_simpleIdentifier_topLevelVariable_externalFinal() async {
+    await assertErrorsInCode('''
+external final x;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 32, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 41, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 53, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 58, 1),
+    ]);
+  }
+
+  test_simpleIdentifier_topLevelVariable_lateFinal() async {
+    await assertNoErrorsInCode('''
+late final int x;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
+}
+''');
+  }
+
+  test_simpleIdentifier_topLevelVariable_lateFinal_hasInitializer() async {
+    await assertErrorsInCode('''
+late final int x = 0;
+
+void f() {
+  x = 0;
+  x += 0;
+  ++x;
+  x++;
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 36, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 45, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 57, 1),
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 62, 1),
     ]);
   }
 }
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 1e0af75..4cc2964 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -81,9 +81,15 @@
 
   // --launch-dds is provided by the VM if the VM service is to be enabled. In
   // that case, we need to launch DDS as well.
-  // TODO(bkonyi): add support for pub run (#42726)
-  if (args.contains('--launch-dds')) {
+  final launchDdsArg = args.singleWhere(
+    (element) => element.startsWith('--launch-dds'),
+    orElse: () => null,
+  );
+  if (launchDdsArg != null) {
     RunCommand.launchDds = true;
+    final ddsUrl = (launchDdsArg.split('=')[1]).split(':');
+    RunCommand.ddsHost = ddsUrl[0];
+    RunCommand.ddsPort = ddsUrl[1];
   }
   String commandName;
 
@@ -98,8 +104,8 @@
 
     // Run also can't be called with '--launch-dds', remove it if it's
     // contained in args.
-    if (args.contains('--launch-dds')) {
-      args = List.from(args)..remove('--launch-dds');
+    if (launchDdsArg != null) {
+      args = List.from(args)..remove(launchDdsArg);
     }
 
     // These flags have a format that can't be handled by package:args, so
diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart
index 8f28c9f..feeb99c 100644
--- a/pkg/dartdev/lib/src/commands/run.dart
+++ b/pkg/dartdev/lib/src/commands/run.dart
@@ -18,6 +18,8 @@
 
 class RunCommand extends DartdevCommand<int> {
   static bool launchDds = false;
+  static String ddsHost;
+  static String ddsPort;
 
   // kErrorExitCode, as defined in runtime/bin/error_exit.h
   static const errorExitCode = 255;
@@ -209,9 +211,6 @@
     // service intermediary which implements the VM service protocol and
     // provides non-VM specific extensions (e.g., log caching, client
     // synchronization).
-    // TODO(bkonyi): Remove once DevTools supports DDS.
-    // See https://github.com/flutter/flutter/issues/62507
-    launchDds = false;
     _DebuggingSession debugSession;
     if (launchDds) {
       debugSession = _DebuggingSession();
@@ -255,7 +254,9 @@
             sdk.ddsSnapshot
           else
             absolute(dirname(sdk.dart), 'gen', 'dds.dart.snapshot'),
-          serviceInfo.serverUri.toString()
+          serviceInfo.serverUri.toString(),
+          RunCommand.ddsHost,
+          RunCommand.ddsPort,
         ],
         mode: ProcessStartMode.detachedWithStdio);
     final completer = Completer<void>();
@@ -264,10 +265,20 @@
       if (event == 'DDS started') {
         sub.cancel();
         completer.complete();
+      } else if (event.contains('Failed to start DDS')) {
+        sub.cancel();
+        completer.completeError(event.replaceAll(
+          'Failed to start DDS',
+          'Could not start Observatory HTTP server',
+        ));
       }
     });
-
-    await completer.future;
-    return true;
+    try {
+      await completer.future;
+      return true;
+    } catch (e) {
+      stderr.write(e);
+      return false;
+    }
   }
 }
diff --git a/pkg/dds/bin/dds.dart b/pkg/dds/bin/dds.dart
index 476e84b..ed683dd 100644
--- a/pkg/dds/bin/dds.dart
+++ b/pkg/dds/bin/dds.dart
@@ -9,10 +9,39 @@
 /// A simple program which starts a [DartDevelopmentService] instance with a
 /// basic configuration.
 ///
-/// Takes the VM service URI as its single argument.
+/// Takes the following positional arguments:
+///   - VM service URI
+///   - DDS bind address
+///   - DDS port
 Future<void> main(List<String> args) async {
   if (args.isEmpty) return;
+
+  // This URI is provided by the VM service directly so don't bother doing a
+  // lookup.
   final remoteVmServiceUri = Uri.parse(args.first);
-  await DartDevelopmentService.startDartDevelopmentService(remoteVmServiceUri);
-  stderr.write('DDS started');
+
+  // Resolve the address which is potentially provided by the user.
+  InternetAddress address;
+  final addresses = await InternetAddress.lookup(args[1]);
+  // Prefer IPv4 addresses.
+  for (int i = 0; i < addresses.length; i++) {
+    address = addresses[i];
+    if (address.type == InternetAddressType.IPv4) break;
+  }
+  final serviceUri = Uri(
+    scheme: 'http',
+    host: address.address,
+    port: int.parse(args[2]),
+  );
+  try {
+    // TODO(bkonyi): add retry logic similar to that in vmservice_server.dart
+    // See https://github.com/dart-lang/sdk/issues/43192.
+    await DartDevelopmentService.startDartDevelopmentService(
+      remoteVmServiceUri,
+      serviceUri: serviceUri,
+    );
+    stderr.write('DDS started');
+  } catch (e) {
+    stderr.writeln('Failed to start DDS:\n$e');
+  }
 }
diff --git a/pkg/dds/test/auth_codes_test.dart b/pkg/dds/test/auth_codes_test.dart
index 9e7ad8c..653ff41 100644
--- a/pkg/dds/test/auth_codes_test.dart
+++ b/pkg/dds/test/auth_codes_test.dart
@@ -37,7 +37,7 @@
       final service = await vmServiceConnectUri(dds.wsUri.toString());
       final version = await service.getVersion();
       expect(version.major > 0, true);
-      expect(version.minor > 0, true);
+      expect(version.minor >= 0, true);
 
       // Ensure we can still make requests of the VM service via HTTP.
       HttpClient client = HttpClient();
diff --git a/pkg/dds/test/smoke_test.dart b/pkg/dds/test/smoke_test.dart
index e7e5216..0da26db 100644
--- a/pkg/dds/test/smoke_test.dart
+++ b/pkg/dds/test/smoke_test.dart
@@ -41,7 +41,7 @@
           final service = await vmServiceConnectUri(dds.wsUri.toString());
           final version = await service.getVersion();
           expect(version.major > 0, true);
-          expect(version.minor > 0, true);
+          expect(version.minor >= 0, true);
 
           expect(
             dds.uri.pathSegments,
@@ -64,7 +64,7 @@
               .single);
           expect(jsonResponse['result']['type'], 'Version');
           expect(jsonResponse['result']['major'] > 0, true);
-          expect(jsonResponse['result']['minor'] > 0, true);
+          expect(jsonResponse['result']['minor'] >= 0, true);
         },
       );
     }
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 7be2213..466d85f 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,6 +1,11 @@
 # Changelog
 
-## Unreleased
+## 5.0.0
+
+- **breaking**: Update to version `4.0.0` of the spec.
+  - Removes `ClientName` and `WebSocketTarget` objects
+  - Removes `getClientName`, `getWebSocketTarget`, `requirePermissionToResume`,
+    and `setClientName` RPCs.
 - Added `isSystemIsolate` property to `IsolateRef` and `Isolate`.
 - Added `isSystemIsolateGroup` property to `IsolateGroupRef` and `IsolateGroup`.
 - Added `serviceIsolates` and `serviceIsolateGroups` properties to `VM`.
diff --git a/pkg/vm_service/example/vm_service_assert.dart b/pkg/vm_service/example/vm_service_assert.dart
index 4dc33b5..52746bd 100644
--- a/pkg/vm_service/example/vm_service_assert.dart
+++ b/pkg/vm_service/example/vm_service_assert.dart
@@ -367,13 +367,6 @@
   return obj;
 }
 
-vms.ClientName assertClientName(vms.ClientName obj) {
-  assertNotNull(obj);
-  assertString(obj.type);
-  assertString(obj.name);
-  return obj;
-}
-
 vms.CodeRef assertCodeRef(vms.CodeRef obj) {
   assertNotNull(obj);
   assertString(obj.type);
@@ -1201,10 +1194,3 @@
   assertListOfIsolateGroupRef(obj.systemIsolateGroups);
   return obj;
 }
-
-vms.WebSocketTarget assertWebSocketTarget(vms.WebSocketTarget obj) {
-  assertNotNull(obj);
-  assertString(obj.type);
-  assertString(obj.uri);
-  return obj;
-}
diff --git a/pkg/vm_service/java/.gitignore b/pkg/vm_service/java/.gitignore
index 4370934..fbcce7d 100644
--- a/pkg/vm_service/java/.gitignore
+++ b/pkg/vm_service/java/.gitignore
@@ -5,7 +5,6 @@
 src/org/dartlang/vm/service/consumer/AddBreakpointConsumer.java
 src/org/dartlang/vm/service/consumer/AddBreakpointWithScriptUriConsumer.java
 src/org/dartlang/vm/service/consumer/ClearCpuSamplesConsumer.java
-src/org/dartlang/vm/service/consumer/ClientNameConsumer.java
 src/org/dartlang/vm/service/consumer/EvaluateConsumer.java
 src/org/dartlang/vm/service/consumer/EvaluateInFrameConsumer.java
 src/org/dartlang/vm/service/consumer/FlagListConsumer.java
@@ -42,7 +41,6 @@
 src/org/dartlang/vm/service/consumer/TimestampConsumer.java
 src/org/dartlang/vm/service/consumer/VMConsumer.java
 src/org/dartlang/vm/service/consumer/VersionConsumer.java
-src/org/dartlang/vm/service/consumer/WebSocketTargetConsumer.java
 src/org/dartlang/vm/service/element/AllocationProfile.java
 src/org/dartlang/vm/service/element/BoundField.java
 src/org/dartlang/vm/service/element/BoundVariable.java
@@ -51,7 +49,6 @@
 src/org/dartlang/vm/service/element/ClassList.java
 src/org/dartlang/vm/service/element/ClassObj.java
 src/org/dartlang/vm/service/element/ClassRef.java
-src/org/dartlang/vm/service/element/ClientName.java
 src/org/dartlang/vm/service/element/Code.java
 src/org/dartlang/vm/service/element/CodeKind.java
 src/org/dartlang/vm/service/element/CodeRef.java
@@ -129,4 +126,3 @@
 src/org/dartlang/vm/service/element/VM.java
 src/org/dartlang/vm/service/element/VMRef.java
 src/org/dartlang/vm/service/element/Version.java
-src/org/dartlang/vm/service/element/WebSocketTarget.java
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 0fa4741..db1ef8e5 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.38
+version=4.0
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index c9b4ec7..c518b25 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -28,7 +28,7 @@
         HeapSnapshotObjectNoData,
         HeapSnapshotObjectNullData;
 
-const String vmServiceVersion = '3.38.0';
+const String vmServiceVersion = '4.0.0';
 
 /// @optional
 const String optional = 'optional';
@@ -117,7 +117,6 @@
   'Class': Class.parse,
   'ClassHeapStats': ClassHeapStats.parse,
   'ClassList': ClassList.parse,
-  'ClientName': ClientName.parse,
   '@Code': CodeRef.parse,
   'Code': Code.parse,
   '@Context': ContextRef.parse,
@@ -186,7 +185,6 @@
   'Version': Version.parse,
   '@VM': VMRef.parse,
   'VM': VM.parse,
-  'WebSocketTarget': WebSocketTarget.parse,
 };
 
 Map<String, List<String>> _methodReturnTypes = {
@@ -200,7 +198,6 @@
   'evaluateInFrame': const ['InstanceRef', 'ErrorRef'],
   'getAllocationProfile': const ['AllocationProfile'],
   'getClassList': const ['ClassList'],
-  'getClientName': const ['ClientName'],
   'getCpuSamples': const ['CpuSamples'],
   'getFlagList': const ['FlagList'],
   'getInboundReferences': const ['InboundReferences'],
@@ -221,16 +218,13 @@
   'getVMTimeline': const ['Timeline'],
   'getVMTimelineFlags': const ['TimelineFlags'],
   'getVMTimelineMicros': const ['Timestamp'],
-  'getWebSocketTarget': const ['WebSocketTarget'],
   'pause': const ['Success'],
   'kill': const ['Success'],
   'registerService': const ['Success'],
   'reloadSources': const ['ReloadReport'],
   'removeBreakpoint': const ['Success'],
   'requestHeapSnapshot': const ['Success'],
-  'requirePermissionToResume': const ['Success'],
   'resume': const ['Success'],
-  'setClientName': const ['Success'],
   'setExceptionPauseMode': const ['Success'],
   'setFlag': const ['Success', 'Error'],
   'setLibraryDebuggable': const ['Success'],
@@ -518,13 +512,6 @@
   /// returned.
   Future<ClassList> getClassList(String isolateId);
 
-  /// The `getClientName` RPC is used to retrieve the name associated with the
-  /// currently connected VM service client. If no name was previously set
-  /// through the [setClientName] RPC, a default name will be returned.
-  ///
-  /// See [ClientName].
-  Future<ClientName> getClientName();
-
   /// The `getCpuSamples` RPC is used to retrieve samples collected by the CPU
   /// profiler. Only samples collected in the time range `[timeOriginMicros,
   /// timeOriginMicros + timeExtentMicros]` will be reported.
@@ -854,13 +841,6 @@
   /// See [Timestamp] and [getVMTimeline].
   Future<Timestamp> getVMTimelineMicros();
 
-  /// The `getWebSocketTarget` RPC returns the web socket URI that should be
-  /// used by VM service clients with WebSocket implementations that do not
-  /// follow redirects (e.g., `dart:html`'s [WebSocket]).
-  ///
-  /// See [WebSocketTarget].
-  Future<WebSocketTarget> getWebSocketTarget();
-
   /// The `pause` RPC is used to interrupt a running isolate. The RPC enqueues
   /// the interrupt request and potentially returns before the isolate is
   /// paused.
@@ -956,39 +936,6 @@
   /// returned.
   Future<Success> requestHeapSnapshot(String isolateId);
 
-  /// The `requirePermissionToResume` RPC is used to change the pause/resume
-  /// behavior of isolates by providing a way for the VM service to wait for
-  /// approval to resume from some set of clients. This is useful for clients
-  /// which want to perform some operation on an isolate after a pause without
-  /// it being resumed by another client.
-  ///
-  /// If the `onPauseStart` parameter is `true`, isolates will not resume after
-  /// pausing on start until the client sends a `resume` request and all other
-  /// clients which need to provide resume approval for this pause type have
-  /// done so.
-  ///
-  /// If the `onPauseReload` parameter is `true`, isolates will not resume after
-  /// pausing after a reload until the client sends a `resume` request and all
-  /// other clients which need to provide resume approval for this pause type
-  /// have done so.
-  ///
-  /// If the `onPauseExit` parameter is `true`, isolates will not resume after
-  /// pausing on exit until the client sends a `resume` request and all other
-  /// clients which need to provide resume approval for this pause type have
-  /// done so.
-  ///
-  /// **Important Notes:**
-  ///
-  /// - All clients with the same client name share resume permissions. Only a
-  /// single client of a given name is required to provide resume approval.
-  /// - When a client requiring approval disconnects from the service, a paused
-  /// isolate may resume if all other clients requiring resume approval have
-  /// already given approval. In the case that no other client requires resume
-  /// approval for the current pause event, the isolate will be resumed if at
-  /// least one other client has attempted to [resume] the isolate.
-  Future<Success> requirePermissionToResume(
-      {bool onPauseStart, bool onPauseReload, bool onPauseExit});
-
   /// The `resume` RPC is used to resume execution of a paused isolate.
   ///
   /// If the `step` parameter is not provided, the program will resume regular
@@ -1021,15 +968,6 @@
   Future<Success> resume(String isolateId,
       {/*StepOption*/ String step, int frameIndex});
 
-  /// The `setClientName` RPC is used to set a name to be associated with the
-  /// currently connected VM service client. If the `name` parameter is a
-  /// non-empty string, `name` will become the new name associated with the
-  /// client. If `name` is an empty string, the client's name will be reset to
-  /// its default name.
-  ///
-  /// See [Success].
-  Future<Success> setClientName(String name);
-
   /// The `setExceptionPauseMode` RPC is used to control if an isolate pauses
   /// when an exception is thrown.
   ///
@@ -1322,9 +1260,6 @@
             params['isolateId'],
           );
           break;
-        case 'getClientName':
-          response = await _serviceImplementation.getClientName();
-          break;
         case 'getCpuSamples':
           response = await _serviceImplementation.getCpuSamples(
             params['isolateId'],
@@ -1428,9 +1363,6 @@
         case 'getVMTimelineMicros':
           response = await _serviceImplementation.getVMTimelineMicros();
           break;
-        case 'getWebSocketTarget':
-          response = await _serviceImplementation.getWebSocketTarget();
-          break;
         case 'pause':
           response = await _serviceImplementation.pause(
             params['isolateId'],
@@ -1461,13 +1393,6 @@
             params['isolateId'],
           );
           break;
-        case 'requirePermissionToResume':
-          response = await _serviceImplementation.requirePermissionToResume(
-            onPauseStart: params['onPauseStart'],
-            onPauseReload: params['onPauseReload'],
-            onPauseExit: params['onPauseExit'],
-          );
-          break;
         case 'resume':
           response = await _serviceImplementation.resume(
             params['isolateId'],
@@ -1475,11 +1400,6 @@
             frameIndex: params['frameIndex'],
           );
           break;
-        case 'setClientName':
-          response = await _serviceImplementation.setClientName(
-            params['name'],
-          );
-          break;
         case 'setExceptionPauseMode':
           response = await _serviceImplementation.setExceptionPauseMode(
             params['isolateId'],
@@ -1792,9 +1712,6 @@
       _call('getClassList', {'isolateId': isolateId});
 
   @override
-  Future<ClientName> getClientName() => _call('getClientName');
-
-  @override
   Future<CpuSamples> getCpuSamples(
           String isolateId, int timeOriginMicros, int timeExtentMicros) =>
       _call('getCpuSamples', {
@@ -1910,9 +1827,6 @@
   Future<Timestamp> getVMTimelineMicros() => _call('getVMTimelineMicros');
 
   @override
-  Future<WebSocketTarget> getWebSocketTarget() => _call('getWebSocketTarget');
-
-  @override
   Future<Success> pause(String isolateId) =>
       _call('pause', {'isolateId': isolateId});
 
@@ -1950,15 +1864,6 @@
       _call('requestHeapSnapshot', {'isolateId': isolateId});
 
   @override
-  Future<Success> requirePermissionToResume(
-          {bool onPauseStart, bool onPauseReload, bool onPauseExit}) =>
-      _call('requirePermissionToResume', {
-        if (onPauseStart != null) 'onPauseStart': onPauseStart,
-        if (onPauseReload != null) 'onPauseReload': onPauseReload,
-        if (onPauseExit != null) 'onPauseExit': onPauseExit,
-      });
-
-  @override
   Future<Success> resume(String isolateId,
           {/*StepOption*/ String step, int frameIndex}) =>
       _call('resume', {
@@ -1968,10 +1873,6 @@
       });
 
   @override
-  Future<Success> setClientName(String name) =>
-      _call('setClientName', {'name': name});
-
-  @override
   Future<Success> setExceptionPauseMode(
           String isolateId, /*ExceptionPauseMode*/ String mode) =>
       _call('setExceptionPauseMode', {'isolateId': isolateId, 'mode': mode});
@@ -3074,35 +2975,6 @@
   String toString() => '[ClassList type: ${type}, classes: ${classes}]';
 }
 
-/// See [getClientName] and [setClientName].
-class ClientName extends Response {
-  static ClientName parse(Map<String, dynamic> json) =>
-      json == null ? null : ClientName._fromJson(json);
-
-  /// The name of the currently connected VM service client.
-  String name;
-
-  ClientName({
-    @required this.name,
-  });
-
-  ClientName._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
-    name = json['name'];
-  }
-
-  @override
-  Map<String, dynamic> toJson() {
-    var json = <String, dynamic>{};
-    json['type'] = 'ClientName';
-    json.addAll({
-      'name': name,
-    });
-    return json;
-  }
-
-  String toString() => '[ClientName type: ${type}, name: ${name}]';
-}
-
 /// `CodeRef` is a reference to a `Code` object.
 class CodeRef extends ObjRef {
   static CodeRef parse(Map<String, dynamic> json) =>
@@ -7171,32 +7043,3 @@
 
   String toString() => '[VM]';
 }
-
-/// See [getWebSocketTarget]
-class WebSocketTarget extends Response {
-  static WebSocketTarget parse(Map<String, dynamic> json) =>
-      json == null ? null : WebSocketTarget._fromJson(json);
-
-  /// The web socket URI that should be used to connect to the service.
-  String uri;
-
-  WebSocketTarget({
-    @required this.uri,
-  });
-
-  WebSocketTarget._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
-    uri = json['uri'];
-  }
-
-  @override
-  Map<String, dynamic> toJson() {
-    var json = <String, dynamic>{};
-    json['type'] = 'WebSocketTarget';
-    json.addAll({
-      'uri': uri,
-    });
-    return json;
-  }
-
-  String toString() => '[WebSocketTarget type: ${type}, uri: ${uri}]';
-}
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 6c4cda5..fff9595 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -2,7 +2,7 @@
 description: >-
   A library to communicate with a service implementing the Dart VM
   service protocol.
-version: 4.2.0
+version: 5.0.0
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
 
diff --git a/pkg/vm_snapshot_analysis/lib/v8_profile.dart b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
index 8419068..6f7db7b 100644
--- a/pkg/vm_snapshot_analysis/lib/v8_profile.dart
+++ b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
@@ -412,7 +412,9 @@
       case 'Function':
         if (node.name != '<anonymous signature>') {
           var owner = node['owner_'];
-          if (node['data_'].type == 'ClosureData') {
+
+          // Artificial nodes will not have data_ field.
+          if (node['data_']?.type == 'ClosureData') {
             owner = node['data_']['parent_function_'];
           }
           return makeInfoNode(node.index,
diff --git a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
index 78a1ac6..ee2a3d1 100644
--- a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
+++ b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
@@ -665,7 +665,7 @@
           expect(findChild(treemap, 'package:input/input.dart'), isNotNull);
         } else {
           expect(childrenNames(findChild(treemap, 'package:input')),
-              equals({'<self>', 'main.dart', 'input.dart'}));
+              equals({'main.dart', 'input.dart'}));
         }
       });
     });
diff --git a/pkg/vm_snapshot_analysis/test/utils.dart b/pkg/vm_snapshot_analysis/test/utils.dart
index 2531279..acdf42f 100644
--- a/pkg/vm_snapshot_analysis/test/utils.dart
+++ b/pkg/vm_snapshot_analysis/test/utils.dart
@@ -46,7 +46,7 @@
       '-o',
       outputBinary,
       '--packages=$packages',
-      '--extra-gen-snapshot-options=$flag=$sizesJson',
+      '--extra-gen-snapshot-options=--dwarf-stack-traces,$flag=$sizesJson',
       mainDart,
     ]);
 
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 000855d..661dfaf 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -544,15 +544,23 @@
   result = Dart_SetDeferredLoadHandler(Loader::DeferredLoadHandler);
   CHECK_RESULT(result);
 
+  int vm_service_server_port = INVALID_VM_SERVICE_SERVER_PORT;
+  if (Options::disable_dart_dev()) {
+    vm_service_server_port = Options::vm_service_server_port();
+  } else if (Options::vm_service_server_port() !=
+             INVALID_VM_SERVICE_SERVER_PORT) {
+    vm_service_server_port = 0;
+  }
+
   // Load embedder specific bits and return.
   if (!VmService::Setup(
-          Options::vm_service_server_ip(), Options::vm_service_server_port(),
-          Options::vm_service_dev_mode(), Options::vm_service_auth_disabled(),
+          Options::disable_dart_dev() ? Options::vm_service_server_ip()
+                                      : DEFAULT_VM_SERVICE_SERVER_IP,
+          vm_service_server_port, Options::vm_service_dev_mode(),
+          Options::vm_service_auth_disabled(),
           Options::vm_write_service_info_filename(), Options::trace_loading(),
           Options::deterministic(), Options::enable_service_port_fallback(),
-          // TODO(bkonyi): uncomment when DDS is re-enabled.
-          // See https://github.com/flutter/flutter/issues/62507
-          /*!Options::disable_dart_dev()*/ false)) {
+          !Options::disable_dart_dev())) {
     *error = Utils::StrDup(VmService::GetErrorMessage());
     return NULL;
   }
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index 8fa4f79..f1d8649 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -284,10 +284,6 @@
   return true;
 }
 
-static const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost";
-static const int DEFAULT_VM_SERVICE_SERVER_PORT = 8181;
-static const int INVALID_VM_SERVICE_SERVER_PORT = -1;
-
 const char* Options::vm_service_server_ip_ = DEFAULT_VM_SERVICE_SERVER_IP;
 int Options::vm_service_server_port_ = INVALID_VM_SERVICE_SERVER_PORT;
 bool Options::ProcessEnableVmServiceOption(const char* arg,
@@ -605,7 +601,15 @@
   }
 
   if (!Options::disable_dart_dev() && enable_vm_service_) {
-    dart_options->AddArgument("--launch-dds");
+    const char* dds_format_str = "--launch-dds=%s:%d";
+    size_t size = snprintf(nullptr, 0, dds_format_str, vm_service_server_ip(),
+                           vm_service_server_port());
+    // Make room for '\0'.
+    ++size;
+    char* dds_uri = new char[size];
+    snprintf(dds_uri, size, dds_format_str, vm_service_server_ip(),
+             vm_service_server_port());
+    dart_options->AddArgument(dds_uri);
   }
 
   // Verify consistency of arguments.
diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h
index 76c50be..48553df 100644
--- a/runtime/bin/main_options.h
+++ b/runtime/bin/main_options.h
@@ -79,6 +79,10 @@
   kAppJIT,
 };
 
+static const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost";
+static const int DEFAULT_VM_SERVICE_SERVER_PORT = 8181;
+static const int INVALID_VM_SERVICE_SERVER_PORT = -1;
+
 class Options {
  public:
   static int ParseArguments(int argc,
diff --git a/runtime/observatory/tests/service/client_name_rpc_test.dart b/runtime/observatory/tests/service/client_name_rpc_test.dart
index 92aa4c9..8f81da0 100644
--- a/runtime/observatory/tests/service/client_name_rpc_test.dart
+++ b/runtime/observatory/tests/service/client_name_rpc_test.dart
@@ -66,4 +66,5 @@
 main(args) async => runVMTests(
       args,
       tests,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/client_resume_approvals_approve_then_disconnect_test.dart b/runtime/observatory/tests/service/client_resume_approvals_approve_then_disconnect_test.dart
index 439f170..28715a4 100644
--- a/runtime/observatory/tests/service/client_resume_approvals_approve_then_disconnect_test.dart
+++ b/runtime/observatory/tests/service/client_resume_approvals_approve_then_disconnect_test.dart
@@ -66,4 +66,5 @@
       testeeConcurrent: fooBar,
       pause_on_start: true,
       pause_on_exit: true,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/client_resume_approvals_disconnect_test.dart b/runtime/observatory/tests/service/client_resume_approvals_disconnect_test.dart
index d82317a..1ef5273 100644
--- a/runtime/observatory/tests/service/client_resume_approvals_disconnect_test.dart
+++ b/runtime/observatory/tests/service/client_resume_approvals_disconnect_test.dart
@@ -64,4 +64,5 @@
       testeeConcurrent: fooBar,
       pause_on_start: true,
       pause_on_exit: true,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/client_resume_approvals_identical_names_test.dart b/runtime/observatory/tests/service/client_resume_approvals_identical_names_test.dart
index dbcc14a..06df948 100644
--- a/runtime/observatory/tests/service/client_resume_approvals_identical_names_test.dart
+++ b/runtime/observatory/tests/service/client_resume_approvals_identical_names_test.dart
@@ -46,4 +46,5 @@
       testeeConcurrent: fooBar,
       pause_on_start: true,
       pause_on_exit: true,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/client_resume_approvals_multiple_names_test.dart b/runtime/observatory/tests/service/client_resume_approvals_multiple_names_test.dart
index 88d217d..780fe61 100644
--- a/runtime/observatory/tests/service/client_resume_approvals_multiple_names_test.dart
+++ b/runtime/observatory/tests/service/client_resume_approvals_multiple_names_test.dart
@@ -64,4 +64,5 @@
       testeeConcurrent: fooBar,
       pause_on_start: true,
       pause_on_exit: true,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/client_resume_approvals_name_change_test.dart b/runtime/observatory/tests/service/client_resume_approvals_name_change_test.dart
index 0e1374e..dac6f2b 100644
--- a/runtime/observatory/tests/service/client_resume_approvals_name_change_test.dart
+++ b/runtime/observatory/tests/service/client_resume_approvals_name_change_test.dart
@@ -57,4 +57,5 @@
       testeeConcurrent: fooBar,
       pause_on_start: true,
       pause_on_exit: true,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/client_resume_approvals_reload_test.dart b/runtime/observatory/tests/service/client_resume_approvals_reload_test.dart
index 08fdc10..fcd7e72 100644
--- a/runtime/observatory/tests/service/client_resume_approvals_reload_test.dart
+++ b/runtime/observatory/tests/service/client_resume_approvals_reload_test.dart
@@ -65,4 +65,5 @@
       hotReloadTest,
       testeeConcurrent: fooBar,
       pause_on_start: true,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/get_client_name_rpc_test.dart b/runtime/observatory/tests/service/get_client_name_rpc_test.dart
index 25b7828..e9f6ae3 100644
--- a/runtime/observatory/tests/service/get_client_name_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_client_name_rpc_test.dart
@@ -41,4 +41,5 @@
       args,
       test,
       testeeBefore: fooBar,
+      enableService: false,
     );
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 37c147a..7ee46a0 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -11,8 +11,8 @@
   (VM vm) async {
     var result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
-    expect(result['major'], equals(3));
-    expect(result['minor'], equals(38));
+    expect(result['major'], equals(4));
+    expect(result['minor'], equals(0));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/observatory/tests/service/vm_service_dds_test.dart b/runtime/observatory/tests/service/vm_service_dds_test.dart
index c17cc6f..d2f2922 100644
--- a/runtime/observatory/tests/service/vm_service_dds_test.dart
+++ b/runtime/observatory/tests/service/vm_service_dds_test.dart
@@ -12,7 +12,7 @@
   (VM vm, DartDevelopmentService dds) async {
     final client = WebSocketVM(
       WebSocketVMTarget(
-        dds.remoteVmServiceWsUri.toString(),
+        dds.wsUri.toString(),
       ),
     );
     final result = await client.invokeRpcNoUpgrade('getSupportedProtocols', {});
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 955226e..0040724 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -5553,9 +5553,15 @@
     name = str.ToCString();
   }
 
-  TraceStartWritingObject(type, obj, name);
+  // CreateArtificalNodeIfNeeded might call TraceStartWritingObject
+  // and these calls don't nest, so we need to call this outside
+  // of the tracing scope created below.
   if (owner != nullptr) {
     CreateArtificalNodeIfNeeded(owner);
+  }
+
+  TraceStartWritingObject(type, obj, name);
+  if (owner != nullptr) {
     AttributePropertyRef(owner, owner_ref_name,
                          /*permit_artificial_ref=*/true);
   }
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 896c695..b4f7acd 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -14,8 +14,8 @@
 
 namespace dart {
 
-#define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 38
+#define SERVICE_PROTOCOL_MAJOR_VERSION 4
+#define SERVICE_PROTOCOL_MINOR_VERSION 0
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 4ba84da..5eeb755 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.38
+# Dart VM Service Protocol 4.0 
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.38_ of the Dart VM Service Protocol. This
+This document describes of _version 4.0_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -39,7 +39,6 @@
   - [evaluate](#evaluate)
   - [evaluateInFrame](#evaluateinframe)
   - [getAllocationProfile](#getallocationprofile)
-  - [getClientName](#getclientname)
   - [getCpuSamples](#getcpusamples)
   - [getFlagList](#getflaglist)
   - [getInstances](#getinstances)
@@ -59,16 +58,13 @@
   - [getVMTimeline](#getvmtimeline)
   - [getVMTimelineFlags](#getvmtimelineflags)
   - [getVMTimelineMicros](#getvmtimelinemicros)
-  - [getWebSocketTarget](#getwebsockettarget)
   - [invoke](#invoke)
   - [pause](#pause)
   - [kill](#kill)
   - [registerService](#registerService)
   - [reloadSources](#reloadsources)
   - [removeBreakpoint](#removebreakpoint)
-  - [requirePermissionToResume](#requirepermissiontoresume)
   - [resume](#resume)
-  - [setClientName](#setclientname)
   - [setExceptionPauseMode](#setexceptionpausemode)
   - [setFlag](#setflag)
   - [setLibraryDebuggable](#setlibrarydebuggable)
@@ -743,21 +739,6 @@
 
 See [ClassList](#classlist).
 
-### getClientName
-
-_**Note**: This method is deprecated and will be removed in v4.0 of the protocol.
-An equivalent can be found in the Dart Development Service (DDS) protocol._
-
-```
-ClientName getClientName()
-```
-
-The _getClientName_ RPC is used to retrieve the name associated with the currently
-connected VM service client. If no name was previously set through the
-[setClientName](#setclientname) RPC, a default name will be returned.
-
-See [ClientName](#clientname).
-
 ### getCpuSamples
 
 ```
@@ -1144,17 +1125,6 @@
 
 See [Timestamp](#timestamp) and [getVMTimeline](#getvmtimeline).
 
-### getWebSocketTarget
-
-```
-WebSocketTarget getWebSocketTarget()
-```
-
-The _getWebSocketTarget_ RPC returns the web socket URI that should be used by VM service clients
-with WebSocket implementations that do not follow redirects (e.g., `dart:html`'s [WebSocket](https://api.dart.dev/dart-html/WebSocket-class.html)).
-
-See [WebSocketTarget](#websockettarget).
-
 ### pause
 
 ```
@@ -1260,44 +1230,6 @@
 If _isolateId_ refers to an isolate which has exited, then the
 _Collected_ [Sentinel](#sentinel) is returned.
 
-### requirePermissionToResume
-
-_**Note**: This method is deprecated and will be removed in v4.0 of the protocol.
-An equivalent can be found in the Dart Development Service (DDS) protocol._
-
-```
-Success requirePermissionToResume(bool onPauseStart [optional],
-                                  bool onPauseReload[optional],
-                                  bool onPauseExit [optional])
-```
-
-The _requirePermissionToResume_ RPC is used to change the pause/resume behavior
-of isolates by providing a way for the VM service to wait for approval to resume
-from some set of clients. This is useful for clients which want to perform some
-operation on an isolate after a pause without it being resumed by another client.
-
-If the _onPauseStart_ parameter is `true`, isolates will not resume after pausing
-on start until the client sends a `resume` request and all other clients which
-need to provide resume approval for this pause type have done so.
-
-If the _onPauseReload_ parameter is `true`, isolates will not resume after pausing
-after a reload until the client sends a `resume` request and all other clients
-which need to provide resume approval for this pause type have done so.
-
-If the _onPauseExit_ parameter is `true`, isolates will not resume after pausing
-on exit until the client sends a `resume` request and all other clients which
-need to provide resume approval for this pause type have done so.
-
-**Important Notes:**
-
-- All clients with the same client name share resume permissions. Only a
-  single client of a given name is required to provide resume approval.
-- When a client requiring approval disconnects from the service, a paused
-  isolate may resume if all other clients requiring resume approval have
-  already given approval. In the case that no other client requires resume
-  approval for the current pause event, the isolate will be resumed if at
-  least one other client has attempted to [resume](#resume) the isolate.
-
 ### resume
 
 ```
@@ -1332,22 +1264,6 @@
 
 See [Success](#success), [StepOption](#StepOption).
 
-### setClientName
-
-_**Note**: This method is deprecated and will be removed in v4.0 of the protocol.
-An equivalent can be found in the Dart Development Service (DDS) protocol._
-
-```
-Success setClientName(string name)
-```
-
-The _setClientName_ RPC is used to set a name to be associated with the currently
-connected VM service client. If the _name_ parameter is a non-empty string, _name_
-will become the new name associated with the client. If _name_ is an empty string,
-the client's name will be reset to its default name.
-
-See [Success](#success).
-
 ### setExceptionPauseMode
 
 ```
@@ -1783,20 +1699,6 @@
 }
 ```
 
-### ClientName
-
-_**Note**: This class is deprecated and will be removed in v4.0 of the protocol.
-An equivalent can be found in the Dart Development Service (DDS) protocol._
-
-```
-class ClientName extends Response {
-  // The name of the currently connected VM service client.
-  string name;
-}
-```
-
-See [getClientName](#getclientname) and [setClientName](#setclientname).
-
 ### Code
 
 ```
@@ -3887,17 +3789,6 @@
 }
 ```
 
-### WebSocketTarget
-
-```
-class WebSocketTarget extends Response {
-  // The web socket URI that should be used to connect to the service.
-  string uri;
-}
-```
-
-See [getWebSocketTarget](#getwebsockettarget)
-
 ## Revision History
 
 version | comments
@@ -3935,15 +3826,14 @@
 3.28 | TODO(aam): document changes from 3.28
 3.29 | Add `getClientName`, `setClientName`, `requireResumeApproval`
 3.30 | Updated return types of RPCs which require an `isolateId` to allow for `Sentinel` results if the target isolate has shutdown.
-3.31 | Added single client mode, which allows for the Dart Development Service (DDS) to become the sole client of
-the VM service.
+3.31 | Added single client mode, which allows for the Dart Development Service (DDS) to become the sole client of the VM service.
 3.32 | Added `getClassList` RPC and `ClassList` object.
 3.33 | Added deprecation notice for `getClientName`, `setClientName`, `requireResumeApproval`, and `ClientName`. These RPCs are moving to the DDS protocol and will be removed in v4.0 of the VM service protocol.
 3.34 | Added `TimelineStreamSubscriptionsUpdate` event which is sent when `setVMTimelineFlags` is invoked.
 3.35 | Added `getSupportedProtocols` RPC and `ProtocolList`, `Protocol` objects.
 3.36 | Added `getProcessMemoryUsage` RPC and `ProcessMemoryUsage` and `ProcessMemoryItem` objects.
 3.37 | Added `getWebSocketTarget` RPC and `WebSocketTarget` object.
-3.38 | Added `isSystemIsolate` property to `@Isolate` and `Isolate`, `isSystemIsolateGroup` property to `@IsolateGroup` and `IsolateGroup`,
-and properties `systemIsolates` and `systemIsolateGroups` to `VM`.
+3.38 | Added `isSystemIsolate` property to `@Isolate` and `Isolate`, `isSystemIsolateGroup` property to `@IsolateGroup` and `IsolateGroup`, and properties `systemIsolates` and `systemIsolateGroups` to `VM`.
+4.0 | Removed the following deprecated RPCs and objects: `getClientName`, `getWebSocketTarget`, `setClientName`, `requireResumeApproval`, `ClientName`, and `WebSocketTarget`.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
index 5b90440..18e06f9 100644
--- a/runtime/vm/service_event.cc
+++ b/runtime/vm/service_event.cc
@@ -184,9 +184,6 @@
   if (kind() == kVMFlagUpdate) {
     jsobj.AddProperty("flag", flag_name());
     // For backwards compatibility, "new_value" is also provided.
-    // TODO(bkonyi): remove when service protocol major version is incremented.
-    ASSERT(SERVICE_PROTOCOL_MAJOR_VERSION == 3);
-    jsobj.AddProperty("new_value", flag_new_value());
     jsobj.AddProperty("newValue", flag_new_value());
   }
   if (kind() == kIsolateReload) {
diff --git a/sdk/lib/_internal/vm/bin/vmservice_io.dart b/sdk/lib/_internal/vm/bin/vmservice_io.dart
index 329e505..d1f8a5d 100644
--- a/sdk/lib/_internal/vm/bin/vmservice_io.dart
+++ b/sdk/lib/_internal/vm/bin/vmservice_io.dart
@@ -81,11 +81,20 @@
 }
 
 Future<void> ddsConnectedCallback() async {
+  final serviceAddress = server!.serverAddress.toString();
+  _notifyServerState(serviceAddress);
+  onServerAddressChange(serviceAddress);
   if (_waitForDdsToAdvertiseService) {
     await server!.outputConnectionInformation();
   }
 }
 
+Future<void> ddsDisconnectedCallback() async {
+  final serviceAddress = server!.serverAddress.toString();
+  _notifyServerState(serviceAddress);
+  onServerAddressChange(serviceAddress);
+}
+
 Future<Uri> createTempDirCallback(String base) async {
   final temp = await Directory.systemTemp.createTemp(base);
   // Underneath the temporary directory, create a directory with the
@@ -257,6 +266,7 @@
   VMServiceEmbedderHooks.cleanup = cleanupCallback;
   VMServiceEmbedderHooks.createTempDir = createTempDirCallback;
   VMServiceEmbedderHooks.ddsConnected = ddsConnectedCallback;
+  VMServiceEmbedderHooks.ddsDisconnected = ddsDisconnectedCallback;
   VMServiceEmbedderHooks.deleteDir = deleteDirCallback;
   VMServiceEmbedderHooks.writeFile = writeFileCallback;
   VMServiceEmbedderHooks.writeStreamFile = writeStreamFileCallback;
diff --git a/sdk/lib/_internal/vm/bin/vmservice_server.dart b/sdk/lib/_internal/vm/bin/vmservice_server.dart
index f084f29..383b19b 100644
--- a/sdk/lib/_internal/vm/bin/vmservice_server.dart
+++ b/sdk/lib/_internal/vm/bin/vmservice_server.dart
@@ -158,9 +158,14 @@
 
   /// Returns the server address including the auth token.
   Uri? get serverAddress {
-    if (!running) {
+    if (!running || _service.isExiting) {
       return null;
     }
+    // If DDS is connected it should be treated as the "true" VM service and be
+    // advertised as such.
+    if (_service.ddsUri != null) {
+      return _service.ddsUri;
+    }
     final server = _server!;
     final ip = server.address.address;
     final port = server.port;
@@ -364,32 +369,11 @@
           WebSocketClient(webSocket, _service);
         });
       } else {
-        // Forward the websocket connection request to DDS.
-        // The Javascript WebSocket implementation doesn't like websocket
-        // connection requests being redirected. Instead of redirecting, we'll
-        // just forward the connection manually if 'implicit-redirect' is
-        // provided as a protocol.
-        if (subprotocols != null) {
-          if (subprotocols.contains('implicit-redirect')) {
-            WebSocketTransformer.upgrade(request,
-                    protocolSelector: (_) => 'implicit-redirect',
-                    compression: CompressionOptions.compressionOff)
-                .then((WebSocket webSocket) async {
-              final ddsWs = await WebSocket.connect(
-                  _service.ddsUri!.replace(scheme: 'ws').toString());
-              ddsWs.addStream(webSocket);
-              webSocket.addStream(ddsWs);
-              webSocket.done.then((_) {
-                ddsWs.close();
-              });
-              ddsWs.done.then((_) {
-                webSocket.close();
-              });
-            });
-            return;
-          }
-        }
-        request.response.redirect(_service.ddsUri!);
+        request.response.statusCode = HttpStatus.forbidden;
+        request.response.write('Cannot connect directly to the VM service as '
+            'a Dart Development Service (DDS) instance has taken control and '
+            'can be found at ${_service.ddsUri}.');
+        request.response.close();
       }
       return;
     }
diff --git a/sdk/lib/vmservice/running_isolate.dart b/sdk/lib/vmservice/running_isolate.dart
index 029c6aa..8a6dd36 100644
--- a/sdk/lib/vmservice/running_isolate.dart
+++ b/sdk/lib/vmservice/running_isolate.dart
@@ -8,122 +8,13 @@
   final int portId;
   final SendPort sendPort;
   final String name;
-  final Set<String> _resumeApprovalsByName = {};
 
   RunningIsolate(this.portId, this.sendPort, this.name);
 
   String get serviceId => 'isolates/$portId';
 
-  static const kInvalidPauseEvent = -1;
-  static const kPauseOnStartMask = 1 << 0;
-  static const kPauseOnReloadMask = 1 << 1;
-  static const kPauseOnExitMask = 1 << 2;
-  static const kDefaultResumePermissionMask =
-      kPauseOnStartMask | kPauseOnReloadMask | kPauseOnExitMask;
-
-  /// Resumes the isolate if all clients which need to approve a resume have
-  /// done so. Called when the last client of a given name disconnects or
-  /// changes name to ensure we don't deadlock waiting for approval to resume
-  /// from a disconnected client.
-  Future<void> maybeResumeAfterClientChange(
-      VMService service, String disconnectedClientName) async {
-    // Remove approvals from the disconnected client.
-    _resumeApprovalsByName.remove(disconnectedClientName);
-
-    // If we've received approval to resume from all clients who care, clear
-    // approval state and resume.
-    var pauseType;
-    try {
-      pauseType = await _isolatePauseType(service, portId.toString());
-    } catch (_errorResponse) {
-      // ignore errors when attempting to retrieve isolate pause type
-      return;
-    }
-    if (pauseType != kInvalidPauseEvent &&
-        _shouldResume(service, null, pauseType)) {
-      _resumeApprovalsByName.clear();
-      await Message.forMethod('resume')
-        ..params.addAll({
-          'isolateId': portId,
-        })
-        ..sendToIsolate(sendPort);
-    }
-  }
-
-  bool _shouldResume(VMService service, Client? client, int pauseType) {
-    if (client != null) {
-      // Mark the approval by the client.
-      _resumeApprovalsByName.add(client.name);
-    }
-    final requiredClientApprovals = <String>{};
-    final permissions = service.clientResumePermissions;
-
-    // Determine which clients require approval for this pause type.
-    permissions.forEach((name, clientNamePermissions) {
-      if (clientNamePermissions.permissionsMask & pauseType != 0) {
-        requiredClientApprovals.add(name);
-      }
-    });
-
-    // We require at least a single client to resume, even if that client
-    // doesn't require resume approval.
-    if (_resumeApprovalsByName.isEmpty) {
-      return false;
-    }
-
-    // If all the required approvals are present, we should resume.
-    return _resumeApprovalsByName.containsAll(requiredClientApprovals);
-  }
-
-  Future<int> _isolatePauseType(VMService service, String isolateId) async {
-    final getIsolateMessage = Message.forMethod('getIsolate')
-      ..params.addAll({
-        'isolateId': isolateId,
-      });
-    final Response result = await routeRequest(service, getIsolateMessage);
-    final resultJson = result.decodeJson();
-    if (resultJson['result'] == null ||
-        resultJson['result']['pauseEvent'] == null) {
-      // Failed to send getIsolate message(due to isolate being de-registered
-      // for example).
-      throw result;
-    }
-    final pauseEvent = resultJson['result']['pauseEvent'];
-    const pauseEvents = <String, int>{
-      'PauseStart': kPauseOnStartMask,
-      'PausePostRequest': kPauseOnReloadMask,
-      'PauseExit': kPauseOnExitMask,
-    };
-    final kind = pauseEvent['kind'];
-    return pauseEvents[kind] ?? kInvalidPauseEvent;
-  }
-
-  Future<Response> _routeResumeRequest(
-      VMService service, Message message) async {
-    // If we've received approval to resume from all clients who care, clear
-    // approval state and resume.
-    var pauseType;
-    try {
-      pauseType = await _isolatePauseType(service, message.params['isolateId']);
-    } on Response catch (errorResponse) {
-      return errorResponse;
-    }
-    if (pauseType == kInvalidPauseEvent ||
-        _shouldResume(service, message.client, pauseType)) {
-      _resumeApprovalsByName.clear();
-      return message.sendToIsolate(sendPort);
-    }
-
-    // We're still awaiting some approvals. Simply return success, but don't
-    // resume yet.
-    return Response(ResponsePayloadKind.String, encodeSuccess(message));
-  }
-
   @override
   Future<Response> routeRequest(VMService service, Message message) {
-    if (message.method == 'resume') {
-      return _routeResumeRequest(service, message);
-    }
     // Send message to isolate.
     return message.sendToIsolate(sendPort);
   }
diff --git a/sdk/lib/vmservice/vmservice.dart b/sdk/lib/vmservice/vmservice.dart
index 1581e70..e0ccfaa 100644
--- a/sdk/lib/vmservice/vmservice.dart
+++ b/sdk/lib/vmservice/vmservice.dart
@@ -142,6 +142,9 @@
 /// Called when DDS has connected.
 typedef Future<void> DdsConnectedCallback();
 
+/// Called when DDS has disconnected.
+typedef Future<void> DdsDisconnectedCallback();
+
 /// Called when the service is exiting.
 typedef Future CleanupCallback();
 
@@ -182,6 +185,7 @@
   static ServerStartCallback? serverStart;
   static ServerStopCallback? serverStop;
   static DdsConnectedCallback? ddsConnected;
+  static DdsDisconnectedCallback? ddsDisconnected;
   static CleanupCallback? cleanup;
   static CreateTempDirCallback? createTempDir;
   static DeleteDirCallback? deleteDir;
@@ -224,8 +228,6 @@
 
   final devfs = DevFS();
 
-  Uri? vmServiceUri;
-
   Uri? get ddsUri => _ddsUri;
   Uri? _ddsUri;
 
@@ -254,117 +256,6 @@
     return encodeSuccess(message);
   }
 
-  void _clearClientName(Client client) {
-    final name = client.name;
-    client.name = null;
-    final clientsForName = clientResumePermissions[name];
-    if (clientsForName != null) {
-      clientsForName.clients.remove(client);
-      // If this was the last client with a given name, cleanup resume
-      // permissions.
-      if (clientsForName.clients.isEmpty) {
-        clientResumePermissions.remove(name);
-
-        // Check to see if we need to resume any isolates now that the last
-        // client of a given name has disconnected or changed names.
-        //
-        // An isolate will be resumed in this situation if:
-        //
-        // 1) This client required resume approvals for the current pause event
-        // associated with the isolate and all other required resume approvals
-        // have been provided by other clients.
-        //
-        // OR
-        //
-        // 2) This client required resume approvals for the current pause event
-        // associated with the isolate, no other clients require resume approvals
-        // for the current pause event, and at least one client has issued a resume
-        // request.
-        runningIsolates.isolates.forEach((_, isolate) async =>
-            await isolate.maybeResumeAfterClientChange(this, name));
-      }
-    }
-  }
-
-  /// Sets the name associated with a [Client].
-  ///
-  /// If any resume approvals were set for this client previously they will
-  /// need to be reset after a name change.
-  String _setClientName(Message message) {
-    final client = message.client!;
-    if (!message.params.containsKey('name')) {
-      return encodeRpcError(message, kInvalidParams,
-          details: "setClientName: missing required parameter 'name'");
-    }
-    final name = message.params['name'];
-    if (name is! String) {
-      return encodeRpcError(message, kInvalidParams,
-          details: "setClientName: invalid 'name' parameter: $name");
-    }
-    _setClientNameHelper(client, name);
-    return encodeSuccess(message);
-  }
-
-  void _setClientNameHelper(Client client, String name) {
-    _clearClientName(client);
-    name = name.isEmpty ? client.defaultClientName : name;
-    client.name = name;
-    clientResumePermissions.putIfAbsent(
-        client.name, () => _ClientResumePermissions());
-    clientResumePermissions[name]!.clients.add(client);
-  }
-
-  String _getClientName(Message message) => encodeResult(message, {
-        'type': 'ClientName',
-        'name': message.client!.name,
-      });
-
-  String _requirePermissionToResume(Message message) {
-    bool parsePermission(String argName) {
-      final arg = message.params[argName];
-      if (arg == null) {
-        return false;
-      }
-      if (arg is! bool) {
-        throw encodeRpcError(message, kInvalidParams,
-            details: "requirePermissionToResume: invalid '$argName': $arg");
-      }
-      return arg;
-    }
-
-    final client = message.client!;
-    int pauseTypeMask = 0;
-    try {
-      if (parsePermission('onPauseStart')) {
-        pauseTypeMask |= RunningIsolate.kPauseOnStartMask;
-      }
-      if (parsePermission('onPauseReload')) {
-        pauseTypeMask |= RunningIsolate.kPauseOnReloadMask;
-      }
-      if (parsePermission('onPauseExit')) {
-        pauseTypeMask |= RunningIsolate.kPauseOnExitMask;
-      }
-    } on dynamic catch (rpcError) {
-      return rpcError;
-    }
-
-    clientResumePermissions[client.name]!.permissionsMask = pauseTypeMask;
-    return encodeSuccess(message);
-  }
-
-  String _getWebSocketTarget(Message message) {
-    Uri uri = ((_ddsUri != null) ? _ddsUri : vmServiceUri)!;
-    uri = uri.replace(scheme: 'ws', pathSegments: [
-      // Strip empty path segment which causes an extra / to be inserted.
-      ...uri.pathSegments.where((e) => e.isNotEmpty),
-      'ws',
-    ]);
-    return encodeResult(message, {
-      'type': 'WebSocketTarget',
-      'uri': uri.toString(),
-    });
-  }
-
   void _addClient(Client client) {
     assert(client.streams.isEmpty);
     assert(client.services.isEmpty);
@@ -380,9 +271,6 @@
       }
     }
 
-    // Clean up client approvals state.
-    _clearClientName(client);
-
     for (final service in client.services.keys) {
       _eventMessageHandler(
           'Service',
@@ -411,8 +299,9 @@
       final acceptNewWebSocketConnections =
           VMServiceEmbedderHooks.acceptNewWebSocketConnections;
       if (_ddsUri != null && acceptNewWebSocketConnections != null) {
-        acceptNewWebSocketConnections(true);
         _ddsUri = null;
+        VMServiceEmbedderHooks.ddsDisconnected!();
+        acceptNewWebSocketConnections(true);
       }
     }
   }
@@ -581,11 +470,9 @@
   }
 
   Client? _findFirstClientThatHandlesService(String service) {
-    if (clients != null) {
-      for (Client c in clients) {
-        if (c.services.containsKey(service)) {
-          return c;
-        }
+    for (Client c in clients) {
+      if (c.services.containsKey(service)) {
+        return c;
       }
     }
     return null;
@@ -707,22 +594,20 @@
     final namespace = _getNamespace(message.method!);
     final method = _getMethod(message.method!);
     final client = clients[namespace];
-    if (client != null) {
-      if (client.services.containsKey(method)) {
-        final id = _serviceRequests.newId();
-        final oldId = message.serial;
-        final completer = Completer<String>();
-        client.serviceHandles[id] = (Message? m) {
-          if (m != null) {
-            completer.complete(json.encode(m.forwardToJson({'id': oldId})));
-          } else {
-            completer.complete(encodeRpcError(message, kServiceDisappeared));
-          }
-        };
-        client.post(
-            Response.json(message.forwardToJson({'id': id, 'method': method})));
-        return completer.future;
-      }
+    if (client.services.containsKey(method)) {
+      final id = _serviceRequests.newId();
+      final oldId = message.serial;
+      final completer = Completer<String>();
+      client.serviceHandles[id] = (Message? m) {
+        if (m != null) {
+          completer.complete(json.encode(m.forwardToJson({'id': oldId})));
+        } else {
+          completer.complete(encodeRpcError(message, kServiceDisappeared));
+        }
+      };
+      client.post(
+          Response.json(message.forwardToJson({'id': id, 'method': method})));
+      return completer.future;
     }
     return encodeRpcError(message, kMethodNotFound,
         details: 'Unknown service: ${message.method}');
@@ -774,21 +659,9 @@
       if (message.method == 'registerService') {
         return await _registerService(message);
       }
-      if (message.method == 'setClientName') {
-        return _setClientName(message);
-      }
-      if (message.method == 'getClientName') {
-        return _getClientName(message);
-      }
-      if (message.method == 'requirePermissionToResume') {
-        return _requirePermissionToResume(message);
-      }
       if (message.method == 'getSupportedProtocols') {
         return await _getSupportedProtocols(message);
       }
-      if (message.method == 'getWebSocketTarget') {
-        return _getWebSocketTarget(message);
-      }
       if (devfs.shouldHandleMessage(message)) {
         return await devfs.handleMessage(message);
       }
@@ -834,7 +707,6 @@
 
 /// Notify the VM that the server's address has changed.
 void onServerAddressChange(String? address) {
-  VMService().vmServiceUri = (address != null) ? Uri.parse(address) : null;
   _onServerAddressChange(address);
 }
 
diff --git a/tools/VERSION b/tools/VERSION
index f93499e..be4759b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 94
+PRERELEASE 95
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/spec_parser/Dart.g b/tools/spec_parser/Dart.g
index 039e65f..08a433e 100644
--- a/tools/spec_parser/Dart.g
+++ b/tools/spec_parser/Dart.g
@@ -493,12 +493,17 @@
     |    SUPER unconditionalAssignableSelector
     |    constObjectExpression
     |    newExpression
+    |    constructorInvocation
     |    functionPrimary
     |    '(' expression ')'
     |    literal
     |    identifier
     ;
 
+constructorInvocation
+    :    typeName typeArguments '.' identifier arguments
+    ;
+
 literal
     :    nullLiteral
     |    booleanLiteral
@@ -812,14 +817,9 @@
 
 postfixExpression
     :    assignableExpression postfixOperator
-    |    constructorInvocation selector*
     |    primary selector*
     ;
 
-constructorInvocation
-    :    typeName typeArguments '.' identifier arguments
-    ;
-
 postfixOperator
     :    incrementOperator
     ;
@@ -841,7 +841,6 @@
 
 assignableExpression
     :    SUPER unconditionalAssignableSelector
-    |    constructorInvocation assignableSelectorPart
     |    primary assignableSelectorPart
     |    identifier
     ;