[roll] Update third-party dart packages

Roller-URL: https://ci.chromium.org/b/8842972088262781840
Cq-Cl-Tag: roller-builder:flutter-with-deps-roller
Cq-Cl-Tag: roller-bid:8842972088262781840
CQ-Do-Not-Cancel-Tryjobs: true
Change-Id: I827c858c507e8678b250159cad5ae40a75cb99f5
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/dart-pkg/+/550621
Reviewed-by: GI Roller <global-integration-roller@fuchsia-infra.iam.gserviceaccount.com>
Commit-Queue: GI Roller <global-integration-roller@fuchsia-infra.iam.gserviceaccount.com>
diff --git a/_fe_analyzer_shared/BUILD.gn b/_fe_analyzer_shared/BUILD.gn
index 1b4727a..559f149 100644
--- a/_fe_analyzer_shared/BUILD.gn
+++ b/_fe_analyzer_shared/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for _fe_analyzer_shared-22.0.0
+# This file is generated by package_importer.py for _fe_analyzer_shared-22.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/analyzer/BUILD.gn b/analyzer/BUILD.gn
index 71e57d8..f886708 100644
--- a/analyzer/BUILD.gn
+++ b/analyzer/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for analyzer-1.7.0
+# This file is generated by package_importer.py for analyzer-1.7.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/archive/BUILD.gn b/archive/BUILD.gn
index 62ed5ab..f4007eb 100644
--- a/archive/BUILD.gn
+++ b/archive/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for archive-3.1.2
+# This file is generated by package_importer.py for archive-3.1.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/args/BUILD.gn b/args/BUILD.gn
index 806cfa7..edd4835 100644
--- a/args/BUILD.gn
+++ b/args/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for args-2.1.1
+# This file is generated by package_importer.py for args-2.1.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/async/BUILD.gn b/async/BUILD.gn
index 95422ca..fd8ed9b 100644
--- a/async/BUILD.gn
+++ b/async/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for async-2.7.0
+# This file is generated by package_importer.py for async-2.7.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/boolean_selector/BUILD.gn b/boolean_selector/BUILD.gn
index 8b7ab7d..2a61c2b 100644
--- a/boolean_selector/BUILD.gn
+++ b/boolean_selector/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for boolean_selector-2.1.0
+# This file is generated by package_importer.py for boolean_selector-2.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/browser_launcher/BUILD.gn b/browser_launcher/BUILD.gn
index eccfea4..29b6514 100644
--- a/browser_launcher/BUILD.gn
+++ b/browser_launcher/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for browser_launcher-1.0.0
+# This file is generated by package_importer.py for browser_launcher-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/build/BUILD.gn b/build/BUILD.gn
index f52feed..720197a 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for build-2.0.2
+# This file is generated by package_importer.py for build-2.0.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/built_collection/.gitignore b/built_collection/.gitignore
deleted file mode 100644
index f6d9da4..0000000
--- a/built_collection/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-pubspec.lock
-packages
-.dart_tool
-.idea
-.packages
-.pub
-**/*.iml
diff --git a/built_collection/.travis.yml b/built_collection/.travis.yml
deleted file mode 100644
index b358d41..0000000
--- a/built_collection/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: dart
-dart: dev
-script:
-  - tool/presubmit
diff --git a/built_collection/BUILD.gn b/built_collection/BUILD.gn
index 30d3514..1442fad 100644
--- a/built_collection/BUILD.gn
+++ b/built_collection/BUILD.gn
@@ -1,17 +1,15 @@
-# This file is generated by importer.py for built_collection-4.3.2
+# This file is generated by package_importer.py for built_collection-5.1.0
 
 import("//build/dart/dart_library.gni")
 
 dart_library("built_collection") {
   package_name = "built_collection"
 
-  language_version = "2.6"
+  language_version = "2.12"
 
   disable_analysis = true
 
   deps = [
-    "//third_party/dart-pkg/pub/collection",
-    "//third_party/dart-pkg/pub/quiver",
   ]
 
   sources = [
@@ -19,8 +17,11 @@
     "src/internal/copy_on_write_list.dart",
     "src/internal/copy_on_write_map.dart",
     "src/internal/copy_on_write_set.dart",
+    "src/internal/hash.dart",
     "src/internal/iterables.dart",
+    "src/internal/null_safety.dart",
     "src/internal/test_helpers.dart",
+    "src/internal/unmodifiable_set.dart",
     "src/iterable.dart",
     "src/iterable/built_iterable.dart",
     "src/list.dart",
diff --git a/built_collection/CHANGELOG.md b/built_collection/CHANGELOG.md
index a5a85c3..c346355 100644
--- a/built_collection/CHANGELOG.md
+++ b/built_collection/CHANGELOG.md
@@ -1,5 +1,22 @@
 # Changelog
 
+## 5.1.0
+
+- Allow collections with nullable types, for example `BuiltList<T?>`.
+- Allow key/value types to be `dynamic`. This can be useful occasionally, and
+  with Dart 2 and null safety it's much harder to use `dynamic` by accident.
+
+## 5.0.0
+
+- Stable null safe release.
+
+## 5.0.0-nullsafety.0
+
+- Migrate to NNBD.
+- Deps on `package:collection` and `package:quiver` have been removed.
+- Multimap builders no longer have `addAll` methods that accept multimaps from
+  quiver. But, the constructors still work with quiver multimaps.
+
 ## 4.3.2
 
 - Add an `example` folder with some example code.
diff --git a/built_collection/README.md b/built_collection/README.md
index 7689656..9f223e3 100644
--- a/built_collection/README.md
+++ b/built_collection/README.md
@@ -3,7 +3,7 @@
 ## Introduction
 
 Built Collections are immutable collections using the
-[builder pattern](http://en.wikipedia.org/wiki/Builder_pattern).
+[builder pattern](https://en.wikipedia.org/wiki/Builder_pattern).
 
 Each of the core SDK collections is split in two: a mutable builder class
 and an immutable "built" class. Builders are for computation,
@@ -22,24 +22,11 @@
 * are immutable, if the elements/keys/values used are immutable;
 * are comparable;
 * are hashable;
-* reject nulls;
-* require generic type parameters;
-* reject wrong-type elements;
 * use copy-on-write to avoid copying unnecessarily.
 
 See below for details on each of these points.
 
 
-## A note about strong mode
-
-Please note that from version `1.1.0` built_collection must be used in
-conjunction with
-[strong mode](https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md)
-to get all the type guarantees. That is, your project must have no warnings or
-errors when analyzed with the strong mode analyzer. This allows some runtime
-checks to be skipped because the equivalent check can be done statically.
-
-
 ## Recommended Style
 
 A project can benefit greatly from using Built Collections throughout.
@@ -91,26 +78,6 @@
 comparisons.
 
 
-## Built Collections Reject Nulls
-
-A `null` in a collection is usually a bug, so Built Collections and their
-builders throw if given a `null` element, key or value.
-
-
-## Built Collections Require Generic Type Parameters
-
-A `List<dynamic>` is error-prone because it can be assigned to a `List` of
-any type without warning. So, all Built Collections must be created with
-explicit element, key or value types.
-
-
-## Built Collections Reject Wrong-type Elements, Keys and Values
-
-Collections that happen to contain elements, keys or values that are not of
-the right type can lead to difficult-to-find bugs. So, all Built
-Collections and their builders are aggressive about validating types, even
-with checked mode disabled.
-
 
 ## Built Collections Avoid Copying Unnecessarily
 
diff --git a/built_collection/benchmark/bin/main.dart b/built_collection/benchmark/bin/main.dart
index a1f7b32..78814a9 100644
--- a/built_collection/benchmark/bin/main.dart
+++ b/built_collection/benchmark/bin/main.dart
@@ -4,6 +4,4 @@
 
 import 'package:benchmark/benchmark.dart';
 
-Future<void> main() async {
-  BuiltCollectionBenchmark().run();
-}
+Future<void> main() => BuiltCollectionBenchmark().run();
diff --git a/built_collection/benchmark/lib/benchmark.dart b/built_collection/benchmark/lib/benchmark.dart
index 8899aa3..58a3f67 100644
--- a/built_collection/benchmark/lib/benchmark.dart
+++ b/built_collection/benchmark/lib/benchmark.dart
@@ -15,7 +15,7 @@
   };
 
   final Map<String, void Function(SetBuilder<int>, Iterable<int>)>
-  setBuilderFunctions = {
+      setBuilderFunctions = {
     'addAll': (b, iterable) => b.addAll(iterable),
   };
 
@@ -30,8 +30,8 @@
       var function = entry.value;
 
       Iterable<int> list = List<int>.generate(1000, (x) => x);
-      Iterable<int> fastLazyIterable = list.map((x) => x + 1);
-      Iterable<int> slowLazyIterable = list.map(_shortDelay);
+      var fastLazyIterable = list.map((x) => x + 1);
+      var slowLazyIterable = list.map(_shortDelay);
       var builderFactory = () => ListBuilder<int>()..addAll(list);
 
       _benchmark('ListBuilder.$name,list', function, builderFactory, list);
@@ -48,8 +48,8 @@
       var function = entry.value;
 
       Iterable<int> list = List<int>.generate(1000, (x) => x);
-      Iterable<int> fastLazyIterable = list.map((x) => x + 1);
-      Iterable<int> slowLazyIterable = list.map(_shortDelay);
+      var fastLazyIterable = list.map((x) => x + 1);
+      var slowLazyIterable = list.map(_shortDelay);
       var builderFactory = () => SetBuilder<int>();
 
       _benchmark('SetBuilder.$name,list', function, builderFactory, list);
@@ -88,6 +88,6 @@
   for (var i = 0; i != 100; ++i) {
     total += i;
   }
-  if (total == 0) return null;
+  if (total == 0) throw StateError('Just checking to prevent optimization.');
   return element;
 }
diff --git a/built_collection/benchmark/pubspec.yaml b/built_collection/benchmark/pubspec.yaml
index 93aa7d9..0ac6241 100644
--- a/built_collection/benchmark/pubspec.yaml
+++ b/built_collection/benchmark/pubspec.yaml
@@ -7,7 +7,7 @@
 homepage: https://github.com/google/built_collection.dart
 
 environment:
-  sdk: '>=2-0-0-dev <3.0.0'
+  sdk: '>=2.9.0-18.0 <3.0.0'
 
 dev_dependencies:
   build_runner: any
diff --git a/built_collection/benchmark/web/main.dart b/built_collection/benchmark/web/main.dart
index a1f7b32..4c8c03b 100644
--- a/built_collection/benchmark/web/main.dart
+++ b/built_collection/benchmark/web/main.dart
@@ -4,6 +4,4 @@
 
 import 'package:benchmark/benchmark.dart';
 
-Future<void> main() async {
-  BuiltCollectionBenchmark().run();
-}
+Future<void> main() async => BuiltCollectionBenchmark().run();
diff --git a/built_collection/example/example.dart b/built_collection/example/example.dart
index 32ef1fc..419db22 100644
--- a/built_collection/example/example.dart
+++ b/built_collection/example/example.dart
@@ -54,4 +54,9 @@
   // `ListMultimap` and `SetMultimap` from `package:quiver`. For information
   // on these, and full details on all the APIs, please see the package
   // [dartdoc](https://pub.dev/documentation/built_collection/latest).
+
+  // Use the values so the analyzer is happy.
+  print(builtList);
+  print(builtSet);
+  print(builtMap);
 }
diff --git a/built_collection/example/pubspec.yaml b/built_collection/example/pubspec.yaml
index 47d4400..8c42041 100644
--- a/built_collection/example/pubspec.yaml
+++ b/built_collection/example/pubspec.yaml
@@ -5,7 +5,7 @@
 homepage: https://github.com/google/built_collection.dart
 
 environment:
-  sdk: '>=2-0-0-dev <3.0.0'
+  sdk: '>=2.9.0-18.0 <3.0.0'
 
 dependency_overrides:
   built_collection:
diff --git a/built_collection/lib/src/internal/copy_on_write_list.dart b/built_collection/lib/src/internal/copy_on_write_list.dart
index da28241..a41f28d 100644
--- a/built_collection/lib/src/internal/copy_on_write_list.dart
+++ b/built_collection/lib/src/internal/copy_on_write_list.dart
@@ -32,7 +32,7 @@
   List<T> cast<T>() => CopyOnWriteList<T>(_list.cast<T>(), _growable);
 
   @override
-  bool contains(Object element) => _list.contains(element);
+  bool contains(Object? element) => _list.contains(element);
 
   @override
   E elementAt(int index) => _list.elementAt(index);
@@ -47,7 +47,7 @@
   E get first => _list.first;
 
   @override
-  E firstWhere(bool Function(E) test, {E Function() orElse}) =>
+  E firstWhere(bool Function(E) test, {E Function()? orElse}) =>
       _list.firstWhere(test, orElse: orElse);
 
   @override
@@ -86,14 +86,14 @@
   E get last => _list.last;
 
   @override
-  int lastIndexOf(E element, [int start]) => _list.lastIndexOf(element, start);
+  int lastIndexOf(E element, [int? start]) => _list.lastIndexOf(element, start);
 
   @override
-  int lastIndexWhere(bool Function(E) test, [int start]) =>
+  int lastIndexWhere(bool Function(E) test, [int? start]) =>
       _list.lastIndexWhere(test, start);
 
   @override
-  E lastWhere(bool Function(E) test, {E Function() orElse}) =>
+  E lastWhere(bool Function(E) test, {E Function()? orElse}) =>
       _list.lastWhere(test, orElse: orElse);
 
   @override
@@ -109,7 +109,7 @@
   E get single => _list.single;
 
   @override
-  E singleWhere(bool Function(E) test, {E Function() orElse}) =>
+  E singleWhere(bool Function(E) test, {E Function()? orElse}) =>
       _list.singleWhere(test, orElse: orElse);
 
   @override
@@ -119,7 +119,7 @@
   Iterable<E> skipWhile(bool Function(E) test) => _list.skipWhile(test);
 
   @override
-  List<E> sublist(int start, [int end]) => _list.sublist(start, end);
+  List<E> sublist(int start, [int? end]) => _list.sublist(start, end);
 
   @override
   Iterable<E> take(int count) => _list.take(count);
@@ -178,13 +178,13 @@
   }
 
   @override
-  void sort([int Function(E, E) compare]) {
+  void sort([int Function(E, E)? compare]) {
     _maybeCopyBeforeWrite();
     _list.sort(compare);
   }
 
   @override
-  void shuffle([Random random]) {
+  void shuffle([Random? random]) {
     _maybeCopyBeforeWrite();
     _list.shuffle(random);
   }
@@ -214,7 +214,7 @@
   }
 
   @override
-  bool remove(Object value) {
+  bool remove(Object? value) {
     _maybeCopyBeforeWrite();
     return _list.remove(value);
   }
@@ -256,7 +256,7 @@
   }
 
   @override
-  void fillRange(int start, int end, [E fillValue]) {
+  void fillRange(int start, int end, [E? fillValue]) {
     _maybeCopyBeforeWrite();
     _list.fillRange(start, end, fillValue);
   }
diff --git a/built_collection/lib/src/internal/copy_on_write_map.dart b/built_collection/lib/src/internal/copy_on_write_map.dart
index 12225da..f9e4c5e 100644
--- a/built_collection/lib/src/internal/copy_on_write_map.dart
+++ b/built_collection/lib/src/internal/copy_on_write_map.dart
@@ -5,7 +5,7 @@
 typedef _MapFactory<K, V> = Map<K, V> Function();
 
 class CopyOnWriteMap<K, V> implements Map<K, V> {
-  final _MapFactory<K, V> _mapFactory;
+  final _MapFactory<K, V>? _mapFactory;
   bool _copyBeforeWrite;
   Map<K, V> _map;
 
@@ -14,16 +14,16 @@
   // Read-only methods: just forward.
 
   @override
-  V operator [](Object key) => _map[key];
+  V? operator [](Object? key) => _map[key];
 
   @override
   Map<K2, V2> cast<K2, V2>() => CopyOnWriteMap<K2, V2>(_map.cast<K2, V2>());
 
   @override
-  bool containsKey(Object key) => _map.containsKey(key);
+  bool containsKey(Object? key) => _map.containsKey(key);
 
   @override
-  bool containsValue(Object value) => _map.containsValue(value);
+  bool containsValue(Object? value) => _map.containsValue(value);
 
   @override
   Iterable<MapEntry<K, V>> get entries => _map.entries;
@@ -82,7 +82,7 @@
   }
 
   @override
-  V remove(Object key) {
+  V? remove(Object? key) {
     _maybeCopyBeforeWrite();
     return _map.remove(key);
   }
@@ -97,7 +97,7 @@
   String toString() => _map.toString();
 
   @override
-  V update(K key, V Function(V) update, {V Function() ifAbsent}) {
+  V update(K key, V Function(V) update, {V Function()? ifAbsent}) {
     _maybeCopyBeforeWrite();
     return _map.update(key, update, ifAbsent: ifAbsent);
   }
@@ -114,7 +114,7 @@
     if (!_copyBeforeWrite) return;
     _copyBeforeWrite = false;
     _map = _mapFactory != null
-        ? (_mapFactory()..addAll(_map))
+        ? (_mapFactory!()..addAll(_map))
         : Map<K, V>.from(_map);
   }
 }
diff --git a/built_collection/lib/src/internal/copy_on_write_set.dart b/built_collection/lib/src/internal/copy_on_write_set.dart
index ff25d8e..42f7aaf 100644
--- a/built_collection/lib/src/internal/copy_on_write_set.dart
+++ b/built_collection/lib/src/internal/copy_on_write_set.dart
@@ -5,7 +5,7 @@
 typedef _SetFactory<E> = Set<E> Function();
 
 class CopyOnWriteSet<E> implements Set<E> {
-  final _SetFactory<E> _setFactory;
+  final _SetFactory<E>? _setFactory;
   bool _copyBeforeWrite;
   Set<E> _set;
 
@@ -17,19 +17,19 @@
   int get length => _set.length;
 
   @override
-  E lookup(Object object) => _set.lookup(object);
+  E? lookup(Object? object) => _set.lookup(object);
 
   @override
-  Set<E> intersection(Set<Object> other) => _set.intersection(other);
+  Set<E> intersection(Set<Object?> other) => _set.intersection(other);
 
   @override
   Set<E> union(Set<E> other) => _set.union(other);
 
   @override
-  Set<E> difference(Set<Object> other) => _set.difference(other);
+  Set<E> difference(Set<Object?> other) => _set.difference(other);
 
   @override
-  bool containsAll(Iterable<Object> other) => _set.containsAll(other);
+  bool containsAll(Iterable<Object?> other) => _set.containsAll(other);
 
   @override
   bool any(bool Function(E) test) => _set.any(test);
@@ -38,7 +38,7 @@
   Set<T> cast<T>() => CopyOnWriteSet<T>(_set.cast<T>());
 
   @override
-  bool contains(Object element) => _set.contains(element);
+  bool contains(Object? element) => _set.contains(element);
 
   @override
   E elementAt(int index) => _set.elementAt(index);
@@ -53,7 +53,7 @@
   E get first => _set.first;
 
   @override
-  E firstWhere(bool Function(E) test, {E Function() orElse}) =>
+  E firstWhere(bool Function(E) test, {E Function()? orElse}) =>
       _set.firstWhere(test, orElse: orElse);
 
   @override
@@ -82,7 +82,7 @@
   E get last => _set.last;
 
   @override
-  E lastWhere(bool Function(E) test, {E Function() orElse}) =>
+  E lastWhere(bool Function(E) test, {E Function()? orElse}) =>
       _set.lastWhere(test, orElse: orElse);
 
   @override
@@ -95,7 +95,7 @@
   E get single => _set.single;
 
   @override
-  E singleWhere(bool Function(E) test, {E Function() orElse}) =>
+  E singleWhere(bool Function(E) test, {E Function()? orElse}) =>
       _set.singleWhere(test, orElse: orElse);
 
   @override
@@ -143,7 +143,7 @@
   }
 
   @override
-  bool remove(Object value) {
+  bool remove(Object? value) {
     _maybeCopyBeforeWrite();
     return _set.remove(value);
   }
@@ -161,13 +161,13 @@
   }
 
   @override
-  void removeAll(Iterable<Object> elements) {
+  void removeAll(Iterable<Object?> elements) {
     _maybeCopyBeforeWrite();
     _set.removeAll(elements);
   }
 
   @override
-  void retainAll(Iterable<Object> elements) {
+  void retainAll(Iterable<Object?> elements) {
     _maybeCopyBeforeWrite();
     _set.retainAll(elements);
   }
@@ -180,7 +180,8 @@
   void _maybeCopyBeforeWrite() {
     if (!_copyBeforeWrite) return;
     _copyBeforeWrite = false;
-    _set =
-        _setFactory != null ? (_setFactory()..addAll(_set)) : Set<E>.from(_set);
+    _set = _setFactory != null
+        ? (_setFactory!()..addAll(_set))
+        : Set<E>.from(_set);
   }
 }
diff --git a/built_collection/lib/src/internal/hash.dart b/built_collection/lib/src/internal/hash.dart
new file mode 100644
index 0000000..94ec240
--- /dev/null
+++ b/built_collection/lib/src/internal/hash.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2020, Google Inc. 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.
+
+/// Generates a hash code for multiple [objects].
+int hashObjects(Iterable objects) =>
+    _finish(objects.fold(0, (h, i) => _combine(h, i.hashCode)));
+
+/// Generates a hash code for two objects.
+int hash2(a, b) => _finish(_combine(_combine(0, a.hashCode), b.hashCode));
+
+// Jenkins hash functions.
+
+int _combine(int hash, int value) {
+  hash = 0x1fffffff & (hash + value);
+  hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+  return hash ^ (hash >> 6);
+}
+
+int _finish(int hash) {
+  hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
+  hash = hash ^ (hash >> 11);
+  return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+}
diff --git a/built_collection/lib/src/internal/iterables.dart b/built_collection/lib/src/internal/iterables.dart
index 259d77a..ca32ebf 100644
--- a/built_collection/lib/src/internal/iterables.dart
+++ b/built_collection/lib/src/internal/iterables.dart
@@ -6,7 +6,7 @@
 
 /// Evaluates a lazy iterable.
 ///
-/// A whitelist of known non-lazy types is returned directly instead.
+/// Known non-lazy types are returned directly instead.
 Iterable<E> evaluateIterable<E>(Iterable<E> iterable) {
   if (iterable is! List && iterable is! BuiltIterable && iterable is! Set) {
     iterable = iterable.toList();
diff --git a/built_collection/lib/src/internal/null_safety.dart b/built_collection/lib/src/internal/null_safety.dart
new file mode 100644
index 0000000..743f538
--- /dev/null
+++ b/built_collection/lib/src/internal/null_safety.dart
@@ -0,0 +1,2 @@
+/// Whether runtime sound mode is enabled.
+final bool isSoundMode = <int?>[] is! List<int>;
diff --git a/built_collection/lib/src/internal/test_helpers.dart b/built_collection/lib/src/internal/test_helpers.dart
index c574fa8..c05cfa0 100644
--- a/built_collection/lib/src/internal/test_helpers.dart
+++ b/built_collection/lib/src/internal/test_helpers.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
+// Copyright (c) 2020, Google Inc. 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.
 
diff --git a/built_collection/lib/src/internal/unmodifiable_set.dart b/built_collection/lib/src/internal/unmodifiable_set.dart
new file mode 100644
index 0000000..7e98647
--- /dev/null
+++ b/built_collection/lib/src/internal/unmodifiable_set.dart
@@ -0,0 +1,155 @@
+// Copyright (c) 2020, Google Inc. 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.
+
+class UnmodifiableSetView<E> implements Set<E> {
+  final Set<E> _set;
+
+  UnmodifiableSetView(this._set);
+
+  // Read-only methods: just forward.
+
+  @override
+  int get length => _set.length;
+
+  @override
+  E? lookup(Object? object) => _set.lookup(object);
+
+  @override
+  Set<E> intersection(Set<Object?> other) => _set.intersection(other);
+
+  @override
+  Set<E> union(Set<E> other) => _set.union(other);
+
+  @override
+  Set<E> difference(Set<Object?> other) => _set.difference(other);
+
+  @override
+  bool containsAll(Iterable<Object?> other) => _set.containsAll(other);
+
+  @override
+  bool any(bool Function(E) test) => _set.any(test);
+
+  @override
+  Set<T> cast<T>() => UnmodifiableSetView<T>(_set.cast<T>());
+
+  @override
+  bool contains(Object? element) => _set.contains(element);
+
+  @override
+  E elementAt(int index) => _set.elementAt(index);
+
+  @override
+  bool every(bool Function(E) test) => _set.every(test);
+
+  @override
+  Iterable<T> expand<T>(Iterable<T> Function(E) f) => _set.expand(f);
+
+  @override
+  E get first => _set.first;
+
+  @override
+  E firstWhere(bool Function(E) test, {E Function()? orElse}) =>
+      _set.firstWhere(test, orElse: orElse);
+
+  @override
+  T fold<T>(T initialValue, T Function(T, E) combine) =>
+      _set.fold(initialValue, combine);
+
+  @override
+  Iterable<E> followedBy(Iterable<E> other) => _set.followedBy(other);
+
+  @override
+  void forEach(void Function(E) f) => _set.forEach(f);
+
+  @override
+  bool get isEmpty => _set.isEmpty;
+
+  @override
+  bool get isNotEmpty => _set.isNotEmpty;
+
+  @override
+  Iterator<E> get iterator => _set.iterator;
+
+  @override
+  String join([String separator = '']) => _set.join(separator);
+
+  @override
+  E get last => _set.last;
+
+  @override
+  E lastWhere(bool Function(E) test, {E Function()? orElse}) =>
+      _set.lastWhere(test, orElse: orElse);
+
+  @override
+  Iterable<T> map<T>(T Function(E) f) => _set.map(f);
+
+  @override
+  E reduce(E Function(E, E) combine) => _set.reduce(combine);
+
+  @override
+  E get single => _set.single;
+
+  @override
+  E singleWhere(bool Function(E) test, {E Function()? orElse}) =>
+      _set.singleWhere(test, orElse: orElse);
+
+  @override
+  Iterable<E> skip(int count) => _set.skip(count);
+
+  @override
+  Iterable<E> skipWhile(bool Function(E) test) => _set.skipWhile(test);
+
+  @override
+  Iterable<E> take(int count) => _set.take(count);
+
+  @override
+  Iterable<E> takeWhile(bool Function(E) test) => _set.takeWhile(test);
+
+  @override
+  List<E> toList({bool growable = true}) => _set.toList(growable: growable);
+
+  @override
+  Set<E> toSet() => _set.toSet();
+
+  @override
+  Iterable<E> where(bool Function(E) test) => _set.where(test);
+
+  @override
+  Iterable<T> whereType<T>() => _set.whereType<T>();
+
+  // Mutating methods: just throw!
+
+  @override
+  bool add(E value) => _throw();
+
+  @override
+  void addAll(Iterable<E> iterable) => _throw();
+
+  @override
+  void clear() => _throw();
+
+  @override
+  bool remove(Object? value) => _throw();
+
+  @override
+  void removeWhere(bool Function(E) test) => _throw();
+
+  @override
+  void retainWhere(bool Function(E) test) => _throw();
+
+  @override
+  void removeAll(Iterable<Object?> elements) => _throw();
+
+  @override
+  void retainAll(Iterable<Object?> elements) => _throw();
+
+  @override
+  String toString() => _set.toString();
+
+  // Internal.
+
+  static T _throw<T>() {
+    throw UnsupportedError('Cannot modify an unmodifiable Set');
+  }
+}
diff --git a/built_collection/lib/src/iterable.dart b/built_collection/lib/src/iterable.dart
index 761e797..1f108f5 100644
--- a/built_collection/lib/src/iterable.dart
+++ b/built_collection/lib/src/iterable.dart
@@ -2,8 +2,6 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-library built_collection.iterable;
-
 import 'package:built_collection/src/list.dart' show BuiltList;
 import 'package:built_collection/src/set.dart' show BuiltSet;
 
diff --git a/built_collection/lib/src/iterable/built_iterable.dart b/built_collection/lib/src/iterable/built_iterable.dart
index d5631a7..7011f7d 100644
--- a/built_collection/lib/src/iterable/built_iterable.dart
+++ b/built_collection/lib/src/iterable/built_iterable.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.iterable;
+part of '../iterable.dart';
 
 /// [Iterable] that is either a [BuiltList] or a [BuiltSet].
 abstract class BuiltIterable<E> implements Iterable<E> {
diff --git a/built_collection/lib/src/list.dart b/built_collection/lib/src/list.dart
index ba604d2..fd28272 100644
--- a/built_collection/lib/src/list.dart
+++ b/built_collection/lib/src/list.dart
@@ -2,16 +2,15 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-library built_collection.list;
-
 import 'dart:math' show Random;
 
 import 'package:built_collection/src/iterable.dart' show BuiltIterable;
 import 'package:built_collection/src/set.dart' show BuiltSet;
-import 'package:quiver/core.dart' show hashObjects;
 
 import 'internal/copy_on_write_list.dart';
+import 'internal/hash.dart';
 import 'internal/iterables.dart';
+import 'internal/null_safety.dart';
 
 part 'list/built_list.dart';
 part 'list/list_builder.dart';
@@ -21,7 +20,7 @@
   final int _overridenHashCode;
 
   OverriddenHashcodeBuiltList(Iterable iterable, this._overridenHashCode)
-      : super.copyAndCheckTypes(iterable);
+      : super.from(iterable);
 
   @override
   // ignore: hash_and_equals
diff --git a/built_collection/lib/src/list/built_list.dart b/built_collection/lib/src/list/built_list.dart
index 9bc1070..6dd8638 100644
--- a/built_collection/lib/src/list/built_list.dart
+++ b/built_collection/lib/src/list/built_list.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.list;
+part of '../list.dart';
 
 /// The Built Collection [List].
 ///
@@ -15,47 +15,29 @@
 /// for the general properties of Built Collections.
 abstract class BuiltList<E> implements Iterable<E>, BuiltIterable<E> {
   final List<E> _list;
-  int _hashCode;
+  int? _hashCode;
 
   /// Instantiates with elements from an [Iterable].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new BuiltList([1, 2, 3])`.
-  ///
-  /// Right: `new BuiltList<int>([1, 2, 3])`.
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory BuiltList([Iterable iterable = const []]) =>
       BuiltList<E>.from(iterable);
 
   /// Instantiates with elements from an [Iterable].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new BuiltList.from([1, 2, 3])`.
-  ///
-  /// Right: `new BuiltList<int>.from([1, 2, 3])`.
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory BuiltList.from(Iterable iterable) {
     if (iterable is _BuiltList && iterable.hasExactElementType(E)) {
       return iterable as BuiltList<E>;
     } else {
-      return _BuiltList<E>.copyAndCheckTypes(iterable);
+      return _BuiltList<E>.from(iterable);
     }
   }
 
   /// Instantiates with elements from an [Iterable<E>].
   ///
   /// `E` must not be `dynamic`.
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory BuiltList.of(Iterable<E> iterable) {
     if (iterable is _BuiltList<E> && iterable.hasExactElementType(E)) {
       return iterable;
     } else {
-      return _BuiltList<E>.copyAndCheckForNull(iterable);
+      return _BuiltList<E>.of(iterable);
     }
   }
 
@@ -85,7 +67,7 @@
   @override
   int get hashCode {
     _hashCode ??= hashObjects(_list);
-    return _hashCode;
+    return _hashCode!;
   }
 
   /// Deep equality.
@@ -93,7 +75,7 @@
   /// A `BuiltList` is only equal to another `BuiltList` with equal elements in
   /// the same order.
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     if (identical(other, this)) return true;
     if (other is! BuiltList) return false;
     if (other.length != length) return false;
@@ -133,18 +115,18 @@
   int indexOf(E element, [int start = 0]) => _list.indexOf(element, start);
 
   /// As [List.lastIndexOf].
-  int lastIndexOf(E element, [int start]) => _list.lastIndexOf(element, start);
+  int lastIndexOf(E element, [int? start]) => _list.lastIndexOf(element, start);
 
   /// As [List.indexWhere].
   int indexWhere(bool Function(E) test, [int start = 0]) =>
       _list.indexWhere(test, start);
 
   /// As [List.lastIndexWhere].
-  int lastIndexWhere(bool Function(E) test, [int start]) =>
+  int lastIndexWhere(bool Function(E) test, [int? start]) =>
       _list.lastIndexWhere(test, start);
 
   /// As [List.sublist] but returns a `BuiltList<E>`.
-  BuiltList<E> sublist(int start, [int end]) =>
+  BuiltList<E> sublist(int start, [int? end]) =>
       _BuiltList<E>.withSafeList(_list.sublist(start, end));
 
   /// As [List.getRange].
@@ -171,7 +153,7 @@
   Iterable<T> expand<T>(Iterable<T> Function(E) f) => _list.expand(f);
 
   @override
-  bool contains(Object element) => _list.contains(element);
+  bool contains(Object? element) => _list.contains(element);
 
   @override
   void forEach(void Function(E) f) => _list.forEach(f);
@@ -237,15 +219,15 @@
   E get single => _list.single;
 
   @override
-  E firstWhere(bool Function(E) test, {E Function() orElse}) =>
+  E firstWhere(bool Function(E) test, {E Function()? orElse}) =>
       _list.firstWhere(test, orElse: orElse);
 
   @override
-  E lastWhere(bool Function(E) test, {E Function() orElse}) =>
+  E lastWhere(bool Function(E) test, {E Function()? orElse}) =>
       _list.lastWhere(test, orElse: orElse);
 
   @override
-  E singleWhere(bool Function(E) test, {E Function() orElse}) =>
+  E singleWhere(bool Function(E) test, {E Function()? orElse}) =>
       _list.singleWhere(test, orElse: orElse);
 
   @override
@@ -256,29 +238,27 @@
 
   // Internal.
 
-  BuiltList._(this._list) {
-    if (E == dynamic) {
-      throw UnsupportedError(
-          'explicit element type required, for example "new BuiltList<int>"');
-    }
-  }
+  BuiltList._(this._list);
 }
 
 /// Default implementation of the public [BuiltList] interface.
 class _BuiltList<E> extends BuiltList<E> {
   _BuiltList.withSafeList(List<E> list) : super._(list);
 
-  _BuiltList.copyAndCheckTypes([Iterable iterable = const []])
+  _BuiltList.from([Iterable iterable = const []])
       : super._(List<E>.from(iterable, growable: false)) {
-    for (var element in _list) {
-      if (element is! E) {
-        throw ArgumentError('iterable contained invalid element: $element');
-      }
-    }
+    _maybeCheckForNull();
   }
 
-  _BuiltList.copyAndCheckForNull(Iterable<E> iterable)
+  _BuiltList.of(Iterable<E> iterable)
       : super._(List<E>.from(iterable, growable: false)) {
+    _maybeCheckForNull();
+  }
+
+  bool get _needsNullCheck => !isSoundMode && null is! E;
+
+  void _maybeCheckForNull() {
+    if (!_needsNullCheck) return;
     for (var element in _list) {
       if (identical(element, null)) {
         throw ArgumentError('iterable contained invalid element: null');
@@ -294,7 +274,7 @@
   /// Converts to a [BuiltList].
   BuiltList<T> build() {
     // We know a `List` is not a `BuiltList`, so we have to copy.
-    return _BuiltList<T>.copyAndCheckForNull(this);
+    return _BuiltList<T>.of(this);
   }
 }
 
diff --git a/built_collection/lib/src/list/list_builder.dart b/built_collection/lib/src/list/list_builder.dart
index 674ad8d..a9a1413 100644
--- a/built_collection/lib/src/list/list_builder.dart
+++ b/built_collection/lib/src/list/list_builder.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.list;
+part of '../list.dart';
 
 /// The Built Collection builder for [BuiltList].
 ///
@@ -12,18 +12,10 @@
 /// [Built Collection library documentation](#built_collection/built_collection)
 /// for the general properties of Built Collections.
 class ListBuilder<E> {
-  List<E> _list;
-  _BuiltList<E> _listOwner;
+  late List<E> _list;
+  _BuiltList<E>? _listOwner;
 
   /// Instantiates with elements from an [Iterable].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new ListBuilder([1, 2, 3])`.
-  ///
-  /// Right: `new ListBuilder<int>([1, 2, 3])`,
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory ListBuilder([Iterable iterable = const []]) {
     return ListBuilder<E>._uninitialized()..replace(iterable);
   }
@@ -36,7 +28,7 @@
     if (_listOwner == null) {
       _setOwner(_BuiltList<E>.withSafeList(_list));
     }
-    return _listOwner;
+    return _listOwner!;
   }
 
   /// Applies a function to `this`.
@@ -60,7 +52,7 @@
 
   /// As [List].
   void operator []=(int index, E element) {
-    _checkElement(element);
+    _maybeCheckElement(element);
     _safeList[index] = element;
   }
 
@@ -69,7 +61,7 @@
 
   /// As [List.first].
   set first(E value) {
-    _checkElement(value);
+    _maybeCheckElement(value);
     _safeList.first = value;
   }
 
@@ -78,7 +70,7 @@
 
   /// As [List.last].
   set last(E value) {
-    _checkElement(value);
+    _maybeCheckElement(value);
     _safeList.last = value;
   }
 
@@ -93,7 +85,7 @@
 
   /// As [List.add].
   void add(E value) {
-    _checkElement(value);
+    _maybeCheckElement(value);
     _safeList.add(value);
   }
 
@@ -104,6 +96,7 @@
     var safeList = _safeList;
     var lengthBefore = safeList.length;
     safeList.addAll(iterable);
+    if (!_needsNullCheck) return;
     try {
       for (var i = lengthBefore; i != safeList.length; ++i) {
         _checkElement(safeList[i]);
@@ -121,12 +114,12 @@
   }
 
   /// As [List.sort].
-  void sort([int Function(E, E) compare]) {
+  void sort([int Function(E, E)? compare]) {
     _safeList.sort(compare);
   }
 
   /// As [List.shuffle].
-  void shuffle([Random random]) {
+  void shuffle([Random? random]) {
     _safeList.shuffle(random);
   }
 
@@ -137,7 +130,7 @@
 
   /// As [List.insert].
   void insert(int index, E element) {
-    _checkElement(element);
+    _maybeCheckElement(element);
     _safeList.insert(index, element);
   }
 
@@ -148,8 +141,8 @@
     var safeList = _safeList;
     var lengthBefore = safeList.length;
     safeList.insertAll(index, iterable);
+    if (!_needsNullCheck) return;
     var insertedLength = safeList.length - lengthBefore;
-
     try {
       for (var i = index; i != index + insertedLength; ++i) {
         _checkElement(safeList[i]);
@@ -163,12 +156,12 @@
   /// As [List.setAll].
   void setAll(int index, Iterable<E> iterable) {
     iterable = evaluateIterable(iterable);
-    _checkElements(iterable);
+    _maybeCheckElements(iterable);
     _safeList.setAll(index, iterable);
   }
 
   /// As [List.remove].
-  bool remove(Object value) => _safeList.remove(value);
+  bool remove(Object? value) => _safeList.remove(value);
 
   /// As [List.removeAt].
   E removeAt(int index) => _safeList.removeAt(index);
@@ -189,14 +182,14 @@
   }
 
   /// As [List.sublist], but updates the builder in place. Returns nothing.
-  void sublist(int start, [int end]) {
+  void sublist(int start, [int? end]) {
     _setSafeList(_list.sublist(start, end));
   }
 
   /// As [List.setRange].
   void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
     iterable = evaluateIterable(iterable);
-    _checkElements(iterable);
+    _maybeCheckElements(iterable);
     _safeList.setRange(start, end, iterable, skipCount);
   }
 
@@ -207,14 +200,14 @@
 
   /// As [List.fillRange], but requires a value.
   void fillRange(int start, int end, E fillValue) {
-    _checkElement(fillValue);
+    _maybeCheckElement(fillValue);
     _safeList.fillRange(start, end, fillValue);
   }
 
   /// As [List.replaceRange].
   void replaceRange(int start, int end, Iterable<E> iterable) {
     iterable = evaluateIterable(iterable);
-    _checkElements(iterable);
+    _maybeCheckElements(iterable);
     _safeList.replaceRange(start, end, iterable);
   }
 
@@ -223,7 +216,7 @@
   /// As [Iterable.map], but updates the builder in place. Returns nothing.
   void map(E Function(E) f) {
     var result = _list.map(f).toList(growable: true);
-    _checkElements(result);
+    _maybeCheckElements(result);
     _setSafeList(result);
   }
 
@@ -235,19 +228,19 @@
   /// As [Iterable.expand], but updates the builder in place. Returns nothing.
   void expand(Iterable<E> Function(E) f) {
     var result = _list.expand(f).toList(growable: true);
-    _checkElements(result);
+    _maybeCheckElements(result);
     _setSafeList(result);
   }
 
   /// As [Iterable.take], but updates the builder in place. Returns nothing.
   void take(int n) {
-    _setSafeList(_list = _list.take(n).toList(growable: true));
+    _setSafeList(_list.take(n).toList(growable: true));
   }
 
   /// As [Iterable.takeWhile], but updates the builder in place. Returns
   /// nothing.
   void takeWhile(bool Function(E) test) {
-    _setSafeList(_list = _list.takeWhile(test).toList(growable: true));
+    _setSafeList(_list.takeWhile(test).toList(growable: true));
   }
 
   /// As [Iterable.skip], but updates the builder in place. Returns nothing.
@@ -263,9 +256,7 @@
 
   // Internal.
 
-  ListBuilder._uninitialized() {
-    _checkGenericTypeParameter();
-  }
+  ListBuilder._uninitialized();
 
   void _setOwner(_BuiltList<E> listOwner) {
     _list = listOwner._list;
@@ -284,11 +275,10 @@
     return _list;
   }
 
-  void _checkGenericTypeParameter() {
-    if (E == dynamic) {
-      throw UnsupportedError('explicit element type required, '
-          'for example "new ListBuilder<int>"');
-    }
+  bool get _needsNullCheck => !isSoundMode && null is! E;
+
+  void _maybeCheckElement(E element) {
+    if (_needsNullCheck) _checkElement(element);
   }
 
   void _checkElement(E element) {
@@ -297,7 +287,8 @@
     }
   }
 
-  void _checkElements(Iterable<E> elements) {
+  void _maybeCheckElements(Iterable<E> elements) {
+    if (!_needsNullCheck) return;
     for (var element in elements) {
       _checkElement(element);
     }
diff --git a/built_collection/lib/src/list_multimap.dart b/built_collection/lib/src/list_multimap.dart
index 1c66902..4cf9fbd 100644
--- a/built_collection/lib/src/list_multimap.dart
+++ b/built_collection/lib/src/list_multimap.dart
@@ -2,13 +2,10 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-library built_collection.list_multimap;
-
-import 'package:quiver/collection.dart' show ListMultimap;
-import 'package:quiver/core.dart' show hashObjects, hash2;
-
 import 'internal/copy_on_write_map.dart';
 
+import 'internal/hash.dart';
+import 'internal/null_safety.dart';
 import 'list.dart';
 
 part 'list_multimap/built_list_multimap.dart';
@@ -20,7 +17,7 @@
   final int _overridenHashCode;
 
   OverriddenHashcodeBuiltListMultimap(map, this._overridenHashCode)
-      : super.copyAndCheck(map.keys, (k) => map[k]);
+      : super.copy(map.keys, (k) => map[k]);
 
   @override
   // ignore: hash_and_equals
diff --git a/built_collection/lib/src/list_multimap/built_list_multimap.dart b/built_collection/lib/src/list_multimap/built_list_multimap.dart
index edd3e26..0cbb669 100644
--- a/built_collection/lib/src/list_multimap/built_list_multimap.dart
+++ b/built_collection/lib/src/list_multimap/built_list_multimap.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.list_multimap;
+part of '../list_multimap.dart';
 
 /// The Built Collection [ListMultimap].
 ///
@@ -21,34 +21,22 @@
   final BuiltList<V> _emptyList = BuiltList<V>();
 
   // Cached.
-  int _hashCode;
-  Iterable<K> _keys;
-  Iterable<V> _values;
+  int? _hashCode;
+  Iterable<K>? _keys;
+  Iterable<V>? _values;
 
   /// Instantiates with elements from a [Map], [ListMultimap] or
   /// [BuiltListMultimap].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong:
-  ///   `new BuiltListMultimap({1: ['1'], 2: ['2'], 3: ['3']})`.
-  ///
-  /// Right:
-  ///   `new BuiltListMultimap<int, String>({1: ['1'], 2: ['2'], 3: ['3']})`,
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory BuiltListMultimap([multimap = const {}]) {
     if (multimap is _BuiltListMultimap &&
         multimap.hasExactKeyAndValueTypes(K, V)) {
       return multimap as BuiltListMultimap<K, V>;
-    } else if (multimap is Map ||
-        multimap is ListMultimap ||
-        multimap is BuiltListMultimap) {
-      return _BuiltListMultimap<K, V>.copyAndCheck(
-          multimap.keys, (k) => multimap[k]);
+    } else if (multimap is Map) {
+      return _BuiltListMultimap<K, V>.copy(multimap.keys, (k) => multimap[k]);
+    } else if (multimap is BuiltListMultimap) {
+      return _BuiltListMultimap<K, V>.copy(multimap.keys, (k) => multimap[k]);
     } else {
-      throw ArgumentError('expected Map, ListMultimap or BuiltListMultimap, '
-          'got ${multimap.runtimeType}');
+      return _BuiltListMultimap<K, V>.copy(multimap.keys, (k) => multimap[k]);
     }
   }
 
@@ -88,7 +76,7 @@
         .map((key) => hash2(key.hashCode, _map[key].hashCode))
         .toList(growable: false)
           ..sort());
-    return _hashCode;
+    return _hashCode!;
   }
 
   /// Deep equality.
@@ -96,7 +84,7 @@
   /// A `BuiltListMultimap` is only equal to another `BuiltListMultimap` with
   /// equal key/values pairs in any order.
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     if (identical(other, this)) return true;
     if (other is! BuiltListMultimap) return false;
     if (other.length != length) return false;
@@ -119,16 +107,16 @@
   // ListMultimap.
 
   /// As [ListMultimap], but results are [BuiltList]s and not mutable.
-  BuiltList<V> operator [](Object key) {
+  BuiltList<V> operator [](Object? key) {
     var result = _map[key];
-    return identical(result, null) ? _emptyList : result;
+    return result ?? _emptyList;
   }
 
   /// As [ListMultimap.containsKey].
-  bool containsKey(Object key) => _map.containsKey(key);
+  bool containsKey(Object? key) => _map.containsKey(key);
 
   /// As [ListMultimap.containsValue].
-  bool containsValue(Object value) => values.contains(value);
+  bool containsValue(Object? value) => values.contains(value);
 
   /// As [ListMultimap.forEach].
   void forEach(void Function(K, V) f) {
@@ -156,7 +144,7 @@
   /// instance.
   Iterable<K> get keys {
     _keys ??= _map.keys;
-    return _keys;
+    return _keys!;
   }
 
   /// As [ListMultimap.length].
@@ -166,28 +154,19 @@
   /// same instance.
   Iterable<V> get values {
     _values ??= _map.values.expand((x) => x);
-    return _values;
+    return _values!;
   }
 
   // Internal.
 
-  BuiltListMultimap._(this._map) {
-    if (K == dynamic) {
-      throw UnsupportedError('explicit key type required, '
-          'for example "new BuiltListMultimap<int, int>"');
-    }
-    if (V == dynamic) {
-      throw UnsupportedError('explicit value type required,'
-          ' for example "new BuiltListMultimap<int, int>"');
-    }
-  }
+  BuiltListMultimap._(this._map);
 }
 
 /// Default implementation of the public [BuiltListMultimap] interface.
 class _BuiltListMultimap<K, V> extends BuiltListMultimap<K, V> {
   _BuiltListMultimap.withSafeMap(Map<K, BuiltList<V>> map) : super._(map);
 
-  _BuiltListMultimap.copyAndCheck(Iterable keys, Function lookup)
+  _BuiltListMultimap.copy(Iterable keys, Function lookup)
       : super._(<K, BuiltList<V>>{}) {
     for (var key in keys) {
       if (key is K) {
diff --git a/built_collection/lib/src/list_multimap/list_multimap_builder.dart b/built_collection/lib/src/list_multimap/list_multimap_builder.dart
index 3cd234a..2c4b23b 100644
--- a/built_collection/lib/src/list_multimap/list_multimap_builder.dart
+++ b/built_collection/lib/src/list_multimap/list_multimap_builder.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.list_multimap;
+part of '../list_multimap.dart';
 
 /// The Built Collection builder for [BuiltListMultimap].
 ///
@@ -14,25 +14,15 @@
 class ListMultimapBuilder<K, V> {
   // BuiltLists copied from another instance so they can be reused directly for
   // keys without changes.
-  Map<K, BuiltList<V>> _builtMap;
+  late Map<K, BuiltList<V>> _builtMap;
   // Instance that _builtMap belongs to. If present, _builtMap must not be
   // mutated.
-  _BuiltListMultimap<K, V> _builtMapOwner;
+  _BuiltListMultimap<K, V>? _builtMapOwner;
   // ListBuilders for keys that are being changed.
-  Map<K, ListBuilder<V>> _builderMap;
+  late Map<K, ListBuilder<V>> _builderMap;
 
   /// Instantiates with elements from a [Map], [ListMultimap] or
   /// [BuiltListMultimap].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong:
-  ///   `new ListMultimapBuilder({1: ['1'], 2: ['2'], 3: ['3']})`.
-  ///
-  /// Right:
-  ///   `new ListMultimapBuilder<int, String>({1: ['1'], 2: ['2'], 3: ['3']})`,
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory ListMultimapBuilder([multimap = const {}]) {
     return ListMultimapBuilder<K, V>._uninitialized()..replace(multimap);
   }
@@ -44,7 +34,7 @@
   BuiltListMultimap<K, V> build() {
     if (_builtMapOwner == null) {
       for (var key in _builderMap.keys) {
-        var builtList = _builderMap[key].build();
+        var builtList = _builderMap[key]!.build();
         if (builtList.isEmpty) {
           _builtMap.remove(key);
         } else {
@@ -54,7 +44,7 @@
 
       _builtMapOwner = _BuiltListMultimap<K, V>.withSafeMap(_builtMap);
     }
-    return _builtMapOwner;
+    return _builtMapOwner!;
   }
 
   /// Applies a function to `this`.
@@ -69,13 +59,12 @@
   void replace(dynamic multimap) {
     if (multimap is _BuiltListMultimap<K, V>) {
       _setOwner(multimap);
-    } else if (multimap is Map ||
-        multimap is ListMultimap ||
-        multimap is BuiltListMultimap) {
+    } else if (multimap is Map) {
+      _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]);
+    } else if (multimap is BuiltListMultimap) {
       _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]);
     } else {
-      throw ArgumentError('expected Map, ListMultimap or BuiltListMultimap, '
-          'got ${multimap.runtimeType}');
+      _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]);
     }
   }
 
@@ -88,9 +77,9 @@
   /// [key] and [value] default to the identity function. [values] is ignored
   /// if not specified.
   void addIterable<T>(Iterable<T> iterable,
-      {K Function(T) key,
-      V Function(T) value,
-      Iterable<V> Function(T) values}) {
+      {K Function(T)? key,
+      V Function(T)? value,
+      Iterable<V> Function(T)? values}) {
     if (value != null && values != null) {
       throw ArgumentError('expected value or values to be set, got both');
     }
@@ -127,23 +116,15 @@
     });
   }
 
-  /// As [ListMultimap.addAll].
-  void addAll(ListMultimap<K, V> other) {
-    // _disown is called in add.
-    other.forEach((key, value) {
-      add(key, value);
-    });
-  }
-
   /// As [ListMultimap.remove].
-  bool remove(Object key, V value) {
+  bool remove(Object? key, V? value) {
     if (key is! K) return false;
     _makeWriteableCopy();
     return _getValuesBuilder(key).remove(value);
   }
 
   /// As [ListMultimap.removeAll], but results are [BuiltList]s.
-  BuiltList<V> removeAll(Object key) {
+  BuiltList<V> removeAll(Object? key) {
     if (key is! K) return BuiltList<V>();
     _makeWriteableCopy();
     var builder = _builderMap[key];
@@ -167,7 +148,7 @@
   }
 
   /// As [ListMultimap], but results are [ListBuilder]s.
-  ListBuilder<V> operator [](Object key) {
+  ListBuilder<V> operator [](Object? key) {
     _makeWriteableCopy();
     return key is K ? _getValuesBuilder(key) : ListBuilder<V>();
   }
@@ -195,9 +176,7 @@
     }
   }
 
-  ListMultimapBuilder._uninitialized() {
-    _checkGenericTypeParameter();
-  }
+  ListMultimapBuilder._uninitialized();
 
   void _setOwner(_BuiltListMultimap<K, V> builtListMultimap) {
     _builtMapOwner = builtListMultimap;
@@ -226,24 +205,17 @@
     }
   }
 
-  void _checkGenericTypeParameter() {
-    if (K == dynamic) {
-      throw UnsupportedError('explicit key type required, '
-          'for example "new ListMultimapBuilder<int, int>"');
-    }
-    if (V == dynamic) {
-      throw UnsupportedError('explicit value type required, '
-          'for example "new ListMultimapBuilder<int, int>"');
-    }
-  }
-
   void _checkKey(K key) {
+    if (isSoundMode) return;
+    if (null is K) return;
     if (identical(key, null)) {
       throw ArgumentError('null key');
     }
   }
 
   void _checkValue(V value) {
+    if (isSoundMode) return;
+    if (null is V) return;
     if (identical(value, null)) {
       throw ArgumentError('null value');
     }
diff --git a/built_collection/lib/src/map.dart b/built_collection/lib/src/map.dart
index e961ad6..ef60ba6 100644
--- a/built_collection/lib/src/map.dart
+++ b/built_collection/lib/src/map.dart
@@ -2,11 +2,9 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-library built_collection.map;
-
-import 'package:quiver/core.dart' show hashObjects, hash2;
-
 import 'internal/copy_on_write_map.dart';
+import 'internal/hash.dart';
+import 'internal/null_safety.dart';
 
 part 'map/built_map.dart';
 part 'map/map_builder.dart';
diff --git a/built_collection/lib/src/map/built_map.dart b/built_collection/lib/src/map/built_map.dart
index ed5cd39..4a9e104 100644
--- a/built_collection/lib/src/map/built_map.dart
+++ b/built_collection/lib/src/map/built_map.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.map;
+part of '../map.dart';
 
 typedef _MapFactory<K, V> = Map<K, V> Function();
 
@@ -16,23 +16,15 @@
 /// [Built Collection library documentation](#built_collection/built_collection)
 /// for the general properties of Built Collections.
 abstract class BuiltMap<K, V> {
-  final _MapFactory<K, V> _mapFactory;
+  final _MapFactory<K, V>? _mapFactory;
   final Map<K, V> _map;
 
   // Cached.
-  int _hashCode;
-  Iterable<K> _keys;
-  Iterable<V> _values;
+  int? _hashCode;
+  Iterable<K>? _keys;
+  Iterable<V>? _values;
 
   /// Instantiates with elements from a [Map] or [BuiltMap].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new BuiltMap({1: '1', 2: '2', 3: '3'})`.
-  ///
-  /// Right: `new BuiltMap<int, String>({1: '1', 2: '2', 3: '3'})`.
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory BuiltMap([map = const {}]) {
     if (map is _BuiltMap && map.hasExactKeyAndValueTypes(K, V)) {
       return map as BuiltMap<K, V>;
@@ -44,25 +36,15 @@
   }
 
   /// Instantiates with elements from a [Map].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new BuiltMap.from({1: '1', 2: '2', 3: '3'})`.
-  ///
-  /// Right: `new BuiltMap<int, String>.from({1: '1', 2: '2', 3: '3'})`.
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory BuiltMap.from(Map map) {
     return _BuiltMap<K, V>.copyAndCheckTypes(map.keys, (k) => map[k]);
   }
 
   /// Instantiates with elements from a [Map<K, V>].
   ///
-  /// `K` and `V` are inferred from `map`, and must not be `dynamic`.
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
+  /// `K` and `V` are inferred from `map`.
   factory BuiltMap.of(Map<K, V> map) {
-    return _BuiltMap<K, V>.copyAndCheckForNull(map.keys, (k) => map[k]);
+    return _BuiltMap<K, V>.copyAndCheckForNull(map.keys, (k) => map[k] as V);
   }
 
   /// Creates a [MapBuilder], applies updates to it, and builds.
@@ -72,7 +54,8 @@
   /// Converts to a [MapBuilder] for modification.
   ///
   /// The `BuiltMap` remains immutable and can continue to be used.
-  MapBuilder<K, V> toBuilder() => MapBuilder<K, V>._fromBuiltMap(this);
+  MapBuilder<K, V> toBuilder() =>
+      MapBuilder<K, V>._fromBuiltMap(this as _BuiltMap<K, V>);
 
   /// Converts to a [MapBuilder], applies updates to it, and builds.
   BuiltMap<K, V> rebuild(Function(MapBuilder<K, V>) updates) =>
@@ -104,7 +87,7 @@
         .map((key) => hash2(key.hashCode, _map[key].hashCode))
         .toList(growable: false)
           ..sort());
-    return _hashCode;
+    return _hashCode!;
   }
 
   /// Deep equality.
@@ -112,7 +95,7 @@
   /// A `BuiltMap` is only equal to another `BuiltMap` with equal key/value
   /// pairs in any order.
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     if (identical(other, this)) return true;
     if (other is! BuiltMap) return false;
     if (other.length != length) return false;
@@ -129,7 +112,7 @@
   // Map.
 
   /// As [Map].
-  V operator [](Object key) => _map[key];
+  V? operator [](Object? key) => _map[key];
 
   /// As [Map.containsKey].
   bool containsKey(Object key) => _map.containsKey(key);
@@ -151,7 +134,7 @@
   /// As [Map.keys], but result is stable; it always returns the same instance.
   Iterable<K> get keys {
     _keys ??= _map.keys;
-    return _keys;
+    return _keys!;
   }
 
   /// As [Map.length].
@@ -161,7 +144,7 @@
   /// instance.
   Iterable<V> get values {
     _values ??= _map.values;
-    return _values;
+    return _values!;
   }
 
   /// As [Map.entries].
@@ -173,21 +156,12 @@
 
   // Internal.
 
-  BuiltMap._(this._mapFactory, this._map) {
-    if (K == dynamic) {
-      throw UnsupportedError(
-          'explicit key type required, for example "new BuiltMap<int, int>"');
-    }
-    if (V == dynamic) {
-      throw UnsupportedError('explicit value type required,'
-          ' for example "new BuiltMap<int, int>"');
-    }
-  }
+  BuiltMap._(this._mapFactory, this._map);
 }
 
 /// Default implementation of the public [BuiltMap] interface.
 class _BuiltMap<K, V> extends BuiltMap<K, V> {
-  _BuiltMap.withSafeMap(_MapFactory<K, V> mapFactory, Map<K, V> map)
+  _BuiltMap.withSafeMap(_MapFactory<K, V>? mapFactory, Map<K, V> map)
       : super._(mapFactory, map);
 
   _BuiltMap.copyAndCheckTypes(Iterable keys, Function lookup)
@@ -208,12 +182,14 @@
 
   _BuiltMap.copyAndCheckForNull(Iterable<K> keys, V Function(K) lookup)
       : super._(null, <K, V>{}) {
+    var checkKeys = !isSoundMode && null is! K;
+    var checkValues = !isSoundMode && null is! V;
     for (var key in keys) {
-      if (identical(key, null)) {
+      if (checkKeys && identical(key, null)) {
         throw ArgumentError('map contained invalid key: null');
       }
       var value = lookup(key);
-      if (value == null) {
+      if (checkValues && value == null) {
         throw ArgumentError('map contained invalid value: null');
       }
       _map[key] = value;
diff --git a/built_collection/lib/src/map/map_builder.dart b/built_collection/lib/src/map/map_builder.dart
index 1742bb5..d44948d 100644
--- a/built_collection/lib/src/map/map_builder.dart
+++ b/built_collection/lib/src/map/map_builder.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.map;
+part of '../map.dart';
 
 /// The Built Collection builder for [BuiltMap].
 ///
@@ -13,19 +13,11 @@
 /// for the general properties of Built Collections.
 class MapBuilder<K, V> {
   /// Used by [_createMap] to instantiate [_map]. The default value is `null`.
-  _MapFactory<K, V> _mapFactory;
-  Map<K, V> _map;
-  _BuiltMap<K, V> _mapOwner;
+  _MapFactory<K, V>? _mapFactory;
+  late Map<K, V> _map;
+  _BuiltMap<K, V>? _mapOwner;
 
   /// Instantiates with elements from a [Map] or [BuiltMap].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new MapBuilder({1: '1', 2: '2', 3: '3'})`.
-  ///
-  /// Right: `new MapBuilder<int, String>({1: '1', 2: '2', 3: '3'})`,
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory MapBuilder([map = const {}]) {
     return MapBuilder<K, V>._uninitialized()..replace(map);
   }
@@ -36,7 +28,7 @@
   /// of `BuiltMap`s.
   BuiltMap<K, V> build() {
     _mapOwner ??= _BuiltMap<K, V>.withSafeMap(_mapFactory, _map);
-    return _mapOwner;
+    return _mapOwner!;
   }
 
   /// Applies a function to `this`.
@@ -50,13 +42,13 @@
       _setOwner(map);
     } else if (map is BuiltMap) {
       var replacement = _createMap();
-      map.forEach((Object key, Object value) {
+      map.forEach((dynamic key, dynamic value) {
         replacement[key as K] = value as V;
       });
       _setSafeMap(replacement);
     } else if (map is Map) {
       var replacement = _createMap();
-      map.forEach((Object key, Object value) {
+      map.forEach((dynamic key, dynamic value) {
         replacement[key as K] = value as V;
       });
       _setSafeMap(replacement);
@@ -82,9 +74,7 @@
   ///
   /// Use [withDefaultBase] to reset `base` to the default value.
   void withBase(_MapFactory<K, V> base) {
-    if (base == null) {
-      throw ArgumentError.notNull('base');
-    }
+    ArgumentError.checkNotNull(base, 'base');
     _mapFactory = base;
     _setSafeMap(_createMap()..addAll(_map));
   }
@@ -100,7 +90,7 @@
   ///
   /// [key] and [value] default to the identity function.
   void addIterable<T>(Iterable<T> iterable,
-      {K Function(T) key, V Function(T) value}) {
+      {K Function(T)? key, V Function(T)? value}) {
     key ??= (T x) => x as K;
     value ??= (T x) => x as V;
     for (var element in iterable) {
@@ -111,7 +101,7 @@
   // Based on Map.
 
   /// As [Map].
-  V operator [](Object key) => _map[key];
+  V? operator [](Object? key) => _map[key];
 
   /// As [Map].
   void operator []=(K key, V value) {
@@ -147,7 +137,7 @@
   }
 
   /// As [Map.remove].
-  V remove(Object key) => _safeMap.remove(key);
+  V? remove(Object? key) => _safeMap.remove(key);
 
   /// As [Map.removeWhere].
   void removeWhere(bool Function(K, V) predicate) {
@@ -165,7 +155,7 @@
   }
 
   /// As [Map.update].
-  V updateValue(K key, V Function(V) update, {V Function() ifAbsent}) =>
+  V updateValue(K key, V Function(V) update, {V Function()? ifAbsent}) =>
       _safeMap.update(key, update, ifAbsent: ifAbsent);
 
   /// As [Map.updateAll].
@@ -175,9 +165,7 @@
 
   // Internal.
 
-  MapBuilder._uninitialized() {
-    _checkGenericTypeParameter();
-  }
+  MapBuilder._uninitialized();
 
   MapBuilder._fromBuiltMap(_BuiltMap<K, V> map)
       : _mapFactory = map._mapFactory,
@@ -204,38 +192,35 @@
     return _map;
   }
 
-  Map<K, V> _createMap() => _mapFactory != null ? _mapFactory() : <K, V>{};
-
-  void _checkGenericTypeParameter() {
-    if (K == dynamic) {
-      throw UnsupportedError(
-          'explicit key type required, for example "new MapBuilder<int, int>"');
-    }
-    if (V == dynamic) {
-      throw UnsupportedError('explicit value type required, '
-          'for example "new MapBuilder<int, int>"');
-    }
-  }
+  Map<K, V> _createMap() => _mapFactory != null ? _mapFactory!() : <K, V>{};
 
   void _checkKey(K key) {
+    if (isSoundMode) return;
+    if (null is K) return;
     if (identical(key, null)) {
       throw ArgumentError('null key');
     }
   }
 
   void _checkKeys(Iterable<K> keys) {
+    if (isSoundMode) return;
+    if (null is K) return;
     for (var key in keys) {
       _checkKey(key);
     }
   }
 
   void _checkValue(V value) {
+    if (isSoundMode) return;
+    if (null is V) return;
     if (identical(value, null)) {
       throw ArgumentError('null value');
     }
   }
 
   void _checkValues(Iterable<V> values) {
+    if (isSoundMode) return;
+    if (null is V) return;
     for (var value in values) {
       _checkValue(value);
     }
diff --git a/built_collection/lib/src/set.dart b/built_collection/lib/src/set.dart
index 26563a7..8b4bf38 100644
--- a/built_collection/lib/src/set.dart
+++ b/built_collection/lib/src/set.dart
@@ -2,15 +2,14 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-library built_collection.set;
-
 import 'package:built_collection/src/iterable.dart' show BuiltIterable;
 import 'package:built_collection/src/list.dart' show BuiltList;
-import 'package:collection/collection.dart' show UnmodifiableSetView;
-import 'package:quiver/core.dart' show hashObjects;
 
+import 'internal/hash.dart';
 import 'internal/copy_on_write_set.dart';
 import 'internal/iterables.dart';
+import 'internal/null_safety.dart';
+import 'internal/unmodifiable_set.dart';
 
 part 'set/built_set.dart';
 part 'set/set_builder.dart';
@@ -20,7 +19,7 @@
   final int _overridenHashCode;
 
   OverriddenHashcodeBuiltSet(Iterable iterable, this._overridenHashCode)
-      : super.copyAndCheckTypes(iterable);
+      : super.from(iterable);
 
   @override
   // ignore: hash_and_equals
diff --git a/built_collection/lib/src/set/built_set.dart b/built_collection/lib/src/set/built_set.dart
index ce98349..7038902 100644
--- a/built_collection/lib/src/set/built_set.dart
+++ b/built_collection/lib/src/set/built_set.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.set;
+part of '../set.dart';
 
 typedef _SetFactory<E> = Set<E> Function();
 
@@ -16,48 +16,28 @@
 /// [Built Collection library documentation](#built_collection/built_collection)
 /// for the general properties of Built Collections.
 abstract class BuiltSet<E> implements Iterable<E>, BuiltIterable<E> {
-  final _SetFactory<E> _setFactory;
+  final _SetFactory<E>? _setFactory;
   final Set<E> _set;
-  int _hashCode;
+  int? _hashCode;
 
   /// Instantiates with elements from an [Iterable].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new BuiltSet([1, 2, 3])`.
-  ///
-  /// Right: `new BuiltSet<int>([1, 2, 3])`.
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory BuiltSet([Iterable iterable = const []]) => BuiltSet.from(iterable);
 
   /// Instantiates with elements from an [Iterable].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new BuiltSet([1, 2, 3])`.
-  ///
-  /// Right: `new BuiltSet<int>([1, 2, 3])`.
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory BuiltSet.from(Iterable iterable) {
     if (iterable is _BuiltSet && iterable.hasExactElementType(E)) {
       return iterable as BuiltSet<E>;
     } else {
-      return _BuiltSet<E>.copyAndCheckTypes(iterable);
+      return _BuiltSet<E>.from(iterable);
     }
   }
 
   /// Instantiates with elements from an [Iterable<E>].
-  ///
-  /// `E` must not be `dynamic`.
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory BuiltSet.of(Iterable<E> iterable) {
     if (iterable is _BuiltSet<E> && iterable.hasExactElementType(E)) {
       return iterable;
     } else {
-      return _BuiltSet<E>.copyAndCheckForNull(iterable);
+      return _BuiltSet<E>.of(iterable);
     }
   }
 
@@ -68,7 +48,8 @@
   /// Converts to a [SetBuilder] for modification.
   ///
   /// The `BuiltSet` remains immutable and can continue to be used.
-  SetBuilder<E> toBuilder() => SetBuilder<E>._fromBuiltSet(this);
+  SetBuilder<E> toBuilder() =>
+      SetBuilder<E>._fromBuiltSet(this as _BuiltSet<E>);
 
   /// Converts to a [SetBuilder], applies updates to it, and builds.
   BuiltSet<E> rebuild(Function(SetBuilder<E>) updates) =>
@@ -88,7 +69,7 @@
   int get hashCode {
     _hashCode ??= hashObjects(
         _set.map((e) => e.hashCode).toList(growable: false)..sort());
-    return _hashCode;
+    return _hashCode!;
   }
 
   /// Deep equality.
@@ -96,7 +77,7 @@
   /// A `BuiltSet` is only equal to another `BuiltSet` with equal elements in
   /// any order.
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     if (identical(other, this)) return true;
     if (other is! BuiltSet) return false;
     if (other.length != length) return false;
@@ -120,18 +101,20 @@
   int get length => _set.length;
 
   /// As [Set.containsAll].
-  bool containsAll(Iterable<Object> other) => _set.containsAll(other);
+  bool containsAll(Iterable<Object?> other) => _set.containsAll(other);
 
-  /// As [Set.difference] but takes and returns a `BuiltSet<E>`.
-  BuiltSet<E> difference(BuiltSet<Object> other) =>
+  /// As [Set.difference] but takes a `BuiltSet<Object?>` and returns a
+  /// `BuiltSet<E>`.
+  BuiltSet<E> difference(BuiltSet<Object?> other) =>
       _BuiltSet<E>.withSafeSet(_setFactory, _set.difference(other._set));
 
-  /// As [Set.intersection] but takes and returns a `BuiltSet<E>`.
-  BuiltSet<E> intersection(BuiltSet<Object> other) =>
+  /// As [Set.intersection] but takes a `BuiltSet<Object?>` and returns a
+  /// `BuiltSet<E>`.
+  BuiltSet<E> intersection(BuiltSet<Object?> other) =>
       _BuiltSet<E>.withSafeSet(_setFactory, _set.intersection(other._set));
 
   /// As [Set.lookup].
-  E lookup(Object object) => _set.lookup(object);
+  E? lookup(Object? object) => _set.lookup(object);
 
   /// As [Set.union] but takes and returns a `BuiltSet<E>`.
   BuiltSet<E> union(BuiltSet<E> other) =>
@@ -161,7 +144,7 @@
   Iterable<T> expand<T>(Iterable<T> Function(E) f) => _set.expand(f);
 
   @override
-  bool contains(Object element) => _set.contains(element);
+  bool contains(Object? element) => _set.contains(element);
 
   @override
   void forEach(void Function(E) f) => _set.forEach(f);
@@ -224,15 +207,15 @@
   E get single => _set.single;
 
   @override
-  E firstWhere(bool Function(E) test, {E Function() orElse}) =>
+  E firstWhere(bool Function(E) test, {E Function()? orElse}) =>
       _set.firstWhere(test, orElse: orElse);
 
   @override
-  E lastWhere(bool Function(E) test, {E Function() orElse}) =>
+  E lastWhere(bool Function(E) test, {E Function()? orElse}) =>
       _set.lastWhere(test, orElse: orElse);
 
   @override
-  E singleWhere(bool Function(E) test, {E Function() orElse}) =>
+  E singleWhere(bool Function(E) test, {E Function()? orElse}) =>
       _set.singleWhere(test, orElse: orElse);
 
   @override
@@ -240,35 +223,29 @@
 
   // Internal.
 
-  BuiltSet._(this._setFactory, this._set) {
-    if (E == dynamic) {
-      throw UnsupportedError(
-          'explicit element type required, for example "new BuiltSet<int>"');
-    }
-  }
+  BuiltSet._(this._setFactory, this._set);
 }
 
 /// Default implementation of the public [BuiltSet] interface.
 class _BuiltSet<E> extends BuiltSet<E> {
-  _BuiltSet.withSafeSet(_SetFactory<E> setFactory, Set<E> set)
+  _BuiltSet.withSafeSet(_SetFactory<E>? setFactory, Set<E> set)
       : super._(setFactory, set);
 
-  _BuiltSet.copyAndCheckTypes(Iterable iterable) : super._(null, <E>{}) {
-    for (var element in iterable) {
-      if (element is E) {
-        _set.add(element);
-      } else {
-        throw ArgumentError('iterable contained invalid element: $element');
-      }
-    }
+  _BuiltSet.from(Iterable iterable) : super._(null, Set<E>.from(iterable)) {
+    _maybeCheckForNull();
   }
 
-  _BuiltSet.copyAndCheckForNull(Iterable iterable) : super._(null, <E>{}) {
-    for (var element in iterable) {
+  _BuiltSet.of(Iterable<E> iterable) : super._(null, <E>{}..addAll(iterable)) {
+    _maybeCheckForNull();
+  }
+
+  bool get _needsNullCheck => !isSoundMode && null is! E;
+
+  void _maybeCheckForNull() {
+    if (!_needsNullCheck) return;
+    for (var element in _set) {
       if (identical(element, null)) {
         throw ArgumentError('iterable contained invalid element: null');
-      } else {
-        _set.add(element);
       }
     }
   }
@@ -281,7 +258,7 @@
   /// Converts to a [BuiltSet].
   BuiltSet<T> build() {
     // We know a `Set` is not a `BuiltSet`, so we have to copy.
-    return _BuiltSet<T>.copyAndCheckForNull(this);
+    return _BuiltSet<T>.of(this);
   }
 }
 
diff --git a/built_collection/lib/src/set/set_builder.dart b/built_collection/lib/src/set/set_builder.dart
index 3bf302a..3d128aa 100644
--- a/built_collection/lib/src/set/set_builder.dart
+++ b/built_collection/lib/src/set/set_builder.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.set;
+part of '../set.dart';
 
 /// The Built Collection builder for [BuiltSet].
 ///
@@ -13,19 +13,11 @@
 /// for the general properties of Built Collections.
 class SetBuilder<E> {
   /// Used by [_createSet] to instantiate [_set]. The default value is `null`.
-  _SetFactory<E> _setFactory;
-  Set<E> _set;
-  _BuiltSet<E> _setOwner;
+  _SetFactory<E>? _setFactory;
+  late Set<E> _set;
+  _BuiltSet<E>? _setOwner;
 
   /// Instantiates with elements from an [Iterable].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong: `new SetBuilder([1, 2, 3])`.
-  ///
-  /// Right: `new SetBuilder<int>([1, 2, 3])`,
-  ///
-  /// Rejects nulls. Rejects elements of the wrong type.
   factory SetBuilder([Iterable iterable = const []]) {
     return SetBuilder<E>._uninitialized()..replace(iterable);
   }
@@ -36,7 +28,7 @@
   /// of `BuiltSet`s.
   BuiltSet<E> build() {
     _setOwner ??= _BuiltSet<E>.withSafeSet(_setFactory, _set);
-    return _setOwner;
+    return _setOwner!;
   }
 
   /// Applies a function to `this`.
@@ -79,9 +71,7 @@
   ///
   /// Use [withDefaultBase] to reset `base` to the default value.
   void withBase(_SetFactory<E> base) {
-    if (base == null) {
-      throw ArgumentError.notNull('base');
-    }
+    ArgumentError.checkNotNull(base, 'base');
     _setFactory = base;
     _setSafeSet(_createSet()..addAll(_set));
   }
@@ -106,14 +96,14 @@
 
   /// As [Set.add].
   bool add(E value) {
-    _checkElement(value);
+    _maybeCheckElement(value);
     return _safeSet.add(value);
   }
 
   /// As [Set.addAll].
   void addAll(Iterable<E> iterable) {
     iterable = evaluateIterable(iterable);
-    _checkElements(iterable);
+    _maybeCheckElements(iterable);
     _safeSet.addAll(iterable);
   }
 
@@ -123,10 +113,10 @@
   }
 
   /// As [Set.remove].
-  bool remove(Object value) => _safeSet.remove(value);
+  bool remove(Object? value) => _safeSet.remove(value);
 
   /// As [Set.removeAll].
-  void removeAll(Iterable<Object> elements) {
+  void removeAll(Iterable<Object?> elements) {
     _safeSet.removeAll(elements);
   }
 
@@ -136,7 +126,7 @@
   }
 
   /// As [Set.retainAll].
-  void retainAll(Iterable<Object> elements) {
+  void retainAll(Iterable<Object?> elements) {
     _safeSet.retainAll(elements);
   }
 
@@ -152,7 +142,7 @@
   /// As [Iterable.map], but updates the builder in place. Returns nothing.
   void map(E Function(E) f) {
     var result = _createSet()..addAll(_set.map(f));
-    _checkElements(result);
+    _maybeCheckElements(result);
     _setSafeSet(result);
   }
 
@@ -164,7 +154,7 @@
   /// As [Iterable.expand], but updates the builder in place. Returns nothing.
   void expand(Iterable<E> Function(E) f) {
     var result = _createSet()..addAll(_set.expand(f));
-    _checkElements(result);
+    _maybeCheckElements(result);
     _setSafeSet(result);
   }
 
@@ -192,9 +182,7 @@
 
   // Internal.
 
-  SetBuilder._uninitialized() {
-    _checkGenericTypeParameter();
-  }
+  SetBuilder._uninitialized();
 
   SetBuilder._fromBuiltSet(_BuiltSet<E> set)
       : _setFactory = set._setFactory,
@@ -221,13 +209,12 @@
     return _set;
   }
 
-  Set<E> _createSet() => _setFactory != null ? _setFactory() : <E>{};
+  Set<E> _createSet() => _setFactory != null ? _setFactory!() : <E>{};
 
-  void _checkGenericTypeParameter() {
-    if (E == dynamic) {
-      throw UnsupportedError('explicit element type required, '
-          'for example "new SetBuilder<int>"');
-    }
+  bool get _needsNullCheck => !isSoundMode && null is! E;
+
+  void _maybeCheckElement(E element) {
+    if (_needsNullCheck) _checkElement(element);
   }
 
   void _checkElement(E element) {
@@ -236,7 +223,8 @@
     }
   }
 
-  void _checkElements(Iterable<E> elements) {
+  void _maybeCheckElements(Iterable<E> elements) {
+    if (!_needsNullCheck) return;
     for (var element in elements) {
       _checkElement(element);
     }
diff --git a/built_collection/lib/src/set_multimap.dart b/built_collection/lib/src/set_multimap.dart
index b1fca63..c40ae76 100644
--- a/built_collection/lib/src/set_multimap.dart
+++ b/built_collection/lib/src/set_multimap.dart
@@ -2,12 +2,9 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-library built_collection.set_multimap;
-
-import 'package:quiver/collection.dart' show SetMultimap;
-import 'package:quiver/core.dart' show hashObjects, hash2;
-
 import 'internal/copy_on_write_map.dart';
+import 'internal/hash.dart';
+import 'internal/null_safety.dart';
 
 import 'set.dart';
 
diff --git a/built_collection/lib/src/set_multimap/built_set_multimap.dart b/built_collection/lib/src/set_multimap/built_set_multimap.dart
index c42b7e4..3df2d25 100644
--- a/built_collection/lib/src/set_multimap/built_set_multimap.dart
+++ b/built_collection/lib/src/set_multimap/built_set_multimap.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.set_multimap;
+part of '../set_multimap.dart';
 
 /// The Built Collection [SetMultimap].
 ///
@@ -20,34 +20,25 @@
   final BuiltSet<V> _emptySet = BuiltSet<V>();
 
   // Cached.
-  int _hashCode;
-  Iterable<K> _keys;
-  Iterable<V> _values;
+  int? _hashCode;
+  Iterable<K>? _keys;
+  Iterable<V>? _values;
 
   /// Instantiates with elements from a [Map], [SetMultimap] or
   /// [BuiltSetMultimap].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong:
-  ///   `new BuiltSetMultimap({1: ['1'], 2: ['2'], 3: ['3']})`.
-  ///
-  /// Right:
-  ///   `new BuiltSetMultimap<int, String>({1: ['1'], 2: ['2'], 3: ['3']})`,
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory BuiltSetMultimap([multimap = const {}]) {
     if (multimap is _BuiltSetMultimap &&
         multimap.hasExactKeyAndValueTypes(K, V)) {
       return multimap as BuiltSetMultimap<K, V>;
-    } else if (multimap is Map ||
-        multimap is SetMultimap ||
-        multimap is BuiltSetMultimap) {
+    } else if (multimap is Map) {
+      return _BuiltSetMultimap<K, V>.copyAndCheck(
+          multimap.keys, (k) => multimap[k]);
+    } else if (multimap is BuiltSetMultimap) {
       return _BuiltSetMultimap<K, V>.copyAndCheck(
           multimap.keys, (k) => multimap[k]);
     } else {
-      throw ArgumentError('expected Map, SetMultimap or BuiltSetMultimap, '
-          'got ${multimap.runtimeType}');
+      return _BuiltSetMultimap<K, V>.copyAndCheck(
+          multimap.keys, (k) => multimap[k]);
     }
   }
 
@@ -85,7 +76,7 @@
         .map((key) => hash2(key.hashCode, _map[key].hashCode))
         .toList(growable: false)
           ..sort());
-    return _hashCode;
+    return _hashCode!;
   }
 
   /// Deep equality.
@@ -93,7 +84,7 @@
   /// A `BuiltSetMultimap` is only equal to another `BuiltSetMultimap` with
   /// equal key/values pairs in any order.
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     if (identical(other, this)) return true;
     if (other is! BuiltSetMultimap) return false;
     if (other.length != length) return false;
@@ -116,16 +107,16 @@
   // SetMultimap.
 
   /// As [SetMultimap], but results are [BuiltSet]s and not mutable.
-  BuiltSet<V> operator [](Object key) {
+  BuiltSet<V>? operator [](Object? key) {
     var result = _map[key];
     return identical(result, null) ? _emptySet : result;
   }
 
   /// As [SetMultimap.containsKey].
-  bool containsKey(Object key) => _map.containsKey(key);
+  bool containsKey(Object? key) => _map.containsKey(key);
 
   /// As [SetMultimap.containsValue].
-  bool containsValue(Object value) => values.contains(value);
+  bool containsValue(Object? value) => values.contains(value);
 
   /// As [SetMultimap.forEach].
   void forEach(void Function(K, V) f) {
@@ -153,7 +144,7 @@
   /// instance.
   Iterable<K> get keys {
     _keys ??= _map.keys;
-    return _keys;
+    return _keys!;
   }
 
   /// As [SetMultimap.length].
@@ -163,21 +154,12 @@
   /// same instance.
   Iterable<V> get values {
     _values ??= _map.values.expand((x) => x);
-    return _values;
+    return _values!;
   }
 
   // Internal.
 
-  BuiltSetMultimap._(this._map) {
-    if (K == dynamic) {
-      throw UnsupportedError('explicit key type required, '
-          'for example "new BuiltSetMultimap<int, int>"');
-    }
-    if (V == dynamic) {
-      throw UnsupportedError('explicit value type required,'
-          ' for example "new BuiltSetMultimap<int, int>"');
-    }
-  }
+  BuiltSetMultimap._(this._map);
 }
 
 /// Default implementation of the public [BuiltSetMultimap] interface.
diff --git a/built_collection/lib/src/set_multimap/set_multimap_builder.dart b/built_collection/lib/src/set_multimap/set_multimap_builder.dart
index f4b078f..2d7bf3e 100644
--- a/built_collection/lib/src/set_multimap/set_multimap_builder.dart
+++ b/built_collection/lib/src/set_multimap/set_multimap_builder.dart
@@ -2,7 +2,7 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-part of built_collection.set_multimap;
+part of '../set_multimap.dart';
 
 /// The Built Collection builder for [BuiltSetMultimap].
 ///
@@ -14,37 +14,24 @@
 class SetMultimapBuilder<K, V> {
   // BuiltSets copied from another instance so they can be reused directly for
   // keys without changes.
-  Map<K, BuiltSet<V>> _builtMap;
+  late Map<K, BuiltSet<V>> _builtMap;
   // Instance that _builtMap belongs to. If present, _builtMap must not be
   // mutated.
-  _BuiltSetMultimap<K, V> _builtMapOwner;
+  _BuiltSetMultimap<K, V>? _builtMapOwner;
   // SetBuilders for keys that are being changed.
-  Map<K, SetBuilder<V>> _builderMap;
+  late Map<K, SetBuilder<V>> _builderMap;
 
   /// Instantiates with elements from a [Map], [SetMultimap] or
   /// [BuiltSetMultimap].
-  ///
-  /// Must be called with a generic type parameter.
-  ///
-  /// Wrong:
-  ///   `new SetMultimapBuilder({1: ['1'], 2: ['2'], 3: ['3']})`.
-  ///
-  /// Right:
-  ///   `new SetMultimapBuilder<int, String>({1: ['1'], 2: ['2'], 3: ['3']})`,
-  ///
-  /// Rejects nulls. Rejects keys and values of the wrong type.
   factory SetMultimapBuilder([multimap = const {}]) {
     return SetMultimapBuilder<K, V>._uninitialized()..replace(multimap);
   }
 
   /// Converts to a [BuiltSetMultimap].
-  ///
-  /// The `SetMultimapBuilder` can be modified again and used to create any
-  /// number of `BuiltSetMultimap`s.
   BuiltSetMultimap<K, V> build() {
     if (_builtMapOwner == null) {
       for (var key in _builderMap.keys) {
-        var builtSet = _builderMap[key].build();
+        var builtSet = _builderMap[key]!.build();
         if (builtSet.isEmpty) {
           _builtMap.remove(key);
         } else {
@@ -54,7 +41,7 @@
 
       _builtMapOwner = _BuiltSetMultimap<K, V>.withSafeMap(_builtMap);
     }
-    return _builtMapOwner;
+    return _builtMapOwner!;
   }
 
   /// Applies a function to `this`.
@@ -67,13 +54,12 @@
   void replace(dynamic multimap) {
     if (multimap is _BuiltSetMultimap<K, V>) {
       _setOwner(multimap);
-    } else if (multimap is Map ||
-        multimap is SetMultimap ||
-        multimap is BuiltSetMultimap) {
+    } else if (multimap is Map) {
+      _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]);
+    } else if (multimap is BuiltSetMultimap) {
       _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]);
     } else {
-      throw ArgumentError('expected Map, SetMultimap or BuiltSetMultimap, '
-          'got ${multimap.runtimeType}');
+      _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]);
     }
   }
 
@@ -86,9 +72,9 @@
   /// [key] and [value] default to the identity function. [values] is ignored
   /// if not specified.
   void addIterable<T>(Iterable<T> iterable,
-      {K Function(T) key,
-      V Function(T) value,
-      Iterable<V> Function(T) values}) {
+      {K Function(T)? key,
+      V Function(T)? value,
+      Iterable<V> Function(T)? values}) {
     if (value != null && values != null) {
       throw ArgumentError('expected value or values to be set, got both');
     }
@@ -125,16 +111,8 @@
     });
   }
 
-  /// As [SetMultimap.addAll].
-  void addAll(SetMultimap<K, V> other) {
-    // _disown is called in add.
-    other.forEach((key, value) {
-      add(key, value);
-    });
-  }
-
   /// As [SetMultimap.remove] but returns nothing.
-  void remove(Object key, V value) {
+  void remove(Object? key, V? value) {
     if (key is K) {
       _makeWriteableCopy();
       _getValuesBuilder(key).remove(value);
@@ -142,7 +120,7 @@
   }
 
   /// As [SetMultimap.removeAll] but returns nothing.
-  void removeAll(Object key) {
+  void removeAll(Object? key) {
     if (key is K) {
       _makeWriteableCopy();
 
@@ -182,9 +160,7 @@
     }
   }
 
-  SetMultimapBuilder._uninitialized() {
-    _checkGenericTypeParameter();
-  }
+  SetMultimapBuilder._uninitialized();
 
   void _setOwner(_BuiltSetMultimap<K, V> builtSetMultimap) {
     _builtMapOwner = builtSetMultimap;
@@ -213,24 +189,17 @@
     }
   }
 
-  void _checkGenericTypeParameter() {
-    if (K == dynamic) {
-      throw UnsupportedError('explicit key type required, '
-          'for example "new SetMultimapBuilder<int, int>"');
-    }
-    if (V == dynamic) {
-      throw UnsupportedError('explicit value type required, '
-          'for example "new SetMultimapBuilder<int, int>"');
-    }
-  }
-
   void _checkKey(K key) {
+    if (isSoundMode) return;
+    if (null is K) return;
     if (identical(key, null)) {
       throw ArgumentError('invalid key: $key');
     }
   }
 
   void _checkValue(V value) {
+    if (isSoundMode) return;
+    if (null is V) return;
     if (identical(value, null)) {
       throw ArgumentError('invalid value: $value');
     }
diff --git a/built_collection/pubspec.yaml b/built_collection/pubspec.yaml
index b1e2793..e22b6c3 100644
--- a/built_collection/pubspec.yaml
+++ b/built_collection/pubspec.yaml
@@ -1,5 +1,5 @@
 name: built_collection
-version: 4.3.2
+version: 5.1.0
 description: >
   Immutable collections based on the SDK collections. Each SDK collection class
   is split into a new immutable collection class and a corresponding mutable
@@ -7,12 +7,8 @@
 homepage: https://github.com/google/built_collection.dart
 
 environment:
-  sdk: '>=2.6.0 <3.0.0'
-
-dependencies:
-  collection: '^1.7.0'
-  quiver: '>=0.21.0 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dev_dependencies:
   pedantic: ^1.4.0
-  test: ^1.0.0
+  test: ^1.16.0-nullsafety
diff --git a/built_collection/tool/presubmit b/built_collection/tool/presubmit
index 241d957..6c6bb19 100755
--- a/built_collection/tool/presubmit
+++ b/built_collection/tool/presubmit
@@ -5,12 +5,13 @@
 
 set -e
 
-dartfmt -w $(find bin lib test -name \*.dart 2>/dev/null)
-pub get
-pub upgrade
+dart format $(find bin lib test -name \*.dart 2>/dev/null)
+dart pub get
+dart pub upgrade
+# For dart analyze there is no way yet to give a list of files
 dartanalyzer \
     --fatal-warnings \
     --fatal-infos \
-    --packages="$PWD/.packages" \
     $(find bin lib test -name \*.dart 2>/dev/null)
-pub run test
+
+dart test
diff --git a/built_value/BUILD.gn b/built_value/BUILD.gn
index 7d4b203..b616634 100644
--- a/built_value/BUILD.gn
+++ b/built_value/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for built_value-7.1.0
+# This file is generated by package_importer.py for built_value-8.1.0
 
 import("//build/dart/dart_library.gni")
 
 dart_library("built_value") {
   package_name = "built_value"
 
-  language_version = "2.3"
+  language_version = "2.12"
 
   disable_analysis = true
 
@@ -13,7 +13,7 @@
     "//third_party/dart-pkg/pub/built_collection",
     "//third_party/dart-pkg/pub/collection",
     "//third_party/dart-pkg/pub/fixnum",
-    "//third_party/dart-pkg/pub/quiver",
+    "//third_party/dart-pkg/pub/meta",
   ]
 
   sources = [
@@ -37,6 +37,7 @@
     "src/int64_serializer.dart",
     "src/int_serializer.dart",
     "src/json_object_serializer.dart",
+    "src/null_serializer.dart",
     "src/num_serializer.dart",
     "src/regexp_serializer.dart",
     "src/string_serializer.dart",
diff --git a/built_value/CHANGELOG.md b/built_value/CHANGELOG.md
index 218fbb4..6ba7db1 100644
--- a/built_value/CHANGELOG.md
+++ b/built_value/CHANGELOG.md
@@ -1,5 +1,89 @@
 # Changelog
 
+# 8.1.0
+
+New features:
+
+- Add `@BuiltValueHook` annotation. It provides the same functionality as
+  `_initializeBuilder` and `_finalizeBuilder`, but in a more visible way:
+  annotate a static method on the value class with `@BuiltValueHook` to
+  have it called on builder creation or finalization.
+- Add back `serializeNulls` to `BuiltValueSerializer` annotation. By default
+  generated serializers skip null fields instead of writing them; set
+  `serializeNulls` to write the nulls.
+
+New minor functionality:
+
+- Support use of nulls in collections when the key or value types are
+  explicitly nullable.
+- Allow `JsonObject` to be instantiated from a `Map<dynamic, dynamic>`.
+- Mark nested builder getters in `instantiable: false` classes not nullable,
+  to match the implementations. Use `autoCreateNestedBuilders: false` to get
+  the old behaviour.
+- Allow explicit nulls in JSON for nullable fields when deserializing.
+- Specify annotation targets; the analyzer will now hint if a `built_value`
+  annotation is used where it has no effect.
+- Support polymorphism with mixed in parent value type: generated builder
+  now mixes in parent builder.
+
+Bug fixes:
+
+- Fix support for serializing and deserializing nulls.
+- Fix `nestedBuilders: false` with `instantiable: false`.
+- Fix enum deserialization fallback for `int`.
+- Annotating a wrong type getter with `@SerializersFor` is now an error,
+  instead of just generating nothing.
+
+Cleanup:
+
+- Removed Angular mixin from example, as this feature is no longer needed:
+  Angular now directly supports using static members in templates.
+
+# 8.0.6
+
+- Bump versions of `build_config` and `build_runner`.
+
+# 8.0.5
+
+- Bump version of `analyzer`.
+
+# 8.0.4
+
+- Bump version of `source_gen`.
+
+# 8.0.3
+
+- Fix error message for builder factory not installed.
+- Bump version of `build`.
+
+# 8.0.2
+
+- Bump versions of `analyzer`, `quiver`.
+
+# 8.0.1
+
+- Update `chat` example to webdev.
+- Allow nulls when serializing/deserializing for better JSON interop.
+- Fix generation bugs around enum wire name and polymorphism.
+- Fix generation with generics for analysis with `strict-raw-types`.
+- Add test coverage around generation for generic serialization.
+- Add test coverage around initialization with generics.
+
+# 8.0.0
+
+- Stable null safe release.
+- Add `toJson` and `fromJson` convenience methods to `Serializers`.
+
+# 8.0.0-nullsafety.0
+
+- Migrate to NNBD.
+- Remove dependency on `package:quiver`.
+- Remove support for serializing nulls using
+  `BuiltValueSerializer(serializeNulls: true)`.
+- Make installed plugins public in `Serializers` as `serializerPlugins`.
+- `@memoized` fields can now memoize `null`; previously, nulls would not be
+  cached, causing the computation to rerun.
+
 # 7.1.0
 
 - Support private `Built` classes. Note that private classes cannot be made
diff --git a/built_value/README.md b/built_value/README.md
index d2c09b2..c582d48 100644
--- a/built_value/README.md
+++ b/built_value/README.md
@@ -35,8 +35,9 @@
 
 ## Tools
 
- - The [Json to Dart built_value class converter](https://charafau.github.io/json2builtvalue/) creates classes for you that correspond to a snippet of JSON
- - [VSCode extension](https://marketplace.visualstudio.com/items?itemName=GiancarloCode.built-value-snippets)
+ - [Json to Dart built_value class converter](https://charafau.github.io/json2builtvalue/)
+ - [Json or js Object to Dart built_value class converter](https://januwa.github.io/p5_object_2_builtvalue/index.html)
+- [VSCode extension](https://marketplace.visualstudio.com/items?itemName=GiancarloCode.built-value-snippets)
  - [IntelliJ plugin](https://plugins.jetbrains.com/plugin/13786-built-value-snippets)
 
 ## Examples
@@ -243,6 +244,45 @@
 
 ## FAQ
 
+### How do I check a field is valid on instantiation?
+
+The value class private constructor runs when all fields are initialized and
+can do arbitrary checks:
+
+```dart
+abstract class MyValue {
+  MyValue._() {
+    if (field < 0) {
+      throw ArgumentError(field, 'field', 'Must not be negative.');
+    }
+  }
+```
+
+### How do I process a field on instantiation?
+
+Add a hook that runs immediately before a builder is built. For example, you
+could sort a list, so it's always sorted directly before the value is created:
+
+```dart
+abstract class MyValue {
+  @BuiltValueHook(finalizeBuilder: true)
+  static void _sortItems(MyValueBuilder b) =>
+      b..items.sort();
+```
+
+### How do I set a default for a field?
+
+Add a hook that runs whenever a builder is created:
+
+```dart
+abstract class MyValue {
+  @BuiltValueHook(initializeBuilder: true)
+  static void _setDefaults(MyValueBuilder b) =>
+      b
+        ..name = 'defaultName'
+        ..count = 0;
+```
+
 ### Should I check in and/or publish in the generated `.g.dart` files?
 
 See the [build_runner](https://pub.dev/packages/build_runner#source-control)
diff --git a/built_value/lib/async_serializer.dart b/built_value/lib/async_serializer.dart
index 3cbf855..60d236b 100644
--- a/built_value/lib/async_serializer.dart
+++ b/built_value/lib/async_serializer.dart
@@ -2,8 +2,6 @@
 // All rights reserved. Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-import 'dart:async';
-
 import 'package:built_value/serializer.dart';
 
 /// Deserializer for `BuiltList` that runs asynchronously.
@@ -12,7 +10,7 @@
 /// the top level serialized object is a `BuiltList`. Then use this class to
 /// deserialize to a [Stream] of objects.
 class BuiltListAsyncDeserializer {
-  Stream<Object> deserialize(Serializers serializers, Iterable serialized,
+  Stream<Object?> deserialize(Serializers serializers, Iterable serialized,
       {FullType specifiedType = FullType.unspecified}) async* {
     var elementType = specifiedType.parameters.isEmpty
         ? FullType.unspecified
diff --git a/built_value/lib/built_value.dart b/built_value/lib/built_value.dart
index fdf0464..965ba4f 100644
--- a/built_value/lib/built_value.dart
+++ b/built_value/lib/built_value.dart
@@ -2,6 +2,8 @@
 // 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:meta/meta_meta.dart';
+
 /// Implement this for a Built Value.
 ///
 /// Then use built_value_generator.dart code generation functionality to
@@ -41,7 +43,7 @@
   /// Applies updates.
   ///
   /// [updates] is a function that takes a builder [B].
-  void update(Function(B) updates);
+  void update(Function(B)? updates);
 
   /// Builds.
   ///
@@ -103,7 +105,7 @@
 
   /// The wire name when the class is serialized. Defaults to `null` which
   /// indicates that the name is to be taken from the literal class name.
-  final String wireName;
+  final String? wireName;
 
   /// The default for [BuiltValueField.compare]. Set to `false` if you want to
   /// ignore most fields when comparing, then mark the ones you do want to
@@ -133,30 +135,45 @@
 
 /// Optionally, annotate a Built Value field with this to specify settings.
 /// This is only needed for advanced use.
+@Target({TargetKind.getter})
 class BuiltValueField {
   /// Whether the field is compared and hashed. Defaults to `null` which means
   /// [BuiltValue.defaultCompare] is used.
   ///
   /// Set to `false` to ignore the field when calculating `hashCode` and when
   /// comparing with `operator==`.
-  final bool compare;
+  final bool? compare;
 
   /// Whether the field is serialized. Defaults to `null` which means
   /// [BuiltValue.defaultSerialize] is used.
   ///
   /// If a field is not serialized, it must either be `@nullable` or specify a
   /// default for deserialization to succeed.
-  final bool serialize;
+  final bool? serialize;
 
   /// The wire name when the field is serialized. Defaults to `null` which
   /// indicates the name is to be taken from the literal field name.
-  final String wireName;
+  final String? wireName;
 
-  const BuiltValueField({this.compare, this.serialize, this.wireName});
+  /// Whether the field overrides the `nestedBuilders` setting from the class. Defaults to `null` which
+  /// indicates the setting is to be taken from the `nestedBuilders` setting on the class.
+  final bool? nestedBuilder;
+
+  /// Whether the field overrides the `autoCreateNestedBuilders` setting from the class. Defaults to `null` which
+  /// indicates the setting is to be taken from the `autoCreateNestedBuilders` setting on the class.
+  final bool? autoCreateNestedBuilder;
+
+  const BuiltValueField(
+      {this.compare,
+      this.serialize,
+      this.wireName,
+      this.nestedBuilder,
+      this.autoCreateNestedBuilder});
 }
 
 /// Optionally, annotate a Built Value `Serializer` getters with this to
 /// specify settings. This is only needed for advanced use.
+@Target({TargetKind.getter})
 class BuiltValueSerializer {
   /// Set this to `true` to stop Built Value from generating a serializer for
   /// you. The getter may then return any compatible `Serializer`. Defaults
@@ -166,6 +183,7 @@
   /// Whether the generated serializer should output `null`s.
   ///
   /// By default this is `false` and nulls are omitted from the output.
+
   final bool serializeNulls;
 
   const BuiltValueSerializer(
@@ -180,30 +198,32 @@
 
 /// Optionally, annotate an `EnumClass` with this to specify settings. This
 /// is only needed for advanced use.
+@Target({TargetKind.classType})
 class BuiltValueEnum {
   /// The wire name when the enum is serialized. Defaults to `null` which
   /// indicates that the name is to be taken from the literal class name.
-  final String wireName;
+  final String? wireName;
 
   const BuiltValueEnum({this.wireName});
 }
 
 /// Optionally, annotate an `EnumClass` constant with this to specify settings.
 /// This is only needed for advanced use.
+@Target({TargetKind.field})
 class BuiltValueEnumConst {
   /// The wire name when the constant is serialized. Defaults to `null` which
   /// indicates the name is to be taken from the literal field name.
   ///
   /// Or, set [wireNumber] to serialize to an `int`. Only one of the two may be
   /// used.
-  final String wireName;
+  final String? wireName;
 
   /// The wire name when the constant is serialized. Defaults to `null` which
   /// indicates the name is to be taken from the literal field name.
   ///
-  /// Or, set [wireNumber] to serialize to a `String`. Only one of the two may
+  /// Or, set [wireName] to serialize to a `String`. Only one of the two may
   /// be used.
-  final int wireNumber;
+  final int? wireNumber;
 
   /// Marks a value that is used as a fallback when an unrecognized value
   /// is encountered.
@@ -218,6 +238,46 @@
       {this.wireName, this.wireNumber, this.fallback = false});
 }
 
+/// Optionally, annotate methods with this to cause them to be called by
+/// generated code.
+@Target({TargetKind.method})
+class BuiltValueHook {
+  /// Marks a static method that will be called when the builder for the
+  /// enclosing value type is initialized.
+  ///
+  /// The method must accept a builder of the enclosing value type.
+  ///
+  /// This example uses it to set a default value:
+  ///
+  /// ```
+  /// @BuiltValueHook(initializeBuilder: true)
+  /// static void _initializeBuilder(MyClassBuilder b) =>
+  ///    b..name = 'defaultName';
+  ///
+  /// Defaults to `false`.
+  /// ```
+  final bool initializeBuilder;
+
+  /// Marks a static method that will be called immediately before the builder
+  /// for the enclosing value type is built.
+  ///
+  /// The method must accept a builder of the enclosing value type.
+  ///
+  /// This example uses it to make `items` sorted:
+  ///
+  /// ```
+  /// @BuiltValueHook(initializeBuilder: true)
+  /// static void _finalizeBuilder(MyClassBuilder b) =>
+  ///    b..items.sort();
+  ///
+  /// Defaults to `false`.
+  /// ```
+  final bool finalizeBuilder;
+
+  const BuiltValueHook(
+      {this.initializeBuilder = false, this.finalizeBuilder = false});
+}
+
 /// Enum Class base class.
 ///
 /// Extend this class then use the built_value.dart code generation
@@ -266,7 +326,7 @@
 /// version increase.
 abstract class BuiltValueToStringHelper {
   /// Add a field and its value.
-  void add(String field, Object value);
+  void add(String field, Object? value);
 
   /// Returns to completed toString(). The helper may not be used after this
   /// method is called.
@@ -276,17 +336,17 @@
 
 /// A [BuiltValueToStringHelper] that produces multi-line indented output.
 class IndentingBuiltValueToStringHelper implements BuiltValueToStringHelper {
-  StringBuffer _result = StringBuffer();
+  StringBuffer? _result = StringBuffer();
 
   IndentingBuiltValueToStringHelper(String className) {
-    _result..write(className)..write(' {\n');
+    _result!..write(className)..write(' {\n');
     _indentingBuiltValueToStringHelperIndent += 2;
   }
 
   @override
-  void add(String field, Object value) {
+  void add(String field, Object? value) {
     if (value != null) {
-      _result
+      _result!
         ..write(' ' * _indentingBuiltValueToStringHelperIndent)
         ..write(field)
         ..write('=')
@@ -298,7 +358,7 @@
   @override
   String toString() {
     _indentingBuiltValueToStringHelperIndent -= 2;
-    _result..write(' ' * _indentingBuiltValueToStringHelperIndent)..write('}');
+    _result!..write(' ' * _indentingBuiltValueToStringHelperIndent)..write('}');
     var stringResult = _result.toString();
     _result = null;
     return stringResult;
@@ -309,25 +369,25 @@
 
 /// A [BuiltValueToStringHelper] that produces single line output.
 class FlatBuiltValueToStringHelper implements BuiltValueToStringHelper {
-  StringBuffer _result = StringBuffer();
+  StringBuffer? _result = StringBuffer();
   bool _previousField = false;
 
   FlatBuiltValueToStringHelper(String className) {
-    _result..write(className)..write(' {');
+    _result!..write(className)..write(' {');
   }
 
   @override
-  void add(String field, Object value) {
+  void add(String field, Object? value) {
     if (value != null) {
-      if (_previousField) _result.write(',');
-      _result..write(field)..write('=')..write(value);
+      if (_previousField) _result!.write(',');
+      _result!..write(field)..write('=')..write(value);
       _previousField = true;
     }
   }
 
   @override
   String toString() {
-    _result..write('}');
+    _result!.write('}');
     var stringResult = _result.toString();
     _result = null;
     return stringResult;
@@ -342,6 +402,14 @@
 
   BuiltValueNullFieldError(this.type, this.field);
 
+  /// Throws a [BuiltValueNullFieldError] if [value] is `null`.
+  static T checkNotNull<T>(T? value, String type, String field) {
+    if (value == null) {
+      throw BuiltValueNullFieldError(type, field);
+    }
+    return value;
+  }
+
   @override
   String toString() =>
       'Tried to construct class "$type" with null field "$field". '
diff --git a/built_value/lib/iso_8601_date_time_serializer.dart b/built_value/lib/iso_8601_date_time_serializer.dart
index e64837f..b2b1c6f 100644
--- a/built_value/lib/iso_8601_date_time_serializer.dart
+++ b/built_value/lib/iso_8601_date_time_serializer.dart
@@ -31,7 +31,7 @@
   }
 
   @override
-  DateTime deserialize(Serializers serializers, Object serialized,
+  DateTime deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return DateTime.parse(serialized as String).toUtc();
   }
diff --git a/built_value/lib/iso_8601_duration_serializer.dart b/built_value/lib/iso_8601_duration_serializer.dart
index 0b45146..e038eee 100644
--- a/built_value/lib/iso_8601_duration_serializer.dart
+++ b/built_value/lib/iso_8601_duration_serializer.dart
@@ -15,9 +15,9 @@
 /// consume reasonable strings that match the standard.
 class Iso8601DurationSerializer extends PrimitiveSerializer<Duration> {
   @override
-  Duration deserialize(Serializers serializers, Object serialized,
+  Duration deserialize(Serializers serializers, Object? serialized,
           {FullType specifiedType = FullType.unspecified}) =>
-      _parseDuration(serialized);
+      _parseDuration(serialized as String);
 
   @override
   Object serialize(Serializers serializers, Duration object,
diff --git a/built_value/lib/json_object.dart b/built_value/lib/json_object.dart
index 5c91943..e8a42c9 100644
--- a/built_value/lib/json_object.dart
+++ b/built_value/lib/json_object.dart
@@ -55,20 +55,23 @@
 
   /// Instantiates with [value], which must be a bool, a List, a Map, a num
   /// or a String. Otherwise, an [ArgumentError] is thrown.
-  factory JsonObject(Object value) {
+  factory JsonObject(Object? value) {
     if (value is num) {
       return NumJsonObject(value);
     } else if (value is String) {
       return StringJsonObject(value);
     } else if (value is bool) {
       return BoolJsonObject(value);
-    } else if (value is List<Object>) {
+    } else if (value is List<Object?>) {
       return ListJsonObject(value);
-    } else if (value is Map<String, Object>) {
+    } else if (value is Map<String, Object?>) {
       return MapJsonObject(value);
+    } else if (value is Map) {
+      // Allow wrong type map, check individual values.
+      return MapJsonObject(value.cast());
     } else {
       throw ArgumentError.value(value, 'value',
-          'Must be bool, List<Object>, Map<String, Object>, num or String');
+          'Must be bool, List<Object?>, Map<String?, Object?>, num or String');
     }
   }
 
@@ -107,17 +110,17 @@
 /// A [JsonObject] holding a List.
 class ListJsonObject extends JsonObject {
   @override
-  final List<Object> value;
+  final List<Object?> value;
 
-  ListJsonObject(List<Object> value)
-      : value = UnmodifiableListView<Object>(value),
+  ListJsonObject(List<Object?> value)
+      : value = UnmodifiableListView<Object?>(value),
         super._();
 
   @override
   bool get isList => true;
 
   @override
-  List<Object> get asList => value;
+  List<Object?> get asList => value;
 
   @override
   bool operator ==(dynamic other) {
@@ -133,9 +136,9 @@
 /// A [JsonObject] holding a Map.
 class MapJsonObject extends JsonObject {
   @override
-  final Map<String, Object> value;
+  final Map<String, Object?> value;
 
-  MapJsonObject(Map<String, Object> value)
+  MapJsonObject(Map<String, Object?> value)
       : value = UnmodifiableMapView(value),
         super._();
 
@@ -143,7 +146,7 @@
   bool get isMap => true;
 
   @override
-  Map<String, Object> get asMap => value;
+  Map<String, Object?> get asMap => value;
 
   @override
   bool operator ==(dynamic other) {
diff --git a/built_value/lib/serializer.dart b/built_value/lib/serializer.dart
index 07ff2c9..8b391de 100644
--- a/built_value/lib/serializer.dart
+++ b/built_value/lib/serializer.dart
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 import 'package:built_collection/built_collection.dart';
+import 'package:built_collection/src/internal/hash.dart';
 import 'package:built_value/src/big_int_serializer.dart';
 import 'package:built_value/src/date_time_serializer.dart';
 import 'package:built_value/src/duration_serializer.dart';
@@ -10,7 +11,6 @@
 import 'package:built_value/src/json_object_serializer.dart';
 import 'package:built_value/src/num_serializer.dart';
 import 'package:built_value/src/uri_serializer.dart';
-import 'package:quiver/core.dart';
 
 import 'src/bool_serializer.dart';
 import 'src/built_json_serializers.dart';
@@ -21,6 +21,7 @@
 import 'src/built_set_serializer.dart';
 import 'src/double_serializer.dart';
 import 'src/int_serializer.dart';
+import 'src/null_serializer.dart';
 import 'src/regexp_serializer.dart';
 import 'src/string_serializer.dart';
 
@@ -67,6 +68,7 @@
           ..add(IntSerializer())
           ..add(Int64Serializer())
           ..add(JsonObjectSerializer())
+          ..add(NullSerializer())
           ..add(NumSerializer())
           ..add(RegExpSerializer())
           ..add(StringSerializer())
@@ -98,6 +100,12 @@
   /// The installed [Serializer]s.
   Iterable<Serializer> get serializers;
 
+  /// The installed builder factories.
+  BuiltMap<FullType, Function> get builderFactories;
+
+  /// The installed serializer plugins.
+  Iterable<SerializerPlugin> get serializerPlugins;
+
   /// Serializes [object].
   ///
   /// A [Serializer] must have been provided for every type the object uses.
@@ -109,13 +117,19 @@
   /// Create one using [SerializersBuilder].
   ///
   /// TODO(davidmorgan): document the wire format.
-  Object serialize(Object object,
+  Object? serialize(Object? object,
       {FullType specifiedType = FullType.unspecified});
 
   /// Convenience method for when you know the type you're serializing.
   /// Specify the type by specifying its [Serializer] class. Equivalent to
   /// calling [serialize] with a `specifiedType`.
-  Object serializeWith<T>(Serializer<T> serializer, T object);
+  Object? serializeWith<T>(Serializer<T> serializer, T? object);
+
+  /// Convenience method for when you want a JSON string and know the type
+  /// you're serializing. Specify the type by specifying its [Serializer]
+  /// class. Equivalent to calling [serialize] with a `specifiedType` then
+  /// calling `json.encode`.
+  String toJson<T>(Serializer<T> serializer, T? object);
 
   /// Deserializes [serialized].
   ///
@@ -123,21 +137,27 @@
   ///
   /// If [serialized] was produced by calling [serialize] with [specifiedType],
   /// the exact same [specifiedType] must be provided to deserialize.
-  Object deserialize(Object serialized,
+  Object? deserialize(Object? serialized,
       {FullType specifiedType = FullType.unspecified});
 
   /// Convenience method for when you know the type you're deserializing.
   /// Specify the type by specifying its [Serializer] class. Equivalent to
   /// calling [deserialize] with a `specifiedType`.
-  T deserializeWith<T>(Serializer<T> serializer, Object serialized);
+  T? deserializeWith<T>(Serializer<T> serializer, Object? serialized);
+
+  /// Convenience method for when you have a JSON string and know the type
+  /// you're deserializing. Specify the type by specifying its [Serializer]
+  /// class. Equivalent to calling [deserialize] with a `specifiedType` then
+  /// calling `json.decode`.
+  T? fromJson<T>(Serializer<T> serializer, String serialized);
 
   /// Gets a serializer; returns `null` if none is found. For use in plugins
   /// and other extension code.
-  Serializer serializerForType(Type type);
+  Serializer? serializerForType(Type type);
 
   /// Gets a serializer; returns `null` if none is found. For use in plugins
   /// and other extension code.
-  Serializer serializerForWireName(String wireName);
+  Serializer? serializerForWireName(String wireName);
 
   /// Creates a new builder for the type represented by [fullType].
   ///
@@ -154,22 +174,19 @@
   /// Throws if a builder for [fullType] is not available via [newBuilder].
   void expectBuilder(FullType fullType);
 
-  /// The installed builder factories.
-  BuiltMap<FullType, Function> get builderFactories;
-
   SerializersBuilder toBuilder();
 }
 
 /// Note: this is an experimental feature. API may change without a major
 /// version increase.
 abstract class SerializerPlugin {
-  Object beforeSerialize(Object object, FullType specifiedType);
+  Object? beforeSerialize(Object? object, FullType specifiedType);
 
-  Object afterSerialize(Object object, FullType specifiedType);
+  Object? afterSerialize(Object? object, FullType specifiedType);
 
-  Object beforeDeserialize(Object object, FullType specifiedType);
+  Object? beforeDeserialize(Object? object, FullType specifiedType);
 
-  Object afterDeserialize(Object object, FullType specifiedType);
+  Object? afterDeserialize(Object? object, FullType specifiedType);
 }
 
 /// Builder for [Serializers].
@@ -229,20 +246,30 @@
   static const FullType object = FullType(Object);
 
   /// The root of the type.
-  final Type root;
+  final Type? root;
 
   /// Type parameters of the type.
   final List<FullType> parameters;
 
-  const FullType(this.root, [this.parameters = const []]);
+  /// Whether the type is nullable.
+  final bool nullable;
+
+  const FullType(this.root, [this.parameters = const []]) : nullable = false;
+  const FullType.nullable(this.root, [this.parameters = const []])
+      : nullable = true;
 
   bool get isUnspecified => identical(root, null);
 
+  FullType withNullability(bool nullability) => nullability
+      ? FullType.nullable(root, parameters)
+      : FullType(root, parameters);
+
   @override
   bool operator ==(dynamic other) {
     if (identical(other, this)) return true;
     if (other is! FullType) return false;
     if (root != other.root) return false;
+    if (nullable != other.nullable) return false;
     if (parameters.length != other.parameters.length) return false;
     for (var i = 0; i != parameters.length; ++i) {
       if (parameters[i] != other.parameters[i]) return false;
@@ -252,17 +279,20 @@
 
   @override
   int get hashCode {
-    return hash2(root, hashObjects(parameters));
+    return hash2(root, hashObjects(parameters)) ^ (nullable ? 0x696eefd9 : 0);
   }
 
   @override
   String toString() => isUnspecified
       ? 'unspecified'
-      : parameters.isEmpty
-          ? _getRawName(root)
-          : '${_getRawName(root)}<${parameters.join(", ")}>';
+      : (parameters.isEmpty
+              ? _getRawName(root)
+              : '${_getRawName(root)}<${parameters.join(", ")}>') +
+          _nullabilitySuffix;
 
-  static String _getRawName(Type type) {
+  String get _nullabilitySuffix => nullable ? '?' : '';
+
+  static String _getRawName(Type? type) {
     var name = type.toString();
     var genericsStart = name.indexOf('<');
     return genericsStart == -1 ? name : name.substring(0, genericsStart);
@@ -328,7 +358,7 @@
   /// JSON: booleans, integers, doubles, Strings and [Iterable]s.
   ///
   /// TODO(davidmorgan): document the wire format.
-  Iterable serialize(Serializers serializers, T object,
+  Iterable<Object?> serialize(Serializers serializers, T object,
       {FullType specifiedType = FullType.unspecified});
 
   /// Deserializes [serialized].
@@ -338,17 +368,17 @@
   ///
   /// Use [serializers] as needed for nested deserialization. Information about
   /// the type being deserialized is provided in [specifiedType].
-  T deserialize(Serializers serializers, Iterable serialized,
+  T deserialize(Serializers serializers, Iterable<Object?> serialized,
       {FullType specifiedType = FullType.unspecified});
 }
 
 /// [Error] conveying why deserialization failed.
 class DeserializationError extends Error {
-  final String json;
+  final String? json;
   final FullType type;
   final Error error;
 
-  factory DeserializationError(Object json, FullType type, Error error) {
+  factory DeserializationError(Object? json, FullType type, Error error) {
     var limitedJson = json.toString();
     if (limitedJson.length > 80) {
       limitedJson = limitedJson.replaceRange(77, limitedJson.length, '...');
diff --git a/built_value/lib/src/big_int_serializer.dart b/built_value/lib/src/big_int_serializer.dart
index a34dfc2..5afd747 100644
--- a/built_value/lib/src/big_int_serializer.dart
+++ b/built_value/lib/src/big_int_serializer.dart
@@ -22,7 +22,7 @@
   }
 
   @override
-  BigInt deserialize(Serializers serializers, Object serialized,
+  BigInt deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return BigInt.parse(serialized as String);
   }
diff --git a/built_value/lib/src/bool_serializer.dart b/built_value/lib/src/bool_serializer.dart
index 7438564..0d83dff 100644
--- a/built_value/lib/src/bool_serializer.dart
+++ b/built_value/lib/src/bool_serializer.dart
@@ -19,7 +19,7 @@
   }
 
   @override
-  bool deserialize(Serializers serializers, Object serialized,
+  bool deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return serialized as bool;
   }
diff --git a/built_value/lib/src/built_json_serializers.dart b/built_value/lib/src/built_json_serializers.dart
index 7d39b5e..08b96c4 100644
--- a/built_value/lib/src/built_json_serializers.dart
+++ b/built_value/lib/src/built_json_serializers.dart
@@ -1,8 +1,9 @@
 // Copyright (c) 2015, Google Inc. 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 'dart:convert';
+
 import 'package:built_collection/built_collection.dart';
 import 'package:built_value/serializer.dart';
 
@@ -23,54 +24,68 @@
   @override
   final BuiltMap<FullType, Function> builderFactories;
 
-  final BuiltList<SerializerPlugin> _plugins;
+  @override
+  final BuiltList<SerializerPlugin> serializerPlugins;
 
-  BuiltJsonSerializers._(this._typeToSerializer, this._wireNameToSerializer,
-      this._typeNameToSerializer, this.builderFactories, this._plugins);
+  BuiltJsonSerializers._(
+      this._typeToSerializer,
+      this._wireNameToSerializer,
+      this._typeNameToSerializer,
+      this.builderFactories,
+      this.serializerPlugins);
 
   @override
   Iterable<Serializer> get serializers => _wireNameToSerializer.values;
 
   @override
-  T deserializeWith<T>(Serializer<T> serializer, Object serialized) {
+  T? deserializeWith<T>(Serializer<T> serializer, Object? serialized) {
     return deserialize(serialized,
-        specifiedType: FullType(serializer.types.first)) as T;
+        specifiedType: FullType(serializer.types.first)) as T?;
   }
 
   @override
-  Object serializeWith<T>(Serializer<T> serializer, T object) {
+  T? fromJson<T>(Serializer<T> serializer, String serialized) {
+    return deserializeWith<T>(serializer, json.decode(serialized));
+  }
+
+  @override
+  Object? serializeWith<T>(Serializer<T> serializer, T? object) {
     return serialize(object, specifiedType: FullType(serializer.types.first));
   }
 
   @override
-  Object serialize(Object object,
+  String toJson<T>(Serializer<T> serializer, T? object) {
+    return json.encode(serializeWith<T>(serializer, object));
+  }
+
+  @override
+  Object? serialize(Object? object,
       {FullType specifiedType = FullType.unspecified}) {
     var transformedObject = object;
-    for (var plugin in _plugins) {
+    for (var plugin in serializerPlugins) {
       transformedObject =
           plugin.beforeSerialize(transformedObject, specifiedType);
     }
     var result = _serialize(transformedObject, specifiedType);
-    for (var plugin in _plugins) {
+    for (var plugin in serializerPlugins) {
       result = plugin.afterSerialize(result, specifiedType);
     }
     return result;
   }
 
-  Object _serialize(Object object, FullType specifiedType) {
+  Object? _serialize(Object? object, FullType specifiedType) {
     if (specifiedType.isUnspecified) {
       final serializer = serializerForType(object.runtimeType);
       if (serializer == null) {
         throw StateError("No serializer for '${object.runtimeType}'.");
       }
       if (serializer is StructuredSerializer) {
-        final result = <Object>[serializer.wireName];
+        final result = <Object?>[serializer.wireName];
         return result..addAll(serializer.serialize(this, object));
       } else if (serializer is PrimitiveSerializer) {
-        return <Object>[
-          serializer.wireName,
-          serializer.serialize(this, object)
-        ];
+        return object == null
+            ? <Object?>[serializer.wireName, null]
+            : <Object>[serializer.wireName, serializer.serialize(this, object)];
       } else {
         throw StateError(
             'serializer must be StructuredSerializer or PrimitiveSerializer');
@@ -82,11 +97,15 @@
         return serialize(object);
       }
       if (serializer is StructuredSerializer) {
-        return serializer
-            .serialize(this, object, specifiedType: specifiedType)
-            .toList();
+        return object == null
+            ? null
+            : serializer
+                .serialize(this, object, specifiedType: specifiedType)
+                .toList();
       } else if (serializer is PrimitiveSerializer) {
-        return serializer.serialize(this, object, specifiedType: specifiedType);
+        return object == null
+            ? null
+            : serializer.serialize(this, object, specifiedType: specifiedType);
       } else {
         throw StateError(
             'serializer must be StructuredSerializer or PrimitiveSerializer');
@@ -95,24 +114,24 @@
   }
 
   @override
-  Object deserialize(Object object,
+  Object? deserialize(Object? object,
       {FullType specifiedType = FullType.unspecified}) {
     var transformedObject = object;
-    for (var plugin in _plugins) {
+    for (var plugin in serializerPlugins) {
       transformedObject =
           plugin.beforeDeserialize(transformedObject, specifiedType);
     }
     var result = _deserialize(object, transformedObject, specifiedType);
-    for (var plugin in _plugins) {
+    for (var plugin in serializerPlugins) {
       result = plugin.afterDeserialize(result, specifiedType);
     }
     return result;
   }
 
-  Object _deserialize(
-      Object objectBeforePlugins, Object object, FullType specifiedType) {
+  Object? _deserialize(
+      Object? objectBeforePlugins, Object? object, FullType specifiedType) {
     if (specifiedType.isUnspecified) {
-      final wireName = (object as List).first as String;
+      final wireName = (object as List<Object?>).first as String;
 
       final serializer = serializerForWireName(wireName);
       if (serializer == null) {
@@ -121,13 +140,16 @@
 
       if (serializer is StructuredSerializer) {
         try {
-          return serializer.deserialize(this, (object as List).sublist(1));
+          return serializer.deserialize(this, object.sublist(1));
         } on Error catch (error) {
           throw DeserializationError(object, specifiedType, error);
         }
       } else if (serializer is PrimitiveSerializer) {
         try {
-          return serializer.deserialize(this, (object as List)[1]);
+          var primitive = object[1];
+          return primitive == null
+              ? null
+              : serializer.deserialize(this, primitive);
         } on Error catch (error) {
           throw DeserializationError(object, specifiedType, error);
         }
@@ -148,15 +170,19 @@
 
       if (serializer is StructuredSerializer) {
         try {
-          return serializer.deserialize(this, object as Iterable,
-              specifiedType: specifiedType);
+          return object == null
+              ? null
+              : serializer.deserialize(this, object as Iterable<Object?>,
+                  specifiedType: specifiedType);
         } on Error catch (error) {
           throw DeserializationError(object, specifiedType, error);
         }
       } else if (serializer is PrimitiveSerializer) {
         try {
-          return serializer.deserialize(this, object,
-              specifiedType: specifiedType);
+          return object == null
+              ? null
+              : serializer.deserialize(this, object,
+                  specifiedType: specifiedType);
         } on Error catch (error) {
           throw DeserializationError(object, specifiedType, error);
         }
@@ -168,11 +194,11 @@
   }
 
   @override
-  Serializer serializerForType(Type type) =>
+  Serializer? serializerForType(Type? type) =>
       _typeToSerializer[type] ?? _typeNameToSerializer[_getRawName(type)];
 
   @override
-  Serializer serializerForWireName(String wireName) =>
+  Serializer? serializerForWireName(String wireName) =>
       _wireNameToSerializer[wireName];
 
   @override
@@ -187,7 +213,7 @@
     if (!hasBuilder(fullType)) _throwMissingBuilderFactory(fullType);
   }
 
-  void _throwMissingBuilderFactory(FullType fullType) {
+  Never _throwMissingBuilderFactory(FullType fullType) {
     throw StateError('No builder factory for $fullType. '
         'Fix by adding one, see SerializersBuilder.addBuilderFactory.');
   }
@@ -204,7 +230,7 @@
         _wireNameToSerializer.toBuilder(),
         _typeNameToSerializer.toBuilder(),
         builderFactories.toBuilder(),
-        _plugins.toBuilder());
+        serializerPlugins.toBuilder());
   }
 }
 
@@ -255,6 +281,10 @@
   @override
   void addBuilderFactory(FullType types, Function function) {
     _builderFactories[types] = function;
+    // Nullability of the top level type is irrelevant to serialization, but
+    // lookup might be done with either nullable or not nullable depending
+    // on the context. So, store both for fast lookup.
+    _builderFactories[types.withNullability(!types.nullable)] = function;
   }
 
   @override
@@ -286,7 +316,7 @@
   }
 }
 
-String _getRawName(Type type) {
+String _getRawName(Type? type) {
   var name = type.toString();
   var genericsStart = name.indexOf('<');
   return genericsStart == -1 ? name : name.substring(0, genericsStart);
diff --git a/built_value/lib/src/built_list_multimap_serializer.dart b/built_value/lib/src/built_list_multimap_serializer.dart
index 17916b4..6e9f8ff 100644
--- a/built_value/lib/src/built_list_multimap_serializer.dart
+++ b/built_value/lib/src/built_list_multimap_serializer.dart
@@ -15,7 +15,7 @@
   final String wireName = 'listMultimap';
 
   @override
-  Iterable serialize(
+  Iterable<Object?> serialize(
       Serializers serializers, BuiltListMultimap builtListMultimap,
       {FullType specifiedType = FullType.unspecified}) {
     var isUnderspecified =
@@ -29,7 +29,7 @@
         ? FullType.unspecified
         : specifiedType.parameters[1];
 
-    var result = <Object>[];
+    var result = <Object?>[];
     for (var key in builtListMultimap.keys) {
       result.add(serializers.serialize(key, specifiedType: keyType));
       result.add(builtListMultimap[key]
@@ -41,7 +41,8 @@
   }
 
   @override
-  BuiltListMultimap deserialize(Serializers serializers, Iterable serialized,
+  BuiltListMultimap deserialize(
+      Serializers serializers, Iterable<Object?> serialized,
       {FullType specifiedType = FullType.unspecified}) {
     var isUnderspecified =
         specifiedType.isUnspecified || specifiedType.parameters.isEmpty;
@@ -64,7 +65,7 @@
     for (var i = 0; i != serialized.length; i += 2) {
       final key = serializers.deserialize(serialized.elementAt(i),
           specifiedType: keyType);
-      final values = serialized.elementAt(i + 1).map(
+      final values = (serialized.elementAt(i + 1) as Iterable<Object?>).map(
           (value) => serializers.deserialize(value, specifiedType: valueType));
       for (var value in values) {
         result.add(key, value);
diff --git a/built_value/lib/src/built_list_serializer.dart b/built_value/lib/src/built_list_serializer.dart
index f2338a2..fbc3eab 100644
--- a/built_value/lib/src/built_list_serializer.dart
+++ b/built_value/lib/src/built_list_serializer.dart
@@ -14,7 +14,7 @@
   final String wireName = 'list';
 
   @override
-  Iterable serialize(Serializers serializers, BuiltList builtList,
+  Iterable<Object?> serialize(Serializers serializers, BuiltList builtList,
       {FullType specifiedType = FullType.unspecified}) {
     var isUnderspecified =
         specifiedType.isUnspecified || specifiedType.parameters.isEmpty;
diff --git a/built_value/lib/src/built_map_serializer.dart b/built_value/lib/src/built_map_serializer.dart
index 9062280..9693e35 100644
--- a/built_value/lib/src/built_map_serializer.dart
+++ b/built_value/lib/src/built_map_serializer.dart
@@ -14,7 +14,7 @@
   final String wireName = 'map';
 
   @override
-  Iterable serialize(Serializers serializers, BuiltMap builtMap,
+  Iterable<Object?> serialize(Serializers serializers, BuiltMap builtMap,
       {FullType specifiedType = FullType.unspecified}) {
     var isUnderspecified =
         specifiedType.isUnspecified || specifiedType.parameters.isEmpty;
@@ -27,7 +27,7 @@
         ? FullType.unspecified
         : specifiedType.parameters[1];
 
-    var result = <Object>[];
+    var result = <Object?>[];
     for (var key in builtMap.keys) {
       result.add(serializers.serialize(key, specifiedType: keyType));
       final value = builtMap[key];
diff --git a/built_value/lib/src/built_set_multimap_serializer.dart b/built_value/lib/src/built_set_multimap_serializer.dart
index 34ff094..4504ff3 100644
--- a/built_value/lib/src/built_set_multimap_serializer.dart
+++ b/built_value/lib/src/built_set_multimap_serializer.dart
@@ -14,7 +14,8 @@
   final String wireName = 'setMultimap';
 
   @override
-  Iterable serialize(Serializers serializers, BuiltSetMultimap builtSetMultimap,
+  Iterable<Object?> serialize(
+      Serializers serializers, BuiltSetMultimap builtSetMultimap,
       {FullType specifiedType = FullType.unspecified}) {
     var isUnderspecified =
         specifiedType.isUnspecified || specifiedType.parameters.isEmpty;
@@ -27,10 +28,10 @@
         ? FullType.unspecified
         : specifiedType.parameters[1];
 
-    var result = <Object>[];
+    var result = <Object?>[];
     for (var key in builtSetMultimap.keys) {
       result.add(serializers.serialize(key, specifiedType: keyType));
-      result.add(builtSetMultimap[key]
+      result.add(builtSetMultimap[key]!
           .map(
               (value) => serializers.serialize(value, specifiedType: valueType))
           .toList());
diff --git a/built_value/lib/src/built_set_serializer.dart b/built_value/lib/src/built_set_serializer.dart
index ffbfc4b..9a5d124 100644
--- a/built_value/lib/src/built_set_serializer.dart
+++ b/built_value/lib/src/built_set_serializer.dart
@@ -14,7 +14,7 @@
   final String wireName = 'set';
 
   @override
-  Iterable serialize(Serializers serializers, BuiltSet builtSet,
+  Iterable<Object?> serialize(Serializers serializers, BuiltSet builtSet,
       {FullType specifiedType = FullType.unspecified}) {
     var isUnderspecified =
         specifiedType.isUnspecified || specifiedType.parameters.isEmpty;
diff --git a/built_value/lib/src/date_time_serializer.dart b/built_value/lib/src/date_time_serializer.dart
index 91717b3..cbd4e2d 100644
--- a/built_value/lib/src/date_time_serializer.dart
+++ b/built_value/lib/src/date_time_serializer.dart
@@ -28,7 +28,7 @@
   }
 
   @override
-  DateTime deserialize(Serializers serializers, Object serialized,
+  DateTime deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     var microsecondsSinceEpoch = serialized as int;
     return DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch,
diff --git a/built_value/lib/src/double_serializer.dart b/built_value/lib/src/double_serializer.dart
index 0857dee..490edab 100644
--- a/built_value/lib/src/double_serializer.dart
+++ b/built_value/lib/src/double_serializer.dart
@@ -31,7 +31,7 @@
   }
 
   @override
-  double deserialize(Serializers serializers, Object serialized,
+  double deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     if (serialized == nan) {
       return double.nan;
diff --git a/built_value/lib/src/duration_serializer.dart b/built_value/lib/src/duration_serializer.dart
index 93845a8..af05e0b 100644
--- a/built_value/lib/src/duration_serializer.dart
+++ b/built_value/lib/src/duration_serializer.dart
@@ -23,7 +23,7 @@
   }
 
   @override
-  Duration deserialize(Serializers serializers, Object serialized,
+  Duration deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return Duration(microseconds: serialized as int);
   }
diff --git a/built_value/lib/src/int64_serializer.dart b/built_value/lib/src/int64_serializer.dart
index 7741f55..51f96a3 100644
--- a/built_value/lib/src/int64_serializer.dart
+++ b/built_value/lib/src/int64_serializer.dart
@@ -20,7 +20,7 @@
   }
 
   @override
-  Int64 deserialize(Serializers serializers, Object serialized,
+  Int64 deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return Int64.parseInt(serialized as String);
   }
diff --git a/built_value/lib/src/int_serializer.dart b/built_value/lib/src/int_serializer.dart
index 64f7f26..115e391 100644
--- a/built_value/lib/src/int_serializer.dart
+++ b/built_value/lib/src/int_serializer.dart
@@ -19,7 +19,7 @@
   }
 
   @override
-  int deserialize(Serializers serializers, Object serialized,
+  int deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return serialized as int;
   }
diff --git a/built_value/lib/src/json_object_serializer.dart b/built_value/lib/src/json_object_serializer.dart
index 1ba1d71..ef4dc14 100644
--- a/built_value/lib/src/json_object_serializer.dart
+++ b/built_value/lib/src/json_object_serializer.dart
@@ -27,7 +27,7 @@
   }
 
   @override
-  JsonObject deserialize(Serializers serializers, Object serialized,
+  JsonObject deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return JsonObject(serialized);
   }
diff --git a/built_value/lib/src/null_serializer.dart b/built_value/lib/src/null_serializer.dart
new file mode 100644
index 0000000..f6f3556
--- /dev/null
+++ b/built_value/lib/src/null_serializer.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, Google Inc. 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:built_collection/built_collection.dart';
+import 'package:built_value/serializer.dart';
+
+class NullSerializer implements PrimitiveSerializer<Null> {
+  final bool structured = false;
+  @override
+  final Iterable<Type> types = BuiltList<Type>([Null]);
+  @override
+  final String wireName = 'Null';
+
+  @override
+  Object serialize(Serializers serializers, Null value,
+      {FullType specifiedType = FullType.unspecified}) {
+    // Never actually called; `built_json_serializer.dart` handles nulls.
+    throw UnimplementedError();
+  }
+
+  @override
+  Null deserialize(Serializers serializers, Object? serialized,
+      {FullType specifiedType = FullType.unspecified}) {
+    // Never actually called; `built_json_serializer.dart` handles nulls.
+    throw UnimplementedError();
+  }
+}
diff --git a/built_value/lib/src/num_serializer.dart b/built_value/lib/src/num_serializer.dart
index b9a0173..40b74cd 100644
--- a/built_value/lib/src/num_serializer.dart
+++ b/built_value/lib/src/num_serializer.dart
@@ -28,7 +28,7 @@
   }
 
   @override
-  num deserialize(Serializers serializers, Object serialized,
+  num deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     if (serialized == DoubleSerializer.nan) {
       return double.nan;
diff --git a/built_value/lib/src/regexp_serializer.dart b/built_value/lib/src/regexp_serializer.dart
index 8fbd584..97d5c3c 100644
--- a/built_value/lib/src/regexp_serializer.dart
+++ b/built_value/lib/src/regexp_serializer.dart
@@ -21,7 +21,7 @@
   }
 
   @override
-  RegExp deserialize(Serializers serializers, Object serialized,
+  RegExp deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return RegExp(serialized as String);
   }
diff --git a/built_value/lib/src/string_serializer.dart b/built_value/lib/src/string_serializer.dart
index 46d930f..5d7bea4 100644
--- a/built_value/lib/src/string_serializer.dart
+++ b/built_value/lib/src/string_serializer.dart
@@ -19,7 +19,7 @@
   }
 
   @override
-  String deserialize(Serializers serializers, Object serialized,
+  String deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return serialized as String;
   }
diff --git a/built_value/lib/src/uri_serializer.dart b/built_value/lib/src/uri_serializer.dart
index 4d8389e..c4936de 100644
--- a/built_value/lib/src/uri_serializer.dart
+++ b/built_value/lib/src/uri_serializer.dart
@@ -26,7 +26,7 @@
   }
 
   @override
-  Uri deserialize(Serializers serializers, Object serialized,
+  Uri deserialize(Serializers serializers, Object? serialized,
       {FullType specifiedType = FullType.unspecified}) {
     return Uri.parse(serialized as String);
   }
diff --git a/built_value/lib/standard_json_plugin.dart b/built_value/lib/standard_json_plugin.dart
index fc3a9ad..6d1c200 100644
--- a/built_value/lib/standard_json_plugin.dart
+++ b/built_value/lib/standard_json_plugin.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, Google Inc. 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:built_collection/built_collection.dart';
 import 'package:built_value/json_object.dart';
 import 'package:built_value/serializer.dart';
@@ -27,7 +31,7 @@
   StandardJsonPlugin({this.discriminator = r'$', this.valueKey = ''});
 
   @override
-  Object beforeSerialize(Object object, FullType specifiedType) {
+  Object? beforeSerialize(Object? object, FullType specifiedType) {
     if (_unsupportedTypes.contains(specifiedType.root)) {
       throw ArgumentError(
           'Standard JSON cannot serialize type ${specifiedType.root}.');
@@ -36,7 +40,7 @@
   }
 
   @override
-  Object afterSerialize(Object object, FullType specifiedType) {
+  Object? afterSerialize(Object? object, FullType specifiedType) {
     if (object is List &&
         specifiedType.root != BuiltList &&
         specifiedType.root != BuiltSet &&
@@ -52,12 +56,13 @@
   }
 
   @override
-  Object beforeDeserialize(Object object, FullType specifiedType) {
+  Object? beforeDeserialize(Object? object, FullType specifiedType) {
     if (object is Map && specifiedType.root != JsonObject) {
       if (specifiedType.isUnspecified) {
         return _toListUsingDiscriminator(object);
       } else {
-        return _toList(object, _needsEncodedKeys(specifiedType));
+        return _toList(object, _needsEncodedKeys(specifiedType),
+            keepNulls: specifiedType.root == BuiltMap);
       }
     } else {
       return object;
@@ -65,7 +70,7 @@
   }
 
   @override
-  Object afterDeserialize(Object object, FullType specifiedType) {
+  Object? afterDeserialize(Object? object, FullType specifiedType) {
     return object;
   }
 
@@ -78,7 +83,7 @@
   /// Converts serialization output, a `List`, to a `Map`, when the serialized
   /// type is known statically.
   Map _toMap(List list, bool needsEncodedKeys) {
-    var result = <String, Object>{};
+    var result = <String, Object?>{};
     for (var i = 0; i != list.length ~/ 2; ++i) {
       final key = list[i * 2];
       final value = list[i * 2 + 1];
@@ -102,7 +107,7 @@
     // the value.
     if (list.length == 2) {
       // Just a type and a primitive value. Encode the value in the map.
-      return <String, Object>{discriminator: type, valueKey: list[1]};
+      return <String, Object?>{discriminator: type, valueKey: list[1]};
     }
 
     // If a map has non-String keys then they need encoding to strings before
@@ -139,12 +144,19 @@
 
   /// Converts [StandardJsonPlugin] serialization output, a `Map`, to a `List`,
   /// when the serialized type is known statically.
-  List _toList(Map map, bool hasEncodedKeys) {
-    var result = List(map.length * 2);
+  ///
+  /// By default keys with null values are dropped, pass [keepNulls] true when
+  /// the map is an actual map with nullable values, so they should be kept.
+  List<Object?> _toList(Map map, bool hasEncodedKeys,
+      {bool keepNulls = false}) {
+    var nullValueCount =
+        keepNulls ? 0 : map.values.where((value) => value == null).length;
+    var result = List<Object?>.filled(
+        (map.length - nullValueCount) * 2, 0 /* Will be overwritten. */);
     var i = 0;
     map.forEach((key, value) {
       // Drop null values, they are represented by missing keys.
-      if (value == null) return;
+      if (!keepNulls && value == null) return;
 
       result[i] = hasEncodedKeys ? _decodeKey(key as String) : key;
       result[i + 1] = value;
@@ -156,7 +168,7 @@
   /// Converts [StandardJsonPlugin] serialization output, a `Map`, to a `List`,
   /// when the serialized type is not known statically. The type is retrieved
   /// from the [discriminator] field.
-  List _toListUsingDiscriminator(Map map) {
+  List<Object?> _toListUsingDiscriminator(Map map) {
     var type = map[discriminator];
 
     if (type == null) {
@@ -170,7 +182,7 @@
 
     if (map.containsKey(valueKey)) {
       // Just a type and a primitive value. Retrieve the value in the map.
-      final result = List(2);
+      final result = List<Object?>.filled(2, 0 /* Will be overwritten. */);
       result[0] = type;
       result[1] = map[valueKey];
       return result;
@@ -184,7 +196,9 @@
       type = 'map';
     }
 
-    var result = List(map.length * 2 - 1);
+    var nullValueCount = map.values.where((value) => value == null).length;
+    var result = List<Object>.filled(
+        (map.length - nullValueCount) * 2 - 1, 0 /* Will be overwritten. */);
     result[0] = type;
 
     var i = 1;
@@ -202,7 +216,7 @@
   }
 
   /// JSON-decodes a `String` encoded using [_encodeKey].
-  Object _decodeKey(String key) {
+  Object? _decodeKey(String key) {
     return json.decode(key);
   }
 }
diff --git a/built_value/pubspec.yaml b/built_value/pubspec.yaml
index b6d2977..1484c73 100644
--- a/built_value/pubspec.yaml
+++ b/built_value/pubspec.yaml
@@ -1,19 +1,19 @@
 name: built_value
-version: 7.1.0
+version: 8.1.0
 description: >
   Value types with builders, Dart classes as enums, and serialization.
   This library is the runtime dependency.
 homepage: https://github.com/google/built_value.dart
 
 environment:
-  sdk: '>=2.3.0 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dependencies:
-  built_collection: '>=2.0.0 <5.0.0'
-  collection: ^1.0.0
-  fixnum: ^0.10.0
-  quiver: '>=0.21.0 <3.0.0'
+  built_collection: ^5.0.0
+  collection: ^1.15.0
+  fixnum: ^1.0.0
+  meta: ^1.3.0
 
 dev_dependencies:
   pedantic: ^1.4.0
-  test: ^1.0.0
+  test: ^1.16.0
diff --git a/characters/BUILD.gn b/characters/BUILD.gn
index b04eb95..4e356cd 100644
--- a/characters/BUILD.gn
+++ b/characters/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for characters-1.1.0
+# This file is generated by package_importer.py for characters-1.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/charcode/.gitignore b/charcode/.gitignore
deleted file mode 100755
index 79f51c3..0000000
--- a/charcode/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.dart_tool
-.packages
-pubspec.lock
diff --git a/charcode/.travis.yml b/charcode/.travis.yml
deleted file mode 100755
index 4fceea8..0000000
--- a/charcode/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: dart
-
-dart:
-  - preview/raw/2.10.0-0.2-dev
-
-dart_task:
-  - dart_analyzer: --enable-experiment=non-nullable --fatal-warnings --fatal-infos .
-
-matrix:
-  include:
-  # Only validate formatting using the dev release
-  - dart: preview/raw/2.10.0-0.2-dev
-    dart_task: dartfmt
-
-# Only building master means that we don't run two builds for each pull request.
-branches:
-  only: [master]
-
-cache:
- directories:
-   - $HOME/.pub-cache
diff --git a/charcode/AUTHORS b/charcode/AUTHORS
old mode 100755
new mode 100644
diff --git a/charcode/BUILD.gn b/charcode/BUILD.gn
index e5f217f..b0e306e 100644
--- a/charcode/BUILD.gn
+++ b/charcode/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for charcode-1.2.0
+# This file is generated by package_importer.py for charcode-1.3.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/charcode/CHANGELOG.md b/charcode/CHANGELOG.md
old mode 100755
new mode 100644
index b887dee..fdabe47
--- a/charcode/CHANGELOG.md
+++ b/charcode/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 1.3.1
+
+* Optimize for Pub score.
+
+## 1.3.0
+
+* Add camelCase constant names as alternatives for current snake_case constants.
+  Example `$doubleQuote` as alternative to `$double_quote`.
+* Internal tweaks to flag parsing.
+* Switch to using `package:lints`.
+
 ## 1.2.0
 
 * Stable release for null safety.
diff --git a/charcode/CONTRIBUTING.md b/charcode/CONTRIBUTING.md
old mode 100755
new mode 100644
diff --git a/charcode/LICENSE b/charcode/LICENSE
old mode 100755
new mode 100644
diff --git a/charcode/README.md b/charcode/README.md
old mode 100755
new mode 100644
index a6951f0..a878403
--- a/charcode/README.md
+++ b/charcode/README.md
@@ -1,26 +1,53 @@
--[![Build Status](https://travis-ci.org/lrhn/charcode.svg?branch=master)](https://travis-ci.org/lrhn/charcode)
--[![Pub](https://img.shields.io/pub/v/charcode.svg)](https://pub.dev/packages/charcode) -
+[![Build Status](https://github.com/lrhn/charcode/workflows/Dart%20CI/badge.svg)](https://github.com/lrhn/charcode/actions?query=workflow%3A"Dart+CI")
+[![Pub](https://img.shields.io/pub/v/charcode.svg)](https://pub.dev/packages/charcode)
 
 # Character code constants
 
-This package defines symbolic names for some character codes (aka. code points).
+This package can *generate* constant symbolic names for character codes.
 
-They can used when working directly with characters as integers,
-to make the code more readable: `if (firstChar == $A) ...`.
+The constants can used when working directly with characters as integers,
+to make the code more readable: `if (firstChar == $A) ...` vs
+`if (firstChar == 0x41 /*A*/)`.
 
-This is not an official Google package, and is not supported by Google.
+# Pre-defined constants
+
+The package also provides a set of pre-defined constants covering all
+ASCII characters and all HTML entities.
+
+Those constants are intended for use while developing
+a new package or application. When development is done,
+it's recommended that you generate a file for yourself,
+containing only the constants that you actually use,
+and include that in your project.
 
 ## Usage
 
-Import either one of the libraries:
+To generate a set of constants, run the `charcode` application with a list
+of the characters you want to reference.
+
+Example:
+```bash
+dart run charcode -o lib/src/charcodes.dart "09.e\-ftn{}[],:"
+```
+
+Run `dart run charcode --help` to see other options.
+
+After switching to the generated constants file, you can, and should,
+remove your dependency on this package, or keep it as a dev-dependency
+in case you want to generate the file again.
+
+To use the pre-defined constants, import either the ASCII or the HTML library
 ```dart
 import "package:charcode/ascii.dart";
+// or
 import "package:charcode/html_entity.dart";
 ```
-or import both libraries using the `charcode.dart` library:
+or import both libraries using the combined `charcode.dart` library:
 ```dart
 import "package:charcode/charcode.dart";
 ```
+(Importing both libraries directly causes some conflicting names
+to be inaccessible.)
 
 ## Naming
 
@@ -32,20 +59,20 @@
 
 The names of letters are lower-case for lower-case letters (`$sigma` for `σ`),
 and mixed- or upper-case for upper-case letters (`$Sigma` for `Σ`).
-The names of symbols and punctuation are all lower-case,
+The names of symbols and punctuation are all lower-case or camelCase,
 and omit suffixes like "sign", "symbol" and "mark".
-Examples: `$plus`, `$exclamation`, `$tilde`.
+Examples: `$plus`, `$exclamation`, `$tilde`, `$doubleQuote`.
 
 The `ascii.dart` library defines a symbolic name for each ASCII character.
 Some characters have more than one name. For example the common name `$tab`
-and the official abbreviation `$ht` for the horisontal tab.
+and the official abbreviation `$ht` for the horizontal tab.
 
 The `html_entity.dart` library defines a constant for each HTML 4.01 character
 entity using their standard entity abbreviation, including case.
 Examples: `$nbsp` for `&nbps;`, `$aring` for the lower-case `&aring;`
 and `$Aring` for the upper-case `&Aring;`.
 
-The HTML entities include all characters in the Latin-1 code page, greek
+The HTML entities include all characters in the Latin-1 code page, Greek
 letters and some mathematical symbols.
 
 The `charcode.dart` library exports both `ascii.dart` and
@@ -57,4 +84,4 @@
 The Dart language doesn't have character literals.
 If that ever changes, this package will become irrelevant.
 Until then, this package can be used for the most common characters.
-See [request for character literals](http://dartbug.com/4415).
+See [request for character literals](https://dartbug.com/4415).
diff --git a/charcode/analysis_options.yaml b/charcode/analysis_options.yaml
old mode 100755
new mode 100644
index 007066a..6d1ff7a
--- a/charcode/analysis_options.yaml
+++ b/charcode/analysis_options.yaml
@@ -2,9 +2,4 @@
 # for details. All rights reserved. Use of this source is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-include: package:pedantic/analysis_options.1.9.0.yaml
-analyzer:
-  errors:
-    annotate_overrides: ignore
-    prefer_single_quotes: ignore
-    use_function_type_syntax_for_parameters: ignore
+include: package:lints/recommended.yaml
diff --git a/charcode/bin/charcode.dart b/charcode/bin/charcode.dart
old mode 100755
new mode 100644
index 02ae2a4..2053fde
--- a/charcode/bin/charcode.dart
+++ b/charcode/bin/charcode.dart
@@ -70,7 +70,7 @@
     ..add(FlagConfig("?", null, "help",
         description: "Display this usage information"))
     ..add(FlagConfig.optionalParameter("p", "p", "prefix", "",
-        description: "Sets prefix for later generated constants",
+        description: "Sets prefix for later generated constants.",
         valueDescription: "PREFIX"))
     ..add(FlagConfig.requiredParameter("o", "o", "output",
         description: "Write generated code to file instead of stdout",
@@ -84,7 +84,7 @@
 
   for (var arg in parseFlags(flags, args, stderr.writeln)) {
     var key = arg.key;
-    if (key == null) {
+    if (!arg.isFlag) {
       // Not a flag, value is command line argument.
       declarations.parse(arg.value!);
     } else {
@@ -147,7 +147,6 @@
 
 /// A single declaration to be written to the output.
 class Entry implements Comparable<Entry> {
-
   /// The code point.
   final int charCode;
 
@@ -178,8 +177,8 @@
     output.writeln("const int $prefix$name = 0x$hex;");
   }
 
-  /// Orders entries by [charCode] first, then by
-  /// prefixed and name.
+  /// Orders entries by [charCode] first, then by prefixed and name.
+  @override
   int compareTo(Entry other) {
     var delta = charCode - other.charCode;
     if (delta == 0) {
@@ -196,6 +195,7 @@
 class CharacterDeclaration {
   /// The unprefixed name to use for the code point.
   final String name;
+
   /// An optional description.
   final String? description;
   CharacterDeclaration(this.name, this.description);
@@ -332,7 +332,8 @@
           "(U+${charCode.toRadixString(16).toUpperCase().padLeft(4, "0")})");
       return false;
     }
-    _entries.add(Entry(charCode, declaration.name, prefix, declaration.description));
+    _entries.add(
+        Entry(charCode, declaration.name, prefix, declaration.description));
     _entryNames.add(name);
     return true;
   }
@@ -886,25 +887,32 @@
     ..rename(0x2666, "diams", "black diamond suit ('♦')");
 }
 
+/// Usage string printed if the `--help` or `-?` flags are passed.
+///
+/// Flag definitions are appended after the text.
 const String usageString = r"""
 Usage:
   charcode [-h] [-oFILE] (<character-range> | <rename> | -pPREFIX | -fFILE)*
 
+  Emits constant declarations for the characaters specified by character ranges.
+  The constants use the pre-defined names and descriptions for ASCII characters.
+
   A <character-range> is either
-   - A literal character,
-   - An escaped character `\n`, `\r`, `\t`, `\xhh`, `\uhhhh` or `\dd*`,
-       where `h` is a hexadecimal digit and `d` is a decimal digit.
-   - Two characters separated by `-`,
-   - The `\d`, `\w` or `\s` escapes, or
+   - a literal character,
+   - an escaped character `\n`, `\r`, `\t`, `\xHH`, `\uHHHH` or `\DD*`,
+       where `H` is a hexadecimal digit and `D` is a decimal digit.
+   - two such characters separated by `-`,
+   - The `\d` (`0-9`), `\w` (`a-zA-Z0-9$_`) or `\s` (`\x20\t\r\n`) escapes, or
    - A sequence of character ranges.
   Examples: `a`, `a-z`, `\da-fA-F`, `\x00-\uFFFF`.
 
   A <rename> declaration is a single or escaped character, a `=` and an
   identifier name, optionally followed by a `:` and a description
   Example:  x=cross:\"a cross product.\"
-  The declaration names or renames the character and adds or changes
-  the associated description, but will not add the character to the output.
-  Following occurrences of the character will use the new name.
+  The declaration names or renames the character, and adds or changes
+  the associated description, but will not emit the character to the output.
+  Following occurrences of the character in character ranges
+  will use the new name.
   Example: `charcode y y=why x-z` will generate `$y` for the character
     code of "y", then `$x`, `$why` and `$z` as well.
 
diff --git a/charcode/bin/src/uflags.dart b/charcode/bin/src/uflags.dart
old mode 100755
new mode 100644
index b261cc3..acdac37
--- a/charcode/bin/src/uflags.dart
+++ b/charcode/bin/src/uflags.dart
@@ -36,10 +36,11 @@
 /// It has a default value to use if the parameter is omitted.
 ///
 /// An unrecognized or malformed flag is reported using the [warn]
-/// function.
+/// function. If omitted, the [warn] function defaults to printing
+/// using the [print] function.
 Iterable<CmdLineArg<T>> parseFlags<T>(
     Flags<T> flags, Iterable<String> arguments,
-    [void warn(String warning)?]) sync* {
+    [void Function(String warning)? warn]) sync* {
   warn ??= _printWarning;
   var args = arguments.iterator;
   while (args.moveNext()) {
@@ -356,15 +357,29 @@
   }
 }
 
+/// Converts names to canonical form.
+///
+/// Canonical form consists of only *lower case ASCII letters*,
+/// *decimal digits* and single *dash* characters (`-`) separating letter/digit
+/// sequences.
+///
+/// All upper-case letters are made lower-case.
+/// If the input-name contains sequences of non-letter, non-digit characters,
+/// each sequence is replaced by a single `-`.
+/// Leading and trailing `-`s are then ignored
+/// if the result contains anything other than `-`.
 String? canonicalizeName(String? name) {
   if (name == null) return name;
   const $dash = 0x2d;
   var wasDash = false;
   var i = 0;
+  var upperCase = 0x20;
   while (i < name.length) {
     var char = name.codeUnitAt(i++);
-    if (char ^ 0x30 <= 9 || char >= 0x61 && char <= 0x7b) {
+    var lcChar = char | 0x20;
+    if (char ^ 0x30 <= 9 || lcChar >= 0x61 && lcChar <= 0x7b) {
       wasDash = false;
+      upperCase &= char;
       continue;
     }
     if (char == $dash && !wasDash) {
@@ -375,27 +390,21 @@
     var bytes = Uint8List(name.length);
     var j = 0;
     for (; j < i - 1; j++) {
-      bytes[j] = name.codeUnitAt(j);
+      bytes[j] = name.codeUnitAt(j) | 0x20;
     }
 
     // Convert all letters to lower-case, all non letter/digits to a single `-`.
     outer:
     do {
-      if (char >= 0x41 && char <= 0x5b) {
-        bytes[j++] = char | 0x20;
-        i++;
-        wasDash = false;
-      } else {
-        if (!wasDash) {
-          bytes[j++] = $dash;
-          i++;
-          wasDash = true;
-        }
+      if (!wasDash) {
+        bytes[j++] = $dash;
+        wasDash = true;
       }
       while (i < name.length) {
         char = name.codeUnitAt(i++);
-        if (char ^ 0x30 <= 9 || char >= 0x61 && char <= 0x7b) {
-          bytes[j++] = char;
+        var lcChar = char | 0x20;
+        if (char ^ 0x30 <= 9 || lcChar >= 0x61 && lcChar <= 0x7b) {
+          bytes[j++] = lcChar;
           wasDash = false;
           continue;
         }
@@ -408,9 +417,16 @@
       }
       break;
     } while (true);
-    return String.fromCharCodes(bytes);
+    var start = 0;
+    var end = j;
+    if (end > start + 1) {
+      // Omit leading/trailing dashes.
+      if (bytes[start] == $dash) start++;
+      if (bytes[end - 1] == $dash) end--;
+    }
+    return String.fromCharCodes(Uint8List.sublistView(bytes, start, end));
   }
-  return name;
+  return upperCase == 0 ? name.toLowerCase() : name;
 }
 
 void _printWarning(String message) {
diff --git a/charcode/example/example.dart b/charcode/example/example.dart
old mode 100755
new mode 100644
diff --git a/charcode/lib/ascii.dart b/charcode/lib/ascii.dart
old mode 100755
new mode 100644
index 319ab73..8da5b8b
--- a/charcode/lib/ascii.dart
+++ b/charcode/lib/ascii.dart
@@ -17,12 +17,14 @@
 /// For less common symbols, a selection of common names are used.
 ///
 /// For parenthetical markers, there is both a short name, [$lparen]/[$rparen],
-/// and a long name, [$open_paren]/ [$close_paren].
+/// and a long name, [$openParen]/ [$closeParen].
 ///
 /// For common HTML entities, the entity names are also usable as symbolic
 /// names: [$apos], [$quot], [$lt], [$gt], and [$amp].
 library charcode.ascii.dollar_lowercase;
 
+// ignore_for_file: constant_identifier_names
+
 // Control characters.
 
 /// "Null character" control character.
@@ -132,377 +134,419 @@
 /// Space character.
 const int $space = 0x20;
 
-/// Character '!'.
+/// Character `!`.
 const int $exclamation = 0x21;
 
-/// Character '"', short name.
+/// Character `"', short nam`.
 const int $quot = 0x22;
 
-/// Character '"'.
+/// Character `"`.
 const int $quote = 0x22;
 
-/// Character '"'.
+/// Character `"`.
 const int $double_quote = 0x22;
 
-/// Character '"'.
+/// Character `"`.
+const int $doubleQuote = 0x22;
+
+/// Character `"`.
 const int $quotation = 0x22;
 
-/// Character '#'.
+/// Character `#`.
 const int $hash = 0x23;
 
-/// Character '$'.
+/// Character `$`.
 const int $$ = 0x24;
 
-/// Character '$'.
+/// Character `$`.
 const int $dollar = 0x24;
 
-/// Character '%'.
+/// Character `%`.
 const int $percent = 0x25;
 
-/// Character '&', short name.
+/// Character `&`, short name.
 const int $amp = 0x26;
 
-/// Character '&'.
+/// Character `&`.
 const int $ampersand = 0x26;
 
-/// Character "'".
+/// Character `'`.
 const int $apos = 0x27;
 
-/// Character '''.
+/// Character `'`.
 const int $apostrophe = 0x27;
 
-/// Character '''.
+/// Character `'`.
 const int $single_quote = 0x27;
 
-/// Character '('.
+/// Character `'`.
+const int $singleQuote = 0x27;
+
+/// Character `(`.
 const int $lparen = 0x28;
 
-/// Character '('.
+/// Character `(`.
 const int $open_paren = 0x28;
 
-/// Character '('.
+/// Character `(`.
+const int $openParen = 0x28;
+
+/// Character `(`.
 const int $open_parenthesis = 0x28;
 
-/// Character ')'.
+/// Character `(`.
+const int $openParenthesis = 0x28;
+
+/// Character `)`.
 const int $rparen = 0x29;
 
-/// Character ')'.
+/// Character `)`.
 const int $close_paren = 0x29;
 
-/// Character ')'.
+/// Character `)`.
+const int $closeParen = 0x29;
+
+/// Character `)`.
 const int $close_parenthesis = 0x29;
 
-/// Character '*'.
+/// Character `)`.
+const int $closeParenthesis = 0x29;
+
+/// Character `*`.
 const int $asterisk = 0x2A;
 
-/// Character '+'.
+/// Character `+`.
 const int $plus = 0x2B;
 
-/// Character ','.
+/// Character `,`.
 const int $comma = 0x2C;
 
-/// Character '-'.
+/// Character `-`.
 const int $minus = 0x2D;
 
-/// Character '-'.
+/// Character `-`.
 const int $dash = 0x2D;
 
-/// Character '.'.
+/// Character `.`.
 const int $dot = 0x2E;
 
-/// Character '.'.
+/// Character `.`.
 const int $fullstop = 0x2E;
 
-/// Character '/'.
+/// Character `/`.
 const int $slash = 0x2F;
 
-/// Character '/'.
+/// Character `/`.
 const int $solidus = 0x2F;
 
-/// Character '/'.
+/// Character `/`.
 const int $division = 0x2F;
 
-/// Character '0'.
+/// Character `0`.
 const int $0 = 0x30;
 
-/// Character '1'.
+/// Character `1`.
 const int $1 = 0x31;
 
-/// Character '2'.
+/// Character `2`.
 const int $2 = 0x32;
 
-/// Character '3'.
+/// Character `3`.
 const int $3 = 0x33;
 
-/// Character '4'.
+/// Character `4`.
 const int $4 = 0x34;
 
-/// Character '5'.
+/// Character `5`.
 const int $5 = 0x35;
 
-/// Character '6'.
+/// Character `6`.
 const int $6 = 0x36;
 
-/// Character '7'.
+/// Character `7`.
 const int $7 = 0x37;
 
-/// Character '8'.
+/// Character `8`.
 const int $8 = 0x38;
 
-/// Character '9'.
+/// Character `9`.
 const int $9 = 0x39;
 
-/// Character ':'.
+/// Character `:`.
 const int $colon = 0x3A;
 
-/// Character ';'.
+/// Character `;`.
 const int $semicolon = 0x3B;
 
-/// Character '<'.
+/// Character `<`.
 const int $lt = 0x3C;
 
-/// Character '<'.
+/// Character `<`.
 const int $less_than = 0x3C;
 
-/// Character '<'.
+/// Character `<`.
+const int $lessThan = 0x3C;
+
+/// Character `<`.
 const int $langle = 0x3C;
 
-/// Character '<'.
+/// Character `<`.
 const int $open_angle = 0x3C;
 
-/// Character '='.
+/// Character `<`.
+const int $openAngle = 0x3C;
+
+/// Character `=`.
 const int $equal = 0x3D;
 
-/// Character '>'.
+/// Character `>`.
 const int $gt = 0x3E;
 
-/// Character '>'.
+/// Character `>`.
 const int $greater_than = 0x3E;
 
-/// Character '>'.
+/// Character `>`.
+const int $greaterThan = 0x3E;
+
+/// Character `>`.
 const int $rangle = 0x3E;
 
-/// Character '>'.
+/// Character `>`.
 const int $close_angle = 0x3E;
 
-/// Character '?'.
+/// Character `>`.
+const int $closeAngle = 0x3E;
+
+/// Character `?`.
 const int $question = 0x3F;
 
-/// Character '@'.
+/// Character `@`.
 const int $at = 0x40;
 
-/// Character 'A'.
+/// Character `A`.
 const int $A = 0x41;
 
-/// Character 'B'.
+/// Character `B`.
 const int $B = 0x42;
 
-/// Character 'C'.
+/// Character `C`.
 const int $C = 0x43;
 
-/// Character 'D'.
+/// Character `D`.
 const int $D = 0x44;
 
-/// Character 'E'.
+/// Character `E`.
 const int $E = 0x45;
 
-/// Character 'F'.
+/// Character `F`.
 const int $F = 0x46;
 
-/// Character 'G'.
+/// Character `G`.
 const int $G = 0x47;
 
-/// Character 'H'.
+/// Character `H`.
 const int $H = 0x48;
 
-/// Character 'I'.
+/// Character `I`.
 const int $I = 0x49;
 
-/// Character 'J'.
+/// Character `J`.
 const int $J = 0x4A;
 
-/// Character 'K'.
+/// Character `K`.
 const int $K = 0x4B;
 
-/// Character 'L'.
+/// Character `L`.
 const int $L = 0x4C;
 
-/// Character 'M'.
+/// Character `M`.
 const int $M = 0x4D;
 
-/// Character 'N'.
+/// Character `N`.
 const int $N = 0x4E;
 
-/// Character 'O'.
+/// Character `O`.
 const int $O = 0x4F;
 
-/// Character 'P'.
+/// Character `P`.
 const int $P = 0x50;
 
-/// Character 'Q'.
+/// Character `Q`.
 const int $Q = 0x51;
 
-/// Character 'R'.
+/// Character `R`.
 const int $R = 0x52;
 
-/// Character 'S'.
+/// Character `S`.
 const int $S = 0x53;
 
-/// Character 'T'.
+/// Character `T`.
 const int $T = 0x54;
 
-/// Character 'U'.
+/// Character `U`.
 const int $U = 0x55;
 
-/// Character 'V'.
+/// Character `V`.
 const int $V = 0x56;
 
-/// Character 'W'.
+/// Character `W`.
 const int $W = 0x57;
 
-/// Character 'X'.
+/// Character `X`.
 const int $X = 0x58;
 
-/// Character 'Y'.
+/// Character `Y`.
 const int $Y = 0x59;
 
-/// Character 'Z'.
+/// Character `Z`.
 const int $Z = 0x5A;
 
-/// Character '['.
+/// Character `[`.
 const int $lbracket = 0x5B;
 
-/// Character '['.
+/// Character `[`.
 const int $open_bracket = 0x5B;
 
-/// Character '\'.
+/// Character `[`.
+const int $openBracket = 0x5B;
+
+/// Character `\`.
 const int $backslash = 0x5C;
 
-/// Character ']'.
+/// Character `]`.
 const int $rbracket = 0x5D;
 
-/// Character ']'.
+/// Character `]`.
 const int $close_bracket = 0x5D;
 
-/// Character '^'.
+/// Character `]`.
+const int $closeBracket = 0x5D;
+
+/// Character `^`.
 const int $circumflex = 0x5E;
 
-/// Character '^'.
+/// Character `^`.
 const int $caret = 0x5E;
 
-/// Character '^'.
+/// Character `^`.
 const int $hat = 0x5E;
 
-/// Character '_'.
+/// Character `_`.
 const int $_ = 0x5F;
 
-/// Character '_'.
+/// Character `_`.
 const int $underscore = 0x5F;
 
-/// Character '_'.
+/// Character `_`.
 const int $underline = 0x5F;
 
-/// Character '`'.
+/// Character `` ` ``.
 const int $backquote = 0x60;
 
-/// Character '`'.
+/// Character `` ` ``.
 const int $grave = 0x60;
 
-/// Character 'a'.
+/// Character `a`.
 const int $a = 0x61;
 
-/// Character 'b'.
+/// Character `b`.
 const int $b = 0x62;
 
-/// Character 'c'.
+/// Character `c`.
 const int $c = 0x63;
 
-/// Character 'd'.
+/// Character `d`.
 const int $d = 0x64;
 
-/// Character 'e'.
+/// Character `e`.
 const int $e = 0x65;
 
-/// Character 'f'.
+/// Character `f`.
 const int $f = 0x66;
 
-/// Character 'g'.
+/// Character `g`.
 const int $g = 0x67;
 
-/// Character 'h'.
+/// Character `h`.
 const int $h = 0x68;
 
-/// Character 'i'.
+/// Character `i`.
 const int $i = 0x69;
 
-/// Character 'j'.
+/// Character `j`.
 const int $j = 0x6A;
 
-/// Character 'k'.
+/// Character `k`.
 const int $k = 0x6B;
 
-/// Character 'l'.
+/// Character `l`.
 const int $l = 0x6C;
 
-/// Character 'm'.
+/// Character `m`.
 const int $m = 0x6D;
 
-/// Character 'n'.
+/// Character `n`.
 const int $n = 0x6E;
 
-/// Character 'o'.
+/// Character `o`.
 const int $o = 0x6F;
 
-/// Character 'p'.
+/// Character `p`.
 const int $p = 0x70;
 
-/// Character 'q'.
+/// Character `q`.
 const int $q = 0x71;
 
-/// Character 'r'.
+/// Character `r`.
 const int $r = 0x72;
 
-/// Character 's'.
+/// Character `s`.
 const int $s = 0x73;
 
-/// Character 't'.
+/// Character `t`.
 const int $t = 0x74;
 
-/// Character 'u'.
+/// Character `u`.
 const int $u = 0x75;
 
-/// Character 'v'.
+/// Character `v`.
 const int $v = 0x76;
 
-/// Character 'w'.
+/// Character `w`.
 const int $w = 0x77;
 
-/// Character 'x'.
+/// Character `x`.
 const int $x = 0x78;
 
-/// Character 'y'.
+/// Character `y`.
 const int $y = 0x79;
 
-/// Character 'z'.
+/// Character `z`.
 const int $z = 0x7A;
 
-/// Character '{'.
+/// Character `{`.
 const int $lbrace = 0x7B;
 
-/// Character '{'.
+/// Character `{`.
 const int $open_brace = 0x7B;
 
-/// Character '|'.
+/// Character `{`.
+const int $openBrace = 0x7B;
+
+/// Character `|`.
 const int $pipe = 0x7C;
 
-/// Character '|'.
+/// Character `|`.
 const int $bar = 0x7C;
 
-/// Character '}'.
+/// Character `}`.
 const int $rbrace = 0x7D;
 
-/// Character '}'.
+/// Character `}`.
 const int $close_brace = 0x7D;
 
-/// Character '~'.
+/// Character `}`.
+const int $closeBrace = 0x7D;
+
+/// Character `~`.
 const int $tilde = 0x7E;
diff --git a/charcode/lib/charcode.dart b/charcode/lib/charcode.dart
old mode 100755
new mode 100644
diff --git a/charcode/lib/html_entity.dart b/charcode/lib/html_entity.dart
old mode 100755
new mode 100644
diff --git a/charcode/pubspec.yaml b/charcode/pubspec.yaml
old mode 100755
new mode 100644
index 96063f6..294af0b
--- a/charcode/pubspec.yaml
+++ b/charcode/pubspec.yaml
@@ -1,12 +1,14 @@
 name: charcode
-version: 1.2.0
+version: 1.3.1
 repository: https://github.com/lrhn/charcode
 description: >-
   Constants for ASCII and common non-ASCII character codes.
+  Integer constants corresponding to the code points of
+  individual characters.
 
 environment:
-  sdk: ">=2.12.0-0 <3.0.0"
+  sdk: ">=2.12.0 <3.0.0"
 
 dev_dependencies:
-  test: ^1.16.0-nullsafety
-  pedantic: ^1.10.0-nullsafety
+  test: ^1.16.0
+  lints: ^1.0.0
diff --git a/checked_yaml/BUILD.gn b/checked_yaml/BUILD.gn
index 688b6a7..6107211 100644
--- a/checked_yaml/BUILD.gn
+++ b/checked_yaml/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for checked_yaml-1.0.4
+# This file is generated by package_importer.py for checked_yaml-1.0.4
 
 import("//build/dart/dart_library.gni")
 
diff --git a/cli_util/BUILD.gn b/cli_util/BUILD.gn
index c577416..7af0847 100644
--- a/cli_util/BUILD.gn
+++ b/cli_util/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for cli_util-0.3.0
+# This file is generated by package_importer.py for cli_util-0.3.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/clock/BUILD.gn b/clock/BUILD.gn
index 7aed8b9..acc6aca 100644
--- a/clock/BUILD.gn
+++ b/clock/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for clock-1.1.0
+# This file is generated by package_importer.py for clock-1.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/code_builder/.github/workflows/test-package.yml b/code_builder/.github/workflows/test-package.yml
index 28a5086..2db209d 100644
--- a/code_builder/.github/workflows/test-package.yml
+++ b/code_builder/.github/workflows/test-package.yml
@@ -14,7 +14,7 @@
 
 jobs:
   # Check code formatting and static analysis on a single OS (linux)
-  # against Dart dev and 2.7.0.
+  # against Dart dev and 2.12.0.
   analyze:
     runs-on: ubuntu-latest
     strategy:
@@ -24,7 +24,7 @@
         version: [latest]
         include:
           - sdk: stable
-            version: 2.7.0
+            version: 2.12.0
     steps:
       - uses: actions/checkout@v2
       - uses: cedx/setup-dart@v2 # TODO(dart-lang/setup-dart#3): use the official setup-dart action
@@ -43,7 +43,7 @@
 
   # Run tests on a matrix consisting of two dimensions:
   # 1. OS: ubuntu-latest, (macos-latest, windows-latest)
-  # 2. release channel: dev, 2.7.0
+  # 2. release channel: dev, 2.12.0
   test:
     needs: analyze
     runs-on: ${{ matrix.os }}
@@ -57,7 +57,7 @@
         include:
           - os: ubuntu-latest
             sdk: stable
-            version: 2.7.0
+            version: 2.12.0
     steps:
       - uses: actions/checkout@v2
       - uses: cedx/setup-dart@v2 # TODO(dart-lang/setup-dart#3): use the official setup-dart action
diff --git a/code_builder/BUILD.gn b/code_builder/BUILD.gn
index cf220c9..25fc179 100644
--- a/code_builder/BUILD.gn
+++ b/code_builder/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for code_builder-3.7.0
+# This file is generated by package_importer.py for code_builder-4.0.0
 
 import("//build/dart/dart_library.gni")
 
 dart_library("code_builder") {
   package_name = "code_builder"
 
-  language_version = "2.7"
+  language_version = "2.12"
 
   disable_analysis = true
 
@@ -42,6 +42,7 @@
     "src/specs/expression/code.dart",
     "src/specs/expression/invoke.dart",
     "src/specs/expression/literal.dart",
+    "src/specs/expression/parenthesized.dart",
     "src/specs/extension.dart",
     "src/specs/extension.g.dart",
     "src/specs/field.dart",
diff --git a/code_builder/CHANGELOG.md b/code_builder/CHANGELOG.md
index d23919e..92584a7 100644
--- a/code_builder/CHANGELOG.md
+++ b/code_builder/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 4.0.0
+
+* Migrate to null safety.
+* Changed the DartEmittor constructor to use named optional parameters.
+* Add `ParenthesizedExpression` and
+  `ExpressionVisitor.visitParenthesizedExpression.`
+
 ## 3.7.0
 
 * Add support for converting a Method to a generic closure, with
diff --git a/code_builder/LICENSE b/code_builder/LICENSE
index 82e9b52..2372431 100644
--- a/code_builder/LICENSE
+++ b/code_builder/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2016, the Dart project authors. All rights reserved.
+Copyright 2016, the Dart project authors. 
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/code_builder/lib/src/allocator.dart b/code_builder/lib/src/allocator.dart
index 1880946..b8b36f3 100644
--- a/code_builder/lib/src/allocator.dart
+++ b/code_builder/lib/src/allocator.dart
@@ -50,10 +50,11 @@
 
   @override
   String allocate(Reference reference) {
-    if (reference.url != null) {
-      _imports.add(reference.url);
+    final url = reference.url;
+    if (url != null) {
+      _imports.add(url);
     }
-    return reference.symbol;
+    return reference.symbol!;
   }
 
   @override
@@ -64,7 +65,7 @@
   const _NullAllocator();
 
   @override
-  String allocate(Reference reference) => reference.symbol;
+  String allocate(Reference reference) => reference.symbol!;
 
   @override
   Iterable<Directive> get imports => const [];
@@ -79,10 +80,11 @@
   @override
   String allocate(Reference reference) {
     final symbol = reference.symbol;
-    if (reference.url == null || _doNotPrefix.contains(reference.url)) {
-      return symbol;
+    final url = reference.url;
+    if (url == null || _doNotPrefix.contains(url)) {
+      return symbol!;
     }
-    return '_i${_imports.putIfAbsent(reference.url, _nextKey)}.$symbol';
+    return '_i${_imports.putIfAbsent(url, _nextKey)}.$symbol';
   }
 
   int _nextKey() => _keys++;
diff --git a/code_builder/lib/src/base.dart b/code_builder/lib/src/base.dart
index d2a5eb1..216b9a9 100644
--- a/code_builder/lib/src/base.dart
+++ b/code_builder/lib/src/base.dart
@@ -5,7 +5,7 @@
 import 'visitors.dart';
 
 abstract class Spec {
-  R accept<R>(SpecVisitor<R> visitor, [R context]);
+  R accept<R>(SpecVisitor<R> visitor, [R? context]);
 }
 
 /// Returns a generic [Spec] that is lazily generated when visited.
@@ -17,6 +17,6 @@
   const _LazySpec(this.generate);
 
   @override
-  R accept<R>(SpecVisitor<R> visitor, [R context]) =>
+  R accept<R>(SpecVisitor<R> visitor, [R? context]) =>
       generate().accept(visitor, context);
 }
diff --git a/code_builder/lib/src/emitter.dart b/code_builder/lib/src/emitter.dart
index 14093cb..a359144 100644
--- a/code_builder/lib/src/emitter.dart
+++ b/code_builder/lib/src/emitter.dart
@@ -71,19 +71,20 @@
   ///
   /// May specify an [Allocator] to use for symbols, otherwise uses a no-op.
   DartEmitter(
-      [this.allocator = Allocator.none,
-      bool orderDirectives = false,
-      bool useNullSafetySyntax = false])
-      : orderDirectives = orderDirectives ?? false,
-        _useNullSafetySyntax = useNullSafetySyntax ?? false;
+      {this.allocator = Allocator.none,
+      this.orderDirectives = false,
+      bool useNullSafetySyntax = false})
+      : _useNullSafetySyntax = useNullSafetySyntax;
 
   /// Creates a new instance of [DartEmitter] with simple automatic imports.
   factory DartEmitter.scoped(
           {bool orderDirectives = false, bool useNullSafetySyntax = false}) =>
       DartEmitter(
-          Allocator.simplePrefixing(), orderDirectives, useNullSafetySyntax);
+          allocator: Allocator.simplePrefixing(),
+          orderDirectives: orderDirectives,
+          useNullSafetySyntax: useNullSafetySyntax);
 
-  static bool _isLambdaBody(Code code) =>
+  static bool _isLambdaBody(Code? code) =>
       code is ToCodeExpression && !code.isStatement;
 
   /// Whether the provided [method] is considered a lambda method.
@@ -96,7 +97,7 @@
       constructor.factory && _isLambdaBody(constructor.body);
 
   @override
-  StringSink visitAnnotation(Expression spec, [StringSink output]) {
+  StringSink visitAnnotation(Expression spec, [StringSink? output]) {
     (output ??= StringBuffer()).write('@');
     spec.accept(this, output);
     output.write(' ');
@@ -104,54 +105,54 @@
   }
 
   @override
-  StringSink visitClass(Class spec, [StringSink output]) {
-    output ??= StringBuffer();
-    spec.docs.forEach(output.writeln);
-    spec.annotations.forEach((a) => visitAnnotation(a, output));
+  StringSink visitClass(Class spec, [StringSink? output]) {
+    final out = output ??= StringBuffer();
+    spec.docs.forEach(out.writeln);
+    spec.annotations.forEach((a) => visitAnnotation(a, out));
     if (spec.abstract) {
-      output.write('abstract ');
+      out.write('abstract ');
     }
-    output.write('class ${spec.name}');
-    visitTypeParameters(spec.types.map((r) => r.type), output);
+    out.write('class ${spec.name}');
+    visitTypeParameters(spec.types.map((r) => r.type), out);
     if (spec.extend != null) {
-      output.write(' extends ');
-      spec.extend.type.accept(this, output);
+      out.write(' extends ');
+      spec.extend!.type.accept(this, out);
     }
     if (spec.mixins.isNotEmpty) {
-      output
+      out
         ..write(' with ')
         ..writeAll(
             spec.mixins.map<StringSink>((m) => m.type.accept(this)), ',');
     }
     if (spec.implements.isNotEmpty) {
-      output
+      out
         ..write(' implements ')
         ..writeAll(
             spec.implements.map<StringSink>((m) => m.type.accept(this)), ',');
     }
-    output.write(' {');
+    out.write(' {');
     spec.constructors.forEach((c) {
-      visitConstructor(c, spec.name, output);
-      output.writeln();
+      visitConstructor(c, spec.name, out);
+      out.writeln();
     });
     spec.fields.forEach((f) {
-      visitField(f, output);
-      output.writeln();
+      visitField(f, out);
+      out.writeln();
     });
     spec.methods.forEach((m) {
-      visitMethod(m, output);
+      visitMethod(m, out);
       if (_isLambdaMethod(m)) {
-        output.write(';');
+        out.write(';');
       }
-      output.writeln();
+      out.writeln();
     });
-    output.writeln(' }');
-    return output;
+    out.writeln(' }');
+    return out;
   }
 
   @override
   StringSink visitConstructor(Constructor spec, String clazz,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     spec.docs.forEach(output.writeln);
     spec.annotations.forEach((a) => visitAnnotation(a, output));
@@ -215,16 +216,16 @@
     }
     if (spec.redirect != null) {
       output.write(' = ');
-      spec.redirect.type.accept(this, output);
+      spec.redirect!.type.accept(this, output);
       output.write(';');
     } else if (spec.body != null) {
       if (_isLambdaConstructor(spec)) {
         output.write(' => ');
-        spec.body.accept(this, output);
+        spec.body!.accept(this, output);
         output.write(';');
       } else {
         output.write(' { ');
-        spec.body.accept(this, output);
+        spec.body!.accept(this, output);
         output.write(' }');
       }
     } else {
@@ -235,38 +236,38 @@
   }
 
   @override
-  StringSink visitExtension(Extension spec, [StringSink output]) {
-    output ??= StringBuffer();
-    spec.docs.forEach(output.writeln);
-    spec.annotations.forEach((a) => visitAnnotation(a, output));
+  StringSink visitExtension(Extension spec, [StringSink? output]) {
+    final out = output ??= StringBuffer();
+    spec.docs.forEach(out.writeln);
+    spec.annotations.forEach((a) => visitAnnotation(a, out));
 
-    output.write('extension');
+    out.write('extension');
     if (spec.name != null) {
-      output.write(' ${spec.name}');
+      out.write(' ${spec.name}');
     }
-    visitTypeParameters(spec.types.map((r) => r.type), output);
+    visitTypeParameters(spec.types.map((r) => r.type), out);
     if (spec.on != null) {
-      output.write(' on ');
-      spec.on.type.accept(this, output);
+      out.write(' on ');
+      spec.on!.type.accept(this, out);
     }
-    output.write(' {');
+    out.write(' {');
     spec.fields.forEach((f) {
-      visitField(f, output);
-      output.writeln();
+      visitField(f, out);
+      out.writeln();
     });
     spec.methods.forEach((m) {
-      visitMethod(m, output);
+      visitMethod(m, out);
       if (_isLambdaMethod(m)) {
-        output.write(';');
+        out.write(';');
       }
-      output.writeln();
+      out.writeln();
     });
-    output.writeln(' }');
-    return output;
+    out.writeln(' }');
+    return out;
   }
 
   @override
-  StringSink visitDirective(Directive spec, [StringSink output]) {
+  StringSink visitDirective(Directive spec, [StringSink? output]) {
     output ??= StringBuffer();
     switch (spec.type) {
       case DirectiveType.import:
@@ -303,7 +304,7 @@
   }
 
   @override
-  StringSink visitField(Field spec, [StringSink output]) {
+  StringSink visitField(Field spec, [StringSink? output]) {
     output ??= StringBuffer();
     spec.docs.forEach(output.writeln);
     spec.annotations.forEach((a) => visitAnnotation(a, output));
@@ -324,14 +325,14 @@
         break;
     }
     if (spec.type != null) {
-      spec.type.type.accept(this, output);
+      spec.type!.type.accept(this, output);
       output.write(' ');
     }
     output.write(spec.name);
     if (spec.assignment != null) {
       output.write(' = ');
       startConstCode(spec.modifier == FieldModifier.constant, () {
-        spec.assignment.accept(this, output);
+        spec.assignment!.accept(this, output);
       });
     }
     output.writeln(';');
@@ -339,7 +340,7 @@
   }
 
   @override
-  StringSink visitLibrary(Library spec, [StringSink output]) {
+  StringSink visitLibrary(Library spec, [StringSink? output]) {
     output ??= StringBuffer();
     // Process the body first in order to prime the allocators.
     final body = StringBuffer();
@@ -356,7 +357,7 @@
       directives.sort();
     }
 
-    Directive previous;
+    Directive? previous;
     for (final directive in directives) {
       if (_newLineBetween(orderDirectives, previous, directive)) {
         // Note: dartfmt handles creating new lines between directives.
@@ -372,52 +373,52 @@
   }
 
   @override
-  StringSink visitFunctionType(FunctionType spec, [StringSink output]) {
-    output ??= StringBuffer();
+  StringSink visitFunctionType(FunctionType spec, [StringSink? output]) {
+    final out = output ??= StringBuffer();
     if (spec.returnType != null) {
-      spec.returnType.accept(this, output);
-      output.write(' ');
+      spec.returnType!.accept(this, out);
+      out.write(' ');
     }
-    output.write('Function');
+    out.write('Function');
     if (spec.types.isNotEmpty) {
-      output.write('<');
-      visitAll<Reference>(spec.types, output, (spec) {
-        spec.accept(this, output);
+      out.write('<');
+      visitAll<Reference>(spec.types, out, (spec) {
+        spec.accept(this, out);
       });
-      output.write('>');
+      out.write('>');
     }
-    output.write('(');
-    visitAll<Reference>(spec.requiredParameters, output, (spec) {
-      spec.accept(this, output);
+    out.write('(');
+    visitAll<Reference>(spec.requiredParameters, out, (spec) {
+      spec.accept(this, out);
     });
     if (spec.requiredParameters.isNotEmpty &&
         (spec.optionalParameters.isNotEmpty ||
             spec.namedParameters.isNotEmpty)) {
-      output.write(', ');
+      out.write(', ');
     }
     if (spec.optionalParameters.isNotEmpty) {
-      output.write('[');
-      visitAll<Reference>(spec.optionalParameters, output, (spec) {
-        spec.accept(this, output);
+      out.write('[');
+      visitAll<Reference>(spec.optionalParameters, out, (spec) {
+        spec.accept(this, out);
       });
-      output.write(']');
+      out.write(']');
     } else if (spec.namedParameters.isNotEmpty) {
-      output.write('{');
-      visitAll<String>(spec.namedParameters.keys, output, (name) {
-        spec.namedParameters[name].accept(this, output);
-        output..write(' ')..write(name);
+      out.write('{');
+      visitAll<String>(spec.namedParameters.keys, out, (name) {
+        spec.namedParameters[name]!.accept(this, out);
+        out..write(' ')..write(name);
       });
-      output.write('}');
+      out.write('}');
     }
-    output.write(')');
+    out.write(')');
     if (_useNullSafetySyntax && (spec.isNullable ?? false)) {
-      output.write('?');
+      out.write('?');
     }
-    return output;
+    return out;
   }
 
   @override
-  StringSink visitMethod(Method spec, [StringSink output]) {
+  StringSink visitMethod(Method spec, [StringSink? output]) {
     output ??= StringBuffer();
     spec.docs.forEach(output.writeln);
     spec.annotations.forEach((a) => visitAnnotation(a, output));
@@ -428,7 +429,7 @@
       output.write('static ');
     }
     if (spec.returns != null) {
-      spec.returns.accept(this, output);
+      spec.returns!.accept(this, output);
       output.write(' ');
     }
     if (spec.type == MethodType.getter) {
@@ -478,7 +479,7 @@
     }
     if (spec.body != null) {
       if (spec.modifier != null) {
-        switch (spec.modifier) {
+        switch (spec.modifier!) {
           case MethodModifier.async:
             output.write(' async ');
             break;
@@ -495,7 +496,7 @@
       } else {
         output.write(' { ');
       }
-      spec.body.accept(this, output);
+      spec.body!.accept(this, output);
       if (!_isLambdaMethod(spec)) {
         output.write(' } ');
       }
@@ -522,7 +523,7 @@
       output.write('covariant ');
     }
     if (spec.type != null) {
-      spec.type.type.accept(this, output);
+      spec.type!.type.accept(this, output);
       output.write(' ');
     }
     if (spec.toThis) {
@@ -531,26 +532,26 @@
     output.write(spec.name);
     if (optional && spec.defaultTo != null) {
       output.write(' = ');
-      spec.defaultTo.accept(this, output);
+      spec.defaultTo!.accept(this, output);
     }
   }
 
   @override
-  StringSink visitReference(Reference spec, [StringSink output]) =>
+  StringSink visitReference(Reference spec, [StringSink? output]) =>
       (output ??= StringBuffer())..write(allocator.allocate(spec));
 
   @override
-  StringSink visitSpec(Spec spec, [StringSink output]) =>
+  StringSink visitSpec(Spec spec, [StringSink? output]) =>
       spec.accept(this, output);
 
   @override
-  StringSink visitType(TypeReference spec, [StringSink output]) {
+  StringSink visitType(TypeReference spec, [StringSink? output]) {
     output ??= StringBuffer();
     // Intentionally not .accept to avoid stack overflow.
     visitReference(spec, output);
     if (spec.bound != null) {
       output.write(' extends ');
-      spec.bound.type.accept(this, output);
+      spec.bound!.type.accept(this, output);
     }
     visitTypeParameters(spec.types.map((r) => r.type), output);
     if (_useNullSafetySyntax && (spec.isNullable ?? false)) {
@@ -561,7 +562,7 @@
 
   @override
   StringSink visitTypeParameters(Iterable<Reference> specs,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     if (specs.isNotEmpty) {
       output
@@ -573,21 +574,21 @@
   }
 
   @override
-  StringSink visitEnum(Enum spec, [StringSink output]) {
-    output ??= StringBuffer();
-    spec.docs.forEach(output.writeln);
-    spec.annotations.forEach((a) => visitAnnotation(a, output));
-    output.writeln('enum ${spec.name} {');
+  StringSink visitEnum(Enum spec, [StringSink? output]) {
+    final out = output ??= StringBuffer();
+    spec.docs.forEach(out.writeln);
+    spec.annotations.forEach((a) => visitAnnotation(a, out));
+    out.writeln('enum ${spec.name} {');
     spec.values.forEach((v) {
-      v.docs.forEach(output.writeln);
-      v.annotations.forEach((a) => visitAnnotation(a, output));
-      output.write(v.name);
+      v.docs.forEach(out.writeln);
+      v.annotations.forEach((a) => visitAnnotation(a, out));
+      out.write(v.name);
       if (v != spec.values.last) {
-        output.writeln(',');
+        out.writeln(',');
       }
     });
-    output.writeln('}');
-    return output;
+    out.writeln('}');
+    return out;
   }
 }
 
@@ -596,14 +597,14 @@
 /// * [ordered] is `true`
 /// * [a] is non-`null`
 /// * If there should be an empty line before [b] if it's emitted after [a].
-bool _newLineBetween(bool ordered, Directive a, Directive b) {
+bool _newLineBetween(bool ordered, Directive? a, Directive? b) {
   if (!ordered) return false;
   if (a == null) return false;
 
   assert(b != null);
 
   // Put a line between imports and exports
-  if (a.type != b.type) return true;
+  if (a.type != b!.type) return true;
 
   // Within exports, don't put in extra blank lines
   if (a.type == DirectiveType.export) {
diff --git a/code_builder/lib/src/matchers.dart b/code_builder/lib/src/matchers.dart
index a443ce9..11d3d6d 100644
--- a/code_builder/lib/src/matchers.dart
+++ b/code_builder/lib/src/matchers.dart
@@ -18,7 +18,7 @@
 /// be overridden with [emitter].
 Matcher equalsDart(
   String source, [
-  DartEmitter emitter,
+  DartEmitter? emitter,
 ]) =>
     EqualsDart._(EqualsDart._format(source), emitter ?? DartEmitter());
 
diff --git a/code_builder/lib/src/mixins/annotations.dart b/code_builder/lib/src/mixins/annotations.dart
index f8bc948..b4b3be2 100644
--- a/code_builder/lib/src/mixins/annotations.dart
+++ b/code_builder/lib/src/mixins/annotations.dart
@@ -15,5 +15,5 @@
 /// Compliment to the [HasAnnotations] mixin for metadata [annotations].
 abstract class HasAnnotationsBuilder {
   /// Annotations as metadata on the node.
-  ListBuilder<Expression> annotations;
+  abstract ListBuilder<Expression> annotations;
 }
diff --git a/code_builder/lib/src/mixins/dartdoc.dart b/code_builder/lib/src/mixins/dartdoc.dart
index ae44bd1..52d69c0 100644
--- a/code_builder/lib/src/mixins/dartdoc.dart
+++ b/code_builder/lib/src/mixins/dartdoc.dart
@@ -11,5 +11,5 @@
 
 abstract class HasDartDocsBuilder {
   /// Dart docs.
-  ListBuilder<String> docs;
+  abstract ListBuilder<String> docs;
 }
diff --git a/code_builder/lib/src/mixins/generics.dart b/code_builder/lib/src/mixins/generics.dart
index 04a049e..51aae63 100644
--- a/code_builder/lib/src/mixins/generics.dart
+++ b/code_builder/lib/src/mixins/generics.dart
@@ -13,5 +13,5 @@
 
 abstract class HasGenericsBuilder {
   /// Generic type parameters.
-  ListBuilder<Reference> types;
+  abstract ListBuilder<Reference> types;
 }
diff --git a/code_builder/lib/src/specs/class.dart b/code_builder/lib/src/specs/class.dart
index 77175f2..13e586f 100644
--- a/code_builder/lib/src/specs/class.dart
+++ b/code_builder/lib/src/specs/class.dart
@@ -36,8 +36,7 @@
   @override
   BuiltList<String> get docs;
 
-  @nullable
-  Reference get extend;
+  Reference? get extend;
 
   BuiltList<Reference> get implements;
 
@@ -56,7 +55,7 @@
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitClass(this, context);
 }
@@ -68,6 +67,11 @@
 
   ClassBuilder._();
 
+  @override
+  void update(void Function(ClassBuilder)? updates) {
+    updates?.call(this);
+  }
+
   /// Whether the class is `abstract`.
   bool abstract = false;
 
@@ -77,7 +81,7 @@
   @override
   ListBuilder<String> docs = ListBuilder<String>();
 
-  Reference extend;
+  Reference? extend;
 
   ListBuilder<Reference> implements = ListBuilder<Reference>();
   ListBuilder<Reference> mixins = ListBuilder<Reference>();
@@ -90,5 +94,5 @@
   ListBuilder<Field> fields = ListBuilder<Field>();
 
   /// Name of the class.
-  String name;
+  String? name;
 }
diff --git a/code_builder/lib/src/specs/class.g.dart b/code_builder/lib/src/specs/class.g.dart
index 916b96b..f007824 100644
--- a/code_builder/lib/src/specs/class.g.dart
+++ b/code_builder/lib/src/specs/class.g.dart
@@ -14,7 +14,7 @@
   @override
   final BuiltList<String> docs;
   @override
-  final Reference extend;
+  final Reference? extend;
   @override
   final BuiltList<Reference> implements;
   @override
@@ -30,52 +30,33 @@
   @override
   final String name;
 
-  factory _$Class([void Function(ClassBuilder) updates]) =>
+  factory _$Class([void Function(ClassBuilder)? updates]) =>
       (new ClassBuilder()..update(updates)).build() as _$Class;
 
   _$Class._(
-      {this.abstract,
-      this.annotations,
-      this.docs,
+      {required this.abstract,
+      required this.annotations,
+      required this.docs,
       this.extend,
-      this.implements,
-      this.mixins,
-      this.types,
-      this.constructors,
-      this.methods,
-      this.fields,
-      this.name})
+      required this.implements,
+      required this.mixins,
+      required this.types,
+      required this.constructors,
+      required this.methods,
+      required this.fields,
+      required this.name})
       : super._() {
-    if (abstract == null) {
-      throw new BuiltValueNullFieldError('Class', 'abstract');
-    }
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Class', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Class', 'docs');
-    }
-    if (implements == null) {
-      throw new BuiltValueNullFieldError('Class', 'implements');
-    }
-    if (mixins == null) {
-      throw new BuiltValueNullFieldError('Class', 'mixins');
-    }
-    if (types == null) {
-      throw new BuiltValueNullFieldError('Class', 'types');
-    }
-    if (constructors == null) {
-      throw new BuiltValueNullFieldError('Class', 'constructors');
-    }
-    if (methods == null) {
-      throw new BuiltValueNullFieldError('Class', 'methods');
-    }
-    if (fields == null) {
-      throw new BuiltValueNullFieldError('Class', 'fields');
-    }
-    if (name == null) {
-      throw new BuiltValueNullFieldError('Class', 'name');
-    }
+    BuiltValueNullFieldError.checkNotNull(abstract, 'Class', 'abstract');
+    BuiltValueNullFieldError.checkNotNull(annotations, 'Class', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Class', 'docs');
+    BuiltValueNullFieldError.checkNotNull(implements, 'Class', 'implements');
+    BuiltValueNullFieldError.checkNotNull(mixins, 'Class', 'mixins');
+    BuiltValueNullFieldError.checkNotNull(types, 'Class', 'types');
+    BuiltValueNullFieldError.checkNotNull(
+        constructors, 'Class', 'constructors');
+    BuiltValueNullFieldError.checkNotNull(methods, 'Class', 'methods');
+    BuiltValueNullFieldError.checkNotNull(fields, 'Class', 'fields');
+    BuiltValueNullFieldError.checkNotNull(name, 'Class', 'name');
   }
 
   @override
@@ -145,7 +126,7 @@
 }
 
 class _$ClassBuilder extends ClassBuilder {
-  _$Class _$v;
+  _$Class? _$v;
 
   @override
   bool get abstract {
@@ -162,7 +143,7 @@
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -174,7 +155,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -184,13 +165,13 @@
   }
 
   @override
-  Reference get extend {
+  Reference? get extend {
     _$this;
     return super.extend;
   }
 
   @override
-  set extend(Reference extend) {
+  set extend(Reference? extend) {
     _$this;
     super.extend = extend;
   }
@@ -198,7 +179,7 @@
   @override
   ListBuilder<Reference> get implements {
     _$this;
-    return super.implements ??= new ListBuilder<Reference>();
+    return super.implements;
   }
 
   @override
@@ -210,7 +191,7 @@
   @override
   ListBuilder<Reference> get mixins {
     _$this;
-    return super.mixins ??= new ListBuilder<Reference>();
+    return super.mixins;
   }
 
   @override
@@ -222,7 +203,7 @@
   @override
   ListBuilder<Reference> get types {
     _$this;
-    return super.types ??= new ListBuilder<Reference>();
+    return super.types;
   }
 
   @override
@@ -234,7 +215,7 @@
   @override
   ListBuilder<Constructor> get constructors {
     _$this;
-    return super.constructors ??= new ListBuilder<Constructor>();
+    return super.constructors;
   }
 
   @override
@@ -246,7 +227,7 @@
   @override
   ListBuilder<Method> get methods {
     _$this;
-    return super.methods ??= new ListBuilder<Method>();
+    return super.methods;
   }
 
   @override
@@ -258,7 +239,7 @@
   @override
   ListBuilder<Field> get fields {
     _$this;
-    return super.fields ??= new ListBuilder<Field>();
+    return super.fields;
   }
 
   @override
@@ -268,13 +249,13 @@
   }
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
@@ -282,18 +263,19 @@
   _$ClassBuilder() : super._();
 
   ClassBuilder get _$this {
-    if (_$v != null) {
-      super.abstract = _$v.abstract;
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
-      super.extend = _$v.extend;
-      super.implements = _$v.implements?.toBuilder();
-      super.mixins = _$v.mixins?.toBuilder();
-      super.types = _$v.types?.toBuilder();
-      super.constructors = _$v.constructors?.toBuilder();
-      super.methods = _$v.methods?.toBuilder();
-      super.fields = _$v.fields?.toBuilder();
-      super.name = _$v.name;
+    final $v = _$v;
+    if ($v != null) {
+      super.abstract = $v.abstract;
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
+      super.extend = $v.extend;
+      super.implements = $v.implements.toBuilder();
+      super.mixins = $v.mixins.toBuilder();
+      super.types = $v.types.toBuilder();
+      super.constructors = $v.constructors.toBuilder();
+      super.methods = $v.methods.toBuilder();
+      super.fields = $v.fields.toBuilder();
+      super.name = $v.name;
       _$v = null;
     }
     return this;
@@ -301,14 +283,12 @@
 
   @override
   void replace(Class other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Class;
   }
 
   @override
-  void update(void Function(ClassBuilder) updates) {
+  void update(void Function(ClassBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -318,7 +298,8 @@
     try {
       _$result = _$v ??
           new _$Class._(
-              abstract: abstract,
+              abstract: BuiltValueNullFieldError.checkNotNull(
+                  abstract, 'Class', 'abstract'),
               annotations: annotations.build(),
               docs: docs.build(),
               extend: extend,
@@ -328,9 +309,10 @@
               constructors: constructors.build(),
               methods: methods.build(),
               fields: fields.build(),
-              name: name);
+              name:
+                  BuiltValueNullFieldError.checkNotNull(name, 'Class', 'name'));
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
diff --git a/code_builder/lib/src/specs/code.dart b/code_builder/lib/src/specs/code.dart
index b96d502..5cc52f5 100644
--- a/code_builder/lib/src/specs/code.dart
+++ b/code_builder/lib/src/specs/code.dart
@@ -41,7 +41,7 @@
   ) = ScopedCode._;
 
   @override
-  R accept<R>(covariant CodeVisitor<R> visitor, [R context]);
+  R accept<R>(covariant CodeVisitor<R> visitor, [R? context]);
 }
 
 /// Represents blocks of statements of Dart code.
@@ -54,7 +54,7 @@
   Block._();
 
   @override
-  R accept<R>(covariant CodeVisitor<R> visitor, [R context]) =>
+  R accept<R>(covariant CodeVisitor<R> visitor, [R? context]) =>
       visitor.visitBlock(this, context);
 
   BuiltList<Code> get statements;
@@ -79,11 +79,11 @@
 ///
 /// **INTERNAL ONLY**.
 abstract class CodeVisitor<T> implements SpecVisitor<T> {
-  T visitBlock(Block code, [T context]);
+  T visitBlock(Block code, [T? context]);
 
-  T visitStaticCode(StaticCode code, [T context]);
+  T visitStaticCode(StaticCode code, [T? context]);
 
-  T visitScopedCode(ScopedCode code, [T context]);
+  T visitScopedCode(ScopedCode code, [T? context]);
 }
 
 /// Knowledge of how to write valid Dart code from [CodeVisitor].
@@ -92,7 +92,7 @@
   Allocator get allocator;
 
   @override
-  StringSink visitBlock(Block block, [StringSink output]) {
+  StringSink visitBlock(Block block, [StringSink? output]) {
     output ??= StringBuffer();
     return visitAll<Code>(block.statements, output, (statement) {
       statement.accept(this, output);
@@ -100,13 +100,13 @@
   }
 
   @override
-  StringSink visitStaticCode(StaticCode code, [StringSink output]) {
+  StringSink visitStaticCode(StaticCode code, [StringSink? output]) {
     output ??= StringBuffer();
     return output..write(code.code);
   }
 
   @override
-  StringSink visitScopedCode(ScopedCode code, [StringSink output]) {
+  StringSink visitScopedCode(ScopedCode code, [StringSink? output]) {
     output ??= StringBuffer();
     return output..write(code.code(allocator.allocate));
   }
@@ -119,7 +119,7 @@
   const LazyCode._(this.generate);
 
   @override
-  R accept<R>(CodeVisitor<R> visitor, [R context]) =>
+  R accept<R>(CodeVisitor<R> visitor, [R? context]) =>
       generate(visitor).accept(visitor, context);
 }
 
@@ -132,7 +132,7 @@
   const _LazyCode(this.generate);
 
   @override
-  R accept<R>(CodeVisitor<R> visitor, [R context]) =>
+  R accept<R>(CodeVisitor<R> visitor, [R? context]) =>
       generate().accept(visitor, context);
 }
 
@@ -143,7 +143,7 @@
   const StaticCode._(this.code);
 
   @override
-  R accept<R>(CodeVisitor<R> visitor, [R context]) =>
+  R accept<R>(CodeVisitor<R> visitor, [R? context]) =>
       visitor.visitStaticCode(this, context);
 
   @override
@@ -157,9 +157,9 @@
   const ScopedCode._(this.code);
 
   @override
-  R accept<R>(CodeVisitor<R> visitor, [R context]) =>
+  R accept<R>(CodeVisitor<R> visitor, [R? context]) =>
       visitor.visitScopedCode(this, context);
 
   @override
-  String toString() => code((ref) => ref.symbol);
+  String toString() => code((ref) => ref.symbol!);
 }
diff --git a/code_builder/lib/src/specs/code.g.dart b/code_builder/lib/src/specs/code.g.dart
index 63a6eab..db49119 100644
--- a/code_builder/lib/src/specs/code.g.dart
+++ b/code_builder/lib/src/specs/code.g.dart
@@ -10,13 +10,11 @@
   @override
   final BuiltList<Code> statements;
 
-  factory _$Block([void Function(BlockBuilder) updates]) =>
+  factory _$Block([void Function(BlockBuilder)? updates]) =>
       (new BlockBuilder()..update(updates)).build() as _$Block;
 
-  _$Block._({this.statements}) : super._() {
-    if (statements == null) {
-      throw new BuiltValueNullFieldError('Block', 'statements');
-    }
+  _$Block._({required this.statements}) : super._() {
+    BuiltValueNullFieldError.checkNotNull(statements, 'Block', 'statements');
   }
 
   @override
@@ -45,12 +43,12 @@
 }
 
 class _$BlockBuilder extends BlockBuilder {
-  _$Block _$v;
+  _$Block? _$v;
 
   @override
   ListBuilder<Code> get statements {
     _$this;
-    return super.statements ??= new ListBuilder<Code>();
+    return super.statements;
   }
 
   @override
@@ -62,8 +60,9 @@
   _$BlockBuilder() : super._();
 
   BlockBuilder get _$this {
-    if (_$v != null) {
-      super.statements = _$v.statements?.toBuilder();
+    final $v = _$v;
+    if ($v != null) {
+      super.statements = $v.statements.toBuilder();
       _$v = null;
     }
     return this;
@@ -71,14 +70,12 @@
 
   @override
   void replace(Block other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Block;
   }
 
   @override
-  void update(void Function(BlockBuilder) updates) {
+  void update(void Function(BlockBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -88,7 +85,7 @@
     try {
       _$result = _$v ?? new _$Block._(statements: statements.build());
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'statements';
         statements.build();
diff --git a/code_builder/lib/src/specs/constructor.dart b/code_builder/lib/src/specs/constructor.dart
index fb9f689..381f3c5 100644
--- a/code_builder/lib/src/specs/constructor.dart
+++ b/code_builder/lib/src/specs/constructor.dart
@@ -40,8 +40,7 @@
   BuiltList<Code> get initializers;
 
   /// Body of the method.
-  @nullable
-  Code get body;
+  Code? get body;
 
   /// Whether the constructor should be prefixed with `external`.
   bool get external;
@@ -53,16 +52,13 @@
   bool get factory;
 
   /// Whether this constructor is a simple lambda expression.
-  @nullable
-  bool get lambda;
+  bool? get lambda;
 
   /// Name of the constructor - optional.
-  @nullable
-  String get name;
+  String? get name;
 
   /// If non-null, redirect to this constructor.
-  @nullable
-  Reference get redirect;
+  Reference? get redirect;
 }
 
 abstract class ConstructorBuilder extends Object
@@ -88,7 +84,7 @@
   ListBuilder<Code> initializers = ListBuilder<Code>();
 
   /// Body of the constructor.
-  Code body;
+  Code? body;
 
   /// Whether the constructor should be prefixed with `const`.
   bool constant = false;
@@ -100,12 +96,11 @@
   bool factory = false;
 
   /// Whether this constructor is a simple lambda expression.
-  bool lambda;
+  bool? lambda;
 
   /// Name of the constructor - optional.
-  String name;
+  String? name;
 
   /// If non-null, redirect to this constructor.
-  @nullable
-  Reference redirect;
+  Reference? redirect;
 }
diff --git a/code_builder/lib/src/specs/constructor.g.dart b/code_builder/lib/src/specs/constructor.g.dart
index 0f803d9..5a6c4d7 100644
--- a/code_builder/lib/src/specs/constructor.g.dart
+++ b/code_builder/lib/src/specs/constructor.g.dart
@@ -18,7 +18,7 @@
   @override
   final BuiltList<Code> initializers;
   @override
-  final Code body;
+  final Code? body;
   @override
   final bool external;
   @override
@@ -26,53 +26,41 @@
   @override
   final bool factory;
   @override
-  final bool lambda;
+  final bool? lambda;
   @override
-  final String name;
+  final String? name;
   @override
-  final Reference redirect;
+  final Reference? redirect;
 
-  factory _$Constructor([void Function(ConstructorBuilder) updates]) =>
+  factory _$Constructor([void Function(ConstructorBuilder)? updates]) =>
       (new ConstructorBuilder()..update(updates)).build() as _$Constructor;
 
   _$Constructor._(
-      {this.annotations,
-      this.docs,
-      this.optionalParameters,
-      this.requiredParameters,
-      this.initializers,
+      {required this.annotations,
+      required this.docs,
+      required this.optionalParameters,
+      required this.requiredParameters,
+      required this.initializers,
       this.body,
-      this.external,
-      this.constant,
-      this.factory,
+      required this.external,
+      required this.constant,
+      required this.factory,
       this.lambda,
       this.name,
       this.redirect})
       : super._() {
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'docs');
-    }
-    if (optionalParameters == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'optionalParameters');
-    }
-    if (requiredParameters == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'requiredParameters');
-    }
-    if (initializers == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'initializers');
-    }
-    if (external == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'external');
-    }
-    if (constant == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'constant');
-    }
-    if (factory == null) {
-      throw new BuiltValueNullFieldError('Constructor', 'factory');
-    }
+    BuiltValueNullFieldError.checkNotNull(
+        annotations, 'Constructor', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Constructor', 'docs');
+    BuiltValueNullFieldError.checkNotNull(
+        optionalParameters, 'Constructor', 'optionalParameters');
+    BuiltValueNullFieldError.checkNotNull(
+        requiredParameters, 'Constructor', 'requiredParameters');
+    BuiltValueNullFieldError.checkNotNull(
+        initializers, 'Constructor', 'initializers');
+    BuiltValueNullFieldError.checkNotNull(external, 'Constructor', 'external');
+    BuiltValueNullFieldError.checkNotNull(constant, 'Constructor', 'constant');
+    BuiltValueNullFieldError.checkNotNull(factory, 'Constructor', 'factory');
   }
 
   @override
@@ -146,12 +134,12 @@
 }
 
 class _$ConstructorBuilder extends ConstructorBuilder {
-  _$Constructor _$v;
+  _$Constructor? _$v;
 
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -163,7 +151,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -175,7 +163,7 @@
   @override
   ListBuilder<Parameter> get optionalParameters {
     _$this;
-    return super.optionalParameters ??= new ListBuilder<Parameter>();
+    return super.optionalParameters;
   }
 
   @override
@@ -187,7 +175,7 @@
   @override
   ListBuilder<Parameter> get requiredParameters {
     _$this;
-    return super.requiredParameters ??= new ListBuilder<Parameter>();
+    return super.requiredParameters;
   }
 
   @override
@@ -199,7 +187,7 @@
   @override
   ListBuilder<Code> get initializers {
     _$this;
-    return super.initializers ??= new ListBuilder<Code>();
+    return super.initializers;
   }
 
   @override
@@ -209,13 +197,13 @@
   }
 
   @override
-  Code get body {
+  Code? get body {
     _$this;
     return super.body;
   }
 
   @override
-  set body(Code body) {
+  set body(Code? body) {
     _$this;
     super.body = body;
   }
@@ -257,37 +245,37 @@
   }
 
   @override
-  bool get lambda {
+  bool? get lambda {
     _$this;
     return super.lambda;
   }
 
   @override
-  set lambda(bool lambda) {
+  set lambda(bool? lambda) {
     _$this;
     super.lambda = lambda;
   }
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
 
   @override
-  Reference get redirect {
+  Reference? get redirect {
     _$this;
     return super.redirect;
   }
 
   @override
-  set redirect(Reference redirect) {
+  set redirect(Reference? redirect) {
     _$this;
     super.redirect = redirect;
   }
@@ -295,19 +283,20 @@
   _$ConstructorBuilder() : super._();
 
   ConstructorBuilder get _$this {
-    if (_$v != null) {
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
-      super.optionalParameters = _$v.optionalParameters?.toBuilder();
-      super.requiredParameters = _$v.requiredParameters?.toBuilder();
-      super.initializers = _$v.initializers?.toBuilder();
-      super.body = _$v.body;
-      super.external = _$v.external;
-      super.constant = _$v.constant;
-      super.factory = _$v.factory;
-      super.lambda = _$v.lambda;
-      super.name = _$v.name;
-      super.redirect = _$v.redirect;
+    final $v = _$v;
+    if ($v != null) {
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
+      super.optionalParameters = $v.optionalParameters.toBuilder();
+      super.requiredParameters = $v.requiredParameters.toBuilder();
+      super.initializers = $v.initializers.toBuilder();
+      super.body = $v.body;
+      super.external = $v.external;
+      super.constant = $v.constant;
+      super.factory = $v.factory;
+      super.lambda = $v.lambda;
+      super.name = $v.name;
+      super.redirect = $v.redirect;
       _$v = null;
     }
     return this;
@@ -315,14 +304,12 @@
 
   @override
   void replace(Constructor other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Constructor;
   }
 
   @override
-  void update(void Function(ConstructorBuilder) updates) {
+  void update(void Function(ConstructorBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -338,14 +325,17 @@
               requiredParameters: requiredParameters.build(),
               initializers: initializers.build(),
               body: body,
-              external: external,
-              constant: constant,
-              factory: factory,
+              external: BuiltValueNullFieldError.checkNotNull(
+                  external, 'Constructor', 'external'),
+              constant: BuiltValueNullFieldError.checkNotNull(
+                  constant, 'Constructor', 'constant'),
+              factory: BuiltValueNullFieldError.checkNotNull(
+                  factory, 'Constructor', 'factory'),
               lambda: lambda,
               name: name,
               redirect: redirect);
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
diff --git a/code_builder/lib/src/specs/directive.dart b/code_builder/lib/src/specs/directive.dart
index ee28f84..e10c0ea 100644
--- a/code_builder/lib/src/specs/directive.dart
+++ b/code_builder/lib/src/specs/directive.dart
@@ -18,7 +18,7 @@
 
   factory Directive.import(
     String url, {
-    String as,
+    String? as,
     List<String> show = const [],
     List<String> hide = const [],
   }) =>
@@ -64,8 +64,7 @@
 
   Directive._();
 
-  @nullable
-  String get as;
+  String? get as;
 
   String get url;
 
@@ -80,7 +79,7 @@
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitDirective(this, context);
 
@@ -96,15 +95,15 @@
 
   bool deferred = false;
 
-  String as;
+  String? as;
 
-  String url;
+  String? url;
 
   List<String> show = <String>[];
 
   List<String> hide = <String>[];
 
-  DirectiveType type;
+  DirectiveType? type;
 }
 
 enum DirectiveType {
diff --git a/code_builder/lib/src/specs/directive.g.dart b/code_builder/lib/src/specs/directive.g.dart
index ce34db5..500f5cd 100644
--- a/code_builder/lib/src/specs/directive.g.dart
+++ b/code_builder/lib/src/specs/directive.g.dart
@@ -8,7 +8,7 @@
 
 class _$Directive extends Directive {
   @override
-  final String as;
+  final String? as;
   @override
   final String url;
   @override
@@ -20,27 +20,22 @@
   @override
   final bool deferred;
 
-  factory _$Directive([void Function(DirectiveBuilder) updates]) =>
+  factory _$Directive([void Function(DirectiveBuilder)? updates]) =>
       (new DirectiveBuilder()..update(updates)).build() as _$Directive;
 
   _$Directive._(
-      {this.as, this.url, this.type, this.show, this.hide, this.deferred})
+      {this.as,
+      required this.url,
+      required this.type,
+      required this.show,
+      required this.hide,
+      required this.deferred})
       : super._() {
-    if (url == null) {
-      throw new BuiltValueNullFieldError('Directive', 'url');
-    }
-    if (type == null) {
-      throw new BuiltValueNullFieldError('Directive', 'type');
-    }
-    if (show == null) {
-      throw new BuiltValueNullFieldError('Directive', 'show');
-    }
-    if (hide == null) {
-      throw new BuiltValueNullFieldError('Directive', 'hide');
-    }
-    if (deferred == null) {
-      throw new BuiltValueNullFieldError('Directive', 'deferred');
-    }
+    BuiltValueNullFieldError.checkNotNull(url, 'Directive', 'url');
+    BuiltValueNullFieldError.checkNotNull(type, 'Directive', 'type');
+    BuiltValueNullFieldError.checkNotNull(show, 'Directive', 'show');
+    BuiltValueNullFieldError.checkNotNull(hide, 'Directive', 'hide');
+    BuiltValueNullFieldError.checkNotNull(deferred, 'Directive', 'deferred');
   }
 
   @override
@@ -86,40 +81,40 @@
 }
 
 class _$DirectiveBuilder extends DirectiveBuilder {
-  _$Directive _$v;
+  _$Directive? _$v;
 
   @override
-  String get as {
+  String? get as {
     _$this;
     return super.as;
   }
 
   @override
-  set as(String as) {
+  set as(String? as) {
     _$this;
     super.as = as;
   }
 
   @override
-  String get url {
+  String? get url {
     _$this;
     return super.url;
   }
 
   @override
-  set url(String url) {
+  set url(String? url) {
     _$this;
     super.url = url;
   }
 
   @override
-  DirectiveType get type {
+  DirectiveType? get type {
     _$this;
     return super.type;
   }
 
   @override
-  set type(DirectiveType type) {
+  set type(DirectiveType? type) {
     _$this;
     super.type = type;
   }
@@ -163,13 +158,14 @@
   _$DirectiveBuilder() : super._();
 
   DirectiveBuilder get _$this {
-    if (_$v != null) {
-      super.as = _$v.as;
-      super.url = _$v.url;
-      super.type = _$v.type;
-      super.show = _$v.show;
-      super.hide = _$v.hide;
-      super.deferred = _$v.deferred;
+    final $v = _$v;
+    if ($v != null) {
+      super.as = $v.as;
+      super.url = $v.url;
+      super.type = $v.type;
+      super.show = $v.show;
+      super.hide = $v.hide;
+      super.deferred = $v.deferred;
       _$v = null;
     }
     return this;
@@ -177,14 +173,12 @@
 
   @override
   void replace(Directive other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Directive;
   }
 
   @override
-  void update(void Function(DirectiveBuilder) updates) {
+  void update(void Function(DirectiveBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -193,11 +187,15 @@
     final _$result = _$v ??
         new _$Directive._(
             as: as,
-            url: url,
-            type: type,
-            show: show,
-            hide: hide,
-            deferred: deferred);
+            url: BuiltValueNullFieldError.checkNotNull(url, 'Directive', 'url'),
+            type: BuiltValueNullFieldError.checkNotNull(
+                type, 'Directive', 'type'),
+            show: BuiltValueNullFieldError.checkNotNull(
+                show, 'Directive', 'show'),
+            hide: BuiltValueNullFieldError.checkNotNull(
+                hide, 'Directive', 'hide'),
+            deferred: BuiltValueNullFieldError.checkNotNull(
+                deferred, 'Directive', 'deferred'));
     replace(_$result);
     return _$result;
   }
diff --git a/code_builder/lib/src/specs/enum.dart b/code_builder/lib/src/specs/enum.dart
index cb50ab1..c299f21 100644
--- a/code_builder/lib/src/specs/enum.dart
+++ b/code_builder/lib/src/specs/enum.dart
@@ -34,7 +34,7 @@
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitEnum(this, context);
 }
@@ -46,7 +46,7 @@
 
   EnumBuilder._();
 
-  String name;
+  String? name;
 
   ListBuilder<EnumValue> values = ListBuilder<EnumValue>();
 
@@ -81,7 +81,7 @@
 
   EnumValueBuilder._();
 
-  String name;
+  String? name;
 
   @override
   ListBuilder<Expression> annotations = ListBuilder<Expression>();
diff --git a/code_builder/lib/src/specs/enum.g.dart b/code_builder/lib/src/specs/enum.g.dart
index 8834ebd..4e82863 100644
--- a/code_builder/lib/src/specs/enum.g.dart
+++ b/code_builder/lib/src/specs/enum.g.dart
@@ -16,22 +16,19 @@
   @override
   final BuiltList<String> docs;
 
-  factory _$Enum([void Function(EnumBuilder) updates]) =>
+  factory _$Enum([void Function(EnumBuilder)? updates]) =>
       (new EnumBuilder()..update(updates)).build() as _$Enum;
 
-  _$Enum._({this.name, this.values, this.annotations, this.docs}) : super._() {
-    if (name == null) {
-      throw new BuiltValueNullFieldError('Enum', 'name');
-    }
-    if (values == null) {
-      throw new BuiltValueNullFieldError('Enum', 'values');
-    }
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Enum', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Enum', 'docs');
-    }
+  _$Enum._(
+      {required this.name,
+      required this.values,
+      required this.annotations,
+      required this.docs})
+      : super._() {
+    BuiltValueNullFieldError.checkNotNull(name, 'Enum', 'name');
+    BuiltValueNullFieldError.checkNotNull(values, 'Enum', 'values');
+    BuiltValueNullFieldError.checkNotNull(annotations, 'Enum', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Enum', 'docs');
   }
 
   @override
@@ -70,16 +67,16 @@
 }
 
 class _$EnumBuilder extends EnumBuilder {
-  _$Enum _$v;
+  _$Enum? _$v;
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
@@ -87,7 +84,7 @@
   @override
   ListBuilder<EnumValue> get values {
     _$this;
-    return super.values ??= new ListBuilder<EnumValue>();
+    return super.values;
   }
 
   @override
@@ -99,7 +96,7 @@
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -111,7 +108,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -123,11 +120,12 @@
   _$EnumBuilder() : super._();
 
   EnumBuilder get _$this {
-    if (_$v != null) {
-      super.name = _$v.name;
-      super.values = _$v.values?.toBuilder();
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
+    final $v = _$v;
+    if ($v != null) {
+      super.name = $v.name;
+      super.values = $v.values.toBuilder();
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
       _$v = null;
     }
     return this;
@@ -135,14 +133,12 @@
 
   @override
   void replace(Enum other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Enum;
   }
 
   @override
-  void update(void Function(EnumBuilder) updates) {
+  void update(void Function(EnumBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -152,12 +148,12 @@
     try {
       _$result = _$v ??
           new _$Enum._(
-              name: name,
+              name: BuiltValueNullFieldError.checkNotNull(name, 'Enum', 'name'),
               values: values.build(),
               annotations: annotations.build(),
               docs: docs.build());
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'values';
         values.build();
@@ -184,19 +180,16 @@
   @override
   final BuiltList<String> docs;
 
-  factory _$EnumValue([void Function(EnumValueBuilder) updates]) =>
+  factory _$EnumValue([void Function(EnumValueBuilder)? updates]) =>
       (new EnumValueBuilder()..update(updates)).build() as _$EnumValue;
 
-  _$EnumValue._({this.name, this.annotations, this.docs}) : super._() {
-    if (name == null) {
-      throw new BuiltValueNullFieldError('EnumValue', 'name');
-    }
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('EnumValue', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('EnumValue', 'docs');
-    }
+  _$EnumValue._(
+      {required this.name, required this.annotations, required this.docs})
+      : super._() {
+    BuiltValueNullFieldError.checkNotNull(name, 'EnumValue', 'name');
+    BuiltValueNullFieldError.checkNotNull(
+        annotations, 'EnumValue', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'EnumValue', 'docs');
   }
 
   @override
@@ -232,16 +225,16 @@
 }
 
 class _$EnumValueBuilder extends EnumValueBuilder {
-  _$EnumValue _$v;
+  _$EnumValue? _$v;
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
@@ -249,7 +242,7 @@
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -261,7 +254,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -273,10 +266,11 @@
   _$EnumValueBuilder() : super._();
 
   EnumValueBuilder get _$this {
-    if (_$v != null) {
-      super.name = _$v.name;
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
+    final $v = _$v;
+    if ($v != null) {
+      super.name = $v.name;
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
       _$v = null;
     }
     return this;
@@ -284,14 +278,12 @@
 
   @override
   void replace(EnumValue other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$EnumValue;
   }
 
   @override
-  void update(void Function(EnumValueBuilder) updates) {
+  void update(void Function(EnumValueBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -301,9 +293,12 @@
     try {
       _$result = _$v ??
           new _$EnumValue._(
-              name: name, annotations: annotations.build(), docs: docs.build());
+              name: BuiltValueNullFieldError.checkNotNull(
+                  name, 'EnumValue', 'name'),
+              annotations: annotations.build(),
+              docs: docs.build());
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
diff --git a/code_builder/lib/src/specs/expression.dart b/code_builder/lib/src/specs/expression.dart
index c186a7e..947ddde 100644
--- a/code_builder/lib/src/specs/expression.dart
+++ b/code_builder/lib/src/specs/expression.dart
@@ -19,6 +19,7 @@
 part 'expression/code.dart';
 part 'expression/invoke.dart';
 part 'expression/literal.dart';
+part 'expression/parenthesized.dart';
 
 /// Represents a [code] block that wraps an [Expression].
 
@@ -32,7 +33,7 @@
   static const _empty = CodeExpression(Code(''));
 
   @override
-  R accept<R>(covariant ExpressionVisitor<R> visitor, [R context]);
+  R accept<R>(covariant ExpressionVisitor<R> visitor, [R? context]);
 
   /// The expression as a valid [Code] block.
   ///
@@ -55,15 +56,12 @@
       BinaryExpression._(_empty, expression, '!', addSpace: false);
 
   /// Returns the result of `this` `as` [other].
-  Expression asA(Expression other) => CodeExpression(Block.of([
-        const Code('('),
-        BinaryExpression._(
-          expression,
-          other,
-          'as',
-        ).code,
-        const Code(')')
-      ]));
+  Expression asA(Expression other) =>
+      ParenthesizedExpression._(BinaryExpression._(
+        expression,
+        other,
+        'as',
+      ));
 
   /// Returns accessing the index operator (`[]`) on `this`.
   Expression index(Expression index) => BinaryExpression._(
@@ -203,7 +201,7 @@
       );
 
   /// Return `var {name} = {this}`.
-  Expression assignVar(String name, [Reference type]) => BinaryExpression._(
+  Expression assignVar(String name, [Reference? type]) => BinaryExpression._(
         type == null
             ? LiteralExpression._('var $name')
             : BinaryExpression._(
@@ -216,7 +214,7 @@
       );
 
   /// Return `final {name} = {this}`.
-  Expression assignFinal(String name, [Reference type]) => BinaryExpression._(
+  Expression assignFinal(String name, [Reference? type]) => BinaryExpression._(
         type == null
             ? const LiteralExpression._('final')
             : BinaryExpression._(
@@ -229,7 +227,7 @@
       );
 
   /// Return `const {name} = {this}`.
-  Expression assignConst(String name, [Reference type]) => BinaryExpression._(
+  Expression assignConst(String name, [Reference? type]) => BinaryExpression._(
         type == null
             ? const LiteralExpression._('const')
             : BinaryExpression._(
@@ -313,7 +311,7 @@
   const ToCodeExpression(this.code, [this.isStatement = false]);
 
   @override
-  R accept<R>(CodeVisitor<R> visitor, [R context]) =>
+  R accept<R>(CodeVisitor<R> visitor, [R? context]) =>
       (visitor as ExpressionVisitor<R>).visitToCodeExpression(this, context);
 
   @override
@@ -324,15 +322,17 @@
 ///
 /// **INTERNAL ONLY**.
 abstract class ExpressionVisitor<T> implements SpecVisitor<T> {
-  T visitToCodeExpression(ToCodeExpression code, [T context]);
-  T visitBinaryExpression(BinaryExpression expression, [T context]);
-  T visitClosureExpression(ClosureExpression expression, [T context]);
-  T visitCodeExpression(CodeExpression expression, [T context]);
-  T visitInvokeExpression(InvokeExpression expression, [T context]);
-  T visitLiteralExpression(LiteralExpression expression, [T context]);
-  T visitLiteralListExpression(LiteralListExpression expression, [T context]);
-  T visitLiteralSetExpression(LiteralSetExpression expression, [T context]);
-  T visitLiteralMapExpression(LiteralMapExpression expression, [T context]);
+  T visitToCodeExpression(ToCodeExpression code, [T? context]);
+  T visitBinaryExpression(BinaryExpression expression, [T? context]);
+  T visitClosureExpression(ClosureExpression expression, [T? context]);
+  T visitCodeExpression(CodeExpression expression, [T? context]);
+  T visitInvokeExpression(InvokeExpression expression, [T? context]);
+  T visitLiteralExpression(LiteralExpression expression, [T? context]);
+  T visitLiteralListExpression(LiteralListExpression expression, [T? context]);
+  T visitLiteralSetExpression(LiteralSetExpression expression, [T? context]);
+  T visitLiteralMapExpression(LiteralMapExpression expression, [T? context]);
+  T visitParenthesizedExpression(ParenthesizedExpression expression,
+      [T? context]);
 }
 
 /// Knowledge of how to write valid Dart code from [ExpressionVisitor].
@@ -341,7 +341,7 @@
 abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
   @override
   StringSink visitToCodeExpression(ToCodeExpression expression,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     expression.code.accept(this, output);
     if (expression.isStatement) {
@@ -352,7 +352,7 @@
 
   @override
   StringSink visitBinaryExpression(BinaryExpression expression,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     expression.left.accept(this, output);
     if (expression.addSpace) {
@@ -370,14 +370,14 @@
 
   @override
   StringSink visitClosureExpression(ClosureExpression expression,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     return expression.method.accept(this, output);
   }
 
   @override
   StringSink visitCodeExpression(CodeExpression expression,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     final visitor = this as CodeVisitor<StringSink>;
     return expression.code.accept(visitor, output);
@@ -385,45 +385,45 @@
 
   @override
   StringSink visitInvokeExpression(InvokeExpression expression,
-      [StringSink output]) {
-    output ??= StringBuffer();
+      [StringSink? output]) {
+    final out = output ??= StringBuffer();
     return _writeConstExpression(
-        output, expression.type == InvokeExpressionType.constInstance, () {
-      expression.target.accept(this, output);
+        out, expression.type == InvokeExpressionType.constInstance, () {
+      expression.target.accept(this, out);
       if (expression.name != null) {
-        output..write('.')..write(expression.name);
+        out..write('.')..write(expression.name);
       }
       if (expression.typeArguments.isNotEmpty) {
-        output.write('<');
-        visitAll<Reference>(expression.typeArguments, output, (type) {
-          type.accept(this, output);
+        out.write('<');
+        visitAll<Reference>(expression.typeArguments, out, (type) {
+          type.accept(this, out);
         });
-        output.write('>');
+        out.write('>');
       }
-      output.write('(');
-      visitAll<Spec>(expression.positionalArguments, output, (spec) {
-        spec.accept(this, output);
+      out.write('(');
+      visitAll<Spec>(expression.positionalArguments, out, (spec) {
+        spec.accept(this, out);
       });
       if (expression.positionalArguments.isNotEmpty &&
           expression.namedArguments.isNotEmpty) {
-        output.write(', ');
+        out.write(', ');
       }
-      visitAll<String>(expression.namedArguments.keys, output, (name) {
-        output..write(name)..write(': ');
-        expression.namedArguments[name].accept(this, output);
+      visitAll<String>(expression.namedArguments.keys, out, (name) {
+        out..write(name)..write(': ');
+        expression.namedArguments[name]!.accept(this, out);
       });
-      return output..write(')');
+      return out..write(')');
     });
   }
 
   @override
   StringSink visitLiteralExpression(LiteralExpression expression,
-      [StringSink output]) {
+      [StringSink? output]) {
     output ??= StringBuffer();
     return output..write(expression.literal);
   }
 
-  void _acceptLiteral(Object literalOrSpec, StringSink output) {
+  void _acceptLiteral(Object? literalOrSpec, StringSink output) {
     if (literalOrSpec is Spec) {
       literalOrSpec.accept(this, output);
       return;
@@ -436,72 +436,84 @@
   @override
   StringSink visitLiteralListExpression(
     LiteralListExpression expression, [
-    StringSink output,
+    StringSink? output,
   ]) {
-    output ??= StringBuffer();
+    final out = output ??= StringBuffer();
 
     return _writeConstExpression(output, expression.isConst, () {
       if (expression.type != null) {
-        output.write('<');
-        expression.type.accept(this, output);
-        output.write('>');
+        out.write('<');
+        expression.type!.accept(this, output);
+        out.write('>');
       }
-      output.write('[');
-      visitAll<Object>(expression.values, output, (value) {
-        _acceptLiteral(value, output);
+      out.write('[');
+      visitAll<Object?>(expression.values, out, (value) {
+        _acceptLiteral(value, out);
       });
-      return output..write(']');
+      return out..write(']');
     });
   }
 
   @override
   StringSink visitLiteralSetExpression(
     LiteralSetExpression expression, [
-    StringSink output,
+    StringSink? output,
   ]) {
-    output ??= StringBuffer();
+    final out = output ??= StringBuffer();
 
     return _writeConstExpression(output, expression.isConst, () {
       if (expression.type != null) {
-        output.write('<');
-        expression.type.accept(this, output);
-        output.write('>');
+        out.write('<');
+        expression.type!.accept(this, output);
+        out.write('>');
       }
-      output.write('{');
-      visitAll<Object>(expression.values, output, (value) {
-        _acceptLiteral(value, output);
+      out.write('{');
+      visitAll<Object?>(expression.values, out, (value) {
+        _acceptLiteral(value, out);
       });
-      return output..write('}');
+      return out..write('}');
     });
   }
 
   @override
   StringSink visitLiteralMapExpression(
     LiteralMapExpression expression, [
-    StringSink output,
+    StringSink? output,
+  ]) {
+    final out = output ??= StringBuffer();
+    return _writeConstExpression(out, expression.isConst, () {
+      if (expression.keyType != null) {
+        out.write('<');
+        expression.keyType!.accept(this, out);
+        out.write(', ');
+        if (expression.valueType == null) {
+          const Reference('dynamic', 'dart:core').accept(this, out);
+        } else {
+          expression.valueType!.accept(this, out);
+        }
+        out.write('>');
+      }
+      out.write('{');
+      visitAll<Object?>(expression.values.keys, out, (key) {
+        final value = expression.values[key];
+        _acceptLiteral(key, out);
+        out.write(': ');
+        _acceptLiteral(value, out);
+      });
+      return out..write('}');
+    });
+  }
+
+  @override
+  StringSink visitParenthesizedExpression(
+    ParenthesizedExpression expression, [
+    StringSink? output,
   ]) {
     output ??= StringBuffer();
-    return _writeConstExpression(output, expression.isConst, () {
-      if (expression.keyType != null) {
-        output.write('<');
-        expression.keyType.accept(this, output);
-        output.write(', ');
-        if (expression.valueType == null) {
-          const Reference('dynamic', 'dart:core').accept(this, output);
-        } else {
-          expression.valueType.accept(this, output);
-        }
-        output.write('>');
-      }
-      output.write('{');
-      visitAll<Object>(expression.values.keys, output, (key) {
-        final value = expression.values[key];
-        _acceptLiteral(key, output);
-        output.write(': ');
-        _acceptLiteral(value, output);
-      });
-      return output..write('}');
-    });
+    output.write('(');
+    expression.inner.accept(this, output);
+    output.write(')');
+    return output;
   }
 
   /// Executes [visit] within a context which may alter the output if [isConst]
diff --git a/code_builder/lib/src/specs/expression/binary.dart b/code_builder/lib/src/specs/expression/binary.dart
index b3a8808..4672a26 100644
--- a/code_builder/lib/src/specs/expression/binary.dart
+++ b/code_builder/lib/src/specs/expression/binary.dart
@@ -21,6 +21,6 @@
   });
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitBinaryExpression(this, context);
 }
diff --git a/code_builder/lib/src/specs/expression/closure.dart b/code_builder/lib/src/specs/expression/closure.dart
index 4f9e1a1..0506465 100644
--- a/code_builder/lib/src/specs/expression/closure.dart
+++ b/code_builder/lib/src/specs/expression/closure.dart
@@ -27,6 +27,6 @@
   const ClosureExpression._(this.method);
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitClosureExpression(this, context);
 }
diff --git a/code_builder/lib/src/specs/expression/code.dart b/code_builder/lib/src/specs/expression/code.dart
index 3a54219..0eb0c3d 100644
--- a/code_builder/lib/src/specs/expression/code.dart
+++ b/code_builder/lib/src/specs/expression/code.dart
@@ -13,6 +13,6 @@
   const CodeExpression(this.code);
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitCodeExpression(this, context);
 }
diff --git a/code_builder/lib/src/specs/expression/invoke.dart b/code_builder/lib/src/specs/expression/invoke.dart
index fb56395..448ce3e 100644
--- a/code_builder/lib/src/specs/expression/invoke.dart
+++ b/code_builder/lib/src/specs/expression/invoke.dart
@@ -10,18 +10,18 @@
   final Expression target;
 
   /// Optional; type of invocation.
-  final InvokeExpressionType type;
+  final InvokeExpressionType? type;
 
   final List<Expression> positionalArguments;
   final Map<String, Expression> namedArguments;
   final List<Reference> typeArguments;
-  final String name;
+  final String? name;
 
   const InvokeExpression._(
     this.target,
     this.positionalArguments, [
     this.namedArguments = const {},
-    this.typeArguments,
+    this.typeArguments = const [],
     this.name,
   ]) : type = null;
 
@@ -29,7 +29,7 @@
     this.target,
     this.positionalArguments, [
     this.namedArguments = const {},
-    this.typeArguments,
+    this.typeArguments = const [],
     this.name,
   ]) : type = InvokeExpressionType.newInstance;
 
@@ -37,12 +37,12 @@
     this.target,
     this.positionalArguments, [
     this.namedArguments = const {},
-    this.typeArguments,
+    this.typeArguments = const [],
     this.name,
   ]) : type = InvokeExpressionType.constInstance;
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitInvokeExpression(this, context);
 
   @override
diff --git a/code_builder/lib/src/specs/expression/literal.dart b/code_builder/lib/src/specs/expression/literal.dart
index b4ae81a..4c896d7 100644
--- a/code_builder/lib/src/specs/expression/literal.dart
+++ b/code_builder/lib/src/specs/expression/literal.dart
@@ -7,7 +7,7 @@
 /// Converts a runtime Dart [literal] value into an [Expression].
 ///
 /// Unsupported inputs invoke the [onError] callback.
-Expression literal(Object literal, {Expression Function(Object) onError}) {
+Expression literal(Object? literal, {Expression Function(Object)? onError}) {
   if (literal is bool) {
     return literalBool(literal);
   }
@@ -66,34 +66,36 @@
 }
 
 /// Creates a literal list expression from [values].
-LiteralListExpression literalList(Iterable<Object> values, [Reference type]) =>
+LiteralListExpression literalList(Iterable<Object?> values,
+        [Reference? type]) =>
     LiteralListExpression._(false, values.toList(), type);
 
 /// Creates a literal `const` list expression from [values].
-LiteralListExpression literalConstList(List<Object> values, [Reference type]) =>
+LiteralListExpression literalConstList(List<Object?> values,
+        [Reference? type]) =>
     LiteralListExpression._(true, values, type);
 
 /// Creates a literal set expression from [values].
-LiteralSetExpression literalSet(Iterable<Object> values, [Reference type]) =>
+LiteralSetExpression literalSet(Iterable<Object?> values, [Reference? type]) =>
     LiteralSetExpression._(false, values.toSet(), type);
 
 /// Creates a literal `const` set expression from [values].
-LiteralSetExpression literalConstSet(Set<Object> values, [Reference type]) =>
+LiteralSetExpression literalConstSet(Set<Object?> values, [Reference? type]) =>
     LiteralSetExpression._(true, values, type);
 
 /// Create a literal map expression from [values].
 LiteralMapExpression literalMap(
-  Map<Object, Object> values, [
-  Reference keyType,
-  Reference valueType,
+  Map<Object?, Object?> values, [
+  Reference? keyType,
+  Reference? valueType,
 ]) =>
     LiteralMapExpression._(false, values, keyType, valueType);
 
 /// Create a literal `const` map expression from [values].
 LiteralMapExpression literalConstMap(
-  Map<Object, Object> values, [
-  Reference keyType,
-  Reference valueType,
+  Map<Object?, Object?> values, [
+  Reference? keyType,
+  Reference? valueType,
 ]) =>
     LiteralMapExpression._(true, values, keyType, valueType);
 
@@ -113,7 +115,7 @@
   const LiteralExpression._(this.literal);
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitLiteralExpression(this, context);
 
   @override
@@ -122,13 +124,13 @@
 
 class LiteralListExpression extends Expression {
   final bool isConst;
-  final List<Object> values;
-  final Reference type;
+  final List<Object?> values;
+  final Reference? type;
 
   const LiteralListExpression._(this.isConst, this.values, this.type);
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitLiteralListExpression(this, context);
 
   @override
@@ -137,13 +139,13 @@
 
 class LiteralSetExpression extends Expression {
   final bool isConst;
-  final Set<Object> values;
-  final Reference type;
+  final Set<Object?> values;
+  final Reference? type;
 
   const LiteralSetExpression._(this.isConst, this.values, this.type);
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitLiteralSetExpression(this, context);
 
   @override
@@ -152,9 +154,9 @@
 
 class LiteralMapExpression extends Expression {
   final bool isConst;
-  final Map<Object, Object> values;
-  final Reference keyType;
-  final Reference valueType;
+  final Map<Object?, Object?> values;
+  final Reference? keyType;
+  final Reference? valueType;
 
   const LiteralMapExpression._(
     this.isConst,
@@ -164,7 +166,7 @@
   );
 
   @override
-  R accept<R>(ExpressionVisitor<R> visitor, [R context]) =>
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
       visitor.visitLiteralMapExpression(this, context);
 
   @override
diff --git a/code_builder/lib/src/specs/expression/parenthesized.dart b/code_builder/lib/src/specs/expression/parenthesized.dart
new file mode 100644
index 0000000..cc4d468
--- /dev/null
+++ b/code_builder/lib/src/specs/expression/parenthesized.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of code_builder.src.specs.expression;
+
+/// An [Expression] wrapped with parenthesis.
+class ParenthesizedExpression extends Expression {
+  final Expression inner;
+
+  const ParenthesizedExpression._(this.inner);
+
+  @override
+  R accept<R>(ExpressionVisitor<R> visitor, [R? context]) =>
+      visitor.visitParenthesizedExpression(this, context);
+}
diff --git a/code_builder/lib/src/specs/extension.dart b/code_builder/lib/src/specs/extension.dart
index 9260471..bc4055d 100644
--- a/code_builder/lib/src/specs/extension.dart
+++ b/code_builder/lib/src/specs/extension.dart
@@ -33,8 +33,7 @@
   @override
   BuiltList<String> get docs;
 
-  @nullable
-  Reference get on;
+  Reference? get on;
 
   @override
   BuiltList<Reference> get types;
@@ -43,13 +42,12 @@
   BuiltList<Field> get fields;
 
   /// Name of the extension - optional.
-  @nullable
-  String get name;
+  String? get name;
 
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitExtension(this, context);
 }
@@ -67,7 +65,7 @@
   @override
   ListBuilder<String> docs = ListBuilder<String>();
 
-  Reference on;
+  Reference? on;
 
   @override
   ListBuilder<Reference> types = ListBuilder<Reference>();
@@ -76,5 +74,5 @@
   ListBuilder<Field> fields = ListBuilder<Field>();
 
   /// Name of the extension - optional.
-  String name;
+  String? name;
 }
diff --git a/code_builder/lib/src/specs/extension.g.dart b/code_builder/lib/src/specs/extension.g.dart
index befd295..75ed397 100644
--- a/code_builder/lib/src/specs/extension.g.dart
+++ b/code_builder/lib/src/specs/extension.g.dart
@@ -12,7 +12,7 @@
   @override
   final BuiltList<String> docs;
   @override
-  final Reference on;
+  final Reference? on;
   @override
   final BuiltList<Reference> types;
   @override
@@ -20,35 +20,26 @@
   @override
   final BuiltList<Field> fields;
   @override
-  final String name;
+  final String? name;
 
-  factory _$Extension([void Function(ExtensionBuilder) updates]) =>
+  factory _$Extension([void Function(ExtensionBuilder)? updates]) =>
       (new ExtensionBuilder()..update(updates)).build() as _$Extension;
 
   _$Extension._(
-      {this.annotations,
-      this.docs,
+      {required this.annotations,
+      required this.docs,
       this.on,
-      this.types,
-      this.methods,
-      this.fields,
+      required this.types,
+      required this.methods,
+      required this.fields,
       this.name})
       : super._() {
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Extension', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Extension', 'docs');
-    }
-    if (types == null) {
-      throw new BuiltValueNullFieldError('Extension', 'types');
-    }
-    if (methods == null) {
-      throw new BuiltValueNullFieldError('Extension', 'methods');
-    }
-    if (fields == null) {
-      throw new BuiltValueNullFieldError('Extension', 'fields');
-    }
+    BuiltValueNullFieldError.checkNotNull(
+        annotations, 'Extension', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Extension', 'docs');
+    BuiltValueNullFieldError.checkNotNull(types, 'Extension', 'types');
+    BuiltValueNullFieldError.checkNotNull(methods, 'Extension', 'methods');
+    BuiltValueNullFieldError.checkNotNull(fields, 'Extension', 'fields');
   }
 
   @override
@@ -100,12 +91,12 @@
 }
 
 class _$ExtensionBuilder extends ExtensionBuilder {
-  _$Extension _$v;
+  _$Extension? _$v;
 
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -117,7 +108,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -127,13 +118,13 @@
   }
 
   @override
-  Reference get on {
+  Reference? get on {
     _$this;
     return super.on;
   }
 
   @override
-  set on(Reference on) {
+  set on(Reference? on) {
     _$this;
     super.on = on;
   }
@@ -141,7 +132,7 @@
   @override
   ListBuilder<Reference> get types {
     _$this;
-    return super.types ??= new ListBuilder<Reference>();
+    return super.types;
   }
 
   @override
@@ -153,7 +144,7 @@
   @override
   ListBuilder<Method> get methods {
     _$this;
-    return super.methods ??= new ListBuilder<Method>();
+    return super.methods;
   }
 
   @override
@@ -165,7 +156,7 @@
   @override
   ListBuilder<Field> get fields {
     _$this;
-    return super.fields ??= new ListBuilder<Field>();
+    return super.fields;
   }
 
   @override
@@ -175,13 +166,13 @@
   }
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
@@ -189,14 +180,15 @@
   _$ExtensionBuilder() : super._();
 
   ExtensionBuilder get _$this {
-    if (_$v != null) {
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
-      super.on = _$v.on;
-      super.types = _$v.types?.toBuilder();
-      super.methods = _$v.methods?.toBuilder();
-      super.fields = _$v.fields?.toBuilder();
-      super.name = _$v.name;
+    final $v = _$v;
+    if ($v != null) {
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
+      super.on = $v.on;
+      super.types = $v.types.toBuilder();
+      super.methods = $v.methods.toBuilder();
+      super.fields = $v.fields.toBuilder();
+      super.name = $v.name;
       _$v = null;
     }
     return this;
@@ -204,14 +196,12 @@
 
   @override
   void replace(Extension other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Extension;
   }
 
   @override
-  void update(void Function(ExtensionBuilder) updates) {
+  void update(void Function(ExtensionBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -229,7 +219,7 @@
               fields: fields.build(),
               name: name);
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
diff --git a/code_builder/lib/src/specs/field.dart b/code_builder/lib/src/specs/field.dart
index ddb0d28..9716834 100644
--- a/code_builder/lib/src/specs/field.dart
+++ b/code_builder/lib/src/specs/field.dart
@@ -31,8 +31,7 @@
   BuiltList<String> get docs;
 
   /// Field assignment, if any.
-  @nullable
-  Code get assignment;
+  Code? get assignment;
 
   /// Whether this field should be prefixed with `static`.
   ///
@@ -42,15 +41,14 @@
   /// Name of the field.
   String get name;
 
-  @nullable
-  Reference get type;
+  Reference? get type;
 
   FieldModifier get modifier;
 
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitField(this, context);
 }
@@ -75,7 +73,7 @@
   ListBuilder<String> docs = ListBuilder<String>();
 
   /// Field assignment, if any.
-  Code assignment;
+  Code? assignment;
 
   /// Whether this field should be prefixed with `static`.
   ///
@@ -83,9 +81,9 @@
   bool static = false;
 
   /// Name of the field.
-  String name;
+  String? name;
 
-  Reference type;
+  Reference? type;
 
   FieldModifier modifier = FieldModifier.var$;
 }
diff --git a/code_builder/lib/src/specs/field.g.dart b/code_builder/lib/src/specs/field.g.dart
index 5dfccd9..651a6a9 100644
--- a/code_builder/lib/src/specs/field.g.dart
+++ b/code_builder/lib/src/specs/field.g.dart
@@ -12,43 +12,33 @@
   @override
   final BuiltList<String> docs;
   @override
-  final Code assignment;
+  final Code? assignment;
   @override
   final bool static;
   @override
   final String name;
   @override
-  final Reference type;
+  final Reference? type;
   @override
   final FieldModifier modifier;
 
-  factory _$Field([void Function(FieldBuilder) updates]) =>
+  factory _$Field([void Function(FieldBuilder)? updates]) =>
       (new FieldBuilder()..update(updates)).build() as _$Field;
 
   _$Field._(
-      {this.annotations,
-      this.docs,
+      {required this.annotations,
+      required this.docs,
       this.assignment,
-      this.static,
-      this.name,
+      required this.static,
+      required this.name,
       this.type,
-      this.modifier})
+      required this.modifier})
       : super._() {
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Field', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Field', 'docs');
-    }
-    if (static == null) {
-      throw new BuiltValueNullFieldError('Field', 'static');
-    }
-    if (name == null) {
-      throw new BuiltValueNullFieldError('Field', 'name');
-    }
-    if (modifier == null) {
-      throw new BuiltValueNullFieldError('Field', 'modifier');
-    }
+    BuiltValueNullFieldError.checkNotNull(annotations, 'Field', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Field', 'docs');
+    BuiltValueNullFieldError.checkNotNull(static, 'Field', 'static');
+    BuiltValueNullFieldError.checkNotNull(name, 'Field', 'name');
+    BuiltValueNullFieldError.checkNotNull(modifier, 'Field', 'modifier');
   }
 
   @override
@@ -100,12 +90,12 @@
 }
 
 class _$FieldBuilder extends FieldBuilder {
-  _$Field _$v;
+  _$Field? _$v;
 
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -117,7 +107,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -127,13 +117,13 @@
   }
 
   @override
-  Code get assignment {
+  Code? get assignment {
     _$this;
     return super.assignment;
   }
 
   @override
-  set assignment(Code assignment) {
+  set assignment(Code? assignment) {
     _$this;
     super.assignment = assignment;
   }
@@ -151,25 +141,25 @@
   }
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
 
   @override
-  Reference get type {
+  Reference? get type {
     _$this;
     return super.type;
   }
 
   @override
-  set type(Reference type) {
+  set type(Reference? type) {
     _$this;
     super.type = type;
   }
@@ -189,14 +179,15 @@
   _$FieldBuilder() : super._();
 
   FieldBuilder get _$this {
-    if (_$v != null) {
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
-      super.assignment = _$v.assignment;
-      super.static = _$v.static;
-      super.name = _$v.name;
-      super.type = _$v.type;
-      super.modifier = _$v.modifier;
+    final $v = _$v;
+    if ($v != null) {
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
+      super.assignment = $v.assignment;
+      super.static = $v.static;
+      super.name = $v.name;
+      super.type = $v.type;
+      super.modifier = $v.modifier;
       _$v = null;
     }
     return this;
@@ -204,14 +195,12 @@
 
   @override
   void replace(Field other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Field;
   }
 
   @override
-  void update(void Function(FieldBuilder) updates) {
+  void update(void Function(FieldBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -224,12 +213,15 @@
               annotations: annotations.build(),
               docs: docs.build(),
               assignment: assignment,
-              static: static,
-              name: name,
+              static: BuiltValueNullFieldError.checkNotNull(
+                  static, 'Field', 'static'),
+              name:
+                  BuiltValueNullFieldError.checkNotNull(name, 'Field', 'name'),
               type: type,
-              modifier: modifier);
+              modifier: BuiltValueNullFieldError.checkNotNull(
+                  modifier, 'Field', 'modifier'));
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
diff --git a/code_builder/lib/src/specs/library.dart b/code_builder/lib/src/specs/library.dart
index d572246..d4ee597 100644
--- a/code_builder/lib/src/specs/library.dart
+++ b/code_builder/lib/src/specs/library.dart
@@ -23,7 +23,7 @@
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitLibrary(this, context);
 }
diff --git a/code_builder/lib/src/specs/library.g.dart b/code_builder/lib/src/specs/library.g.dart
index d302a94..b9178ec 100644
--- a/code_builder/lib/src/specs/library.g.dart
+++ b/code_builder/lib/src/specs/library.g.dart
@@ -12,16 +12,12 @@
   @override
   final BuiltList<Spec> body;
 
-  factory _$Library([void Function(LibraryBuilder) updates]) =>
+  factory _$Library([void Function(LibraryBuilder)? updates]) =>
       (new LibraryBuilder()..update(updates)).build() as _$Library;
 
-  _$Library._({this.directives, this.body}) : super._() {
-    if (directives == null) {
-      throw new BuiltValueNullFieldError('Library', 'directives');
-    }
-    if (body == null) {
-      throw new BuiltValueNullFieldError('Library', 'body');
-    }
+  _$Library._({required this.directives, required this.body}) : super._() {
+    BuiltValueNullFieldError.checkNotNull(directives, 'Library', 'directives');
+    BuiltValueNullFieldError.checkNotNull(body, 'Library', 'body');
   }
 
   @override
@@ -54,12 +50,12 @@
 }
 
 class _$LibraryBuilder extends LibraryBuilder {
-  _$Library _$v;
+  _$Library? _$v;
 
   @override
   ListBuilder<Directive> get directives {
     _$this;
-    return super.directives ??= new ListBuilder<Directive>();
+    return super.directives;
   }
 
   @override
@@ -71,7 +67,7 @@
   @override
   ListBuilder<Spec> get body {
     _$this;
-    return super.body ??= new ListBuilder<Spec>();
+    return super.body;
   }
 
   @override
@@ -83,9 +79,10 @@
   _$LibraryBuilder() : super._();
 
   LibraryBuilder get _$this {
-    if (_$v != null) {
-      super.directives = _$v.directives?.toBuilder();
-      super.body = _$v.body?.toBuilder();
+    final $v = _$v;
+    if ($v != null) {
+      super.directives = $v.directives.toBuilder();
+      super.body = $v.body.toBuilder();
       _$v = null;
     }
     return this;
@@ -93,14 +90,12 @@
 
   @override
   void replace(Library other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Library;
   }
 
   @override
-  void update(void Function(LibraryBuilder) updates) {
+  void update(void Function(LibraryBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -111,7 +106,7 @@
       _$result = _$v ??
           new _$Library._(directives: directives.build(), body: body.build());
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'directives';
         directives.build();
diff --git a/code_builder/lib/src/specs/method.dart b/code_builder/lib/src/specs/method.dart
index 9be3929..68b4049 100644
--- a/code_builder/lib/src/specs/method.dart
+++ b/code_builder/lib/src/specs/method.dart
@@ -25,7 +25,7 @@
     implements Built<Method, MethodBuilder>, Spec {
   factory Method([void Function(MethodBuilder) updates]) = _$Method;
 
-  factory Method.returnsVoid([void Function(MethodBuilder) updates]) =>
+  factory Method.returnsVoid([void Function(MethodBuilder)? updates]) =>
       Method((b) {
         if (updates != null) {
           updates(b);
@@ -51,8 +51,7 @@
   BuiltList<Parameter> get requiredParameters;
 
   /// Body of the method.
-  @nullable
-  Code get body;
+  Code? get body;
 
   /// Whether the method should be prefixed with `external`.
   bool get external;
@@ -60,8 +59,7 @@
   /// Whether this method is a simple lambda expression.
   ///
   /// May be `null` to be inferred based on the value of [body].
-  @nullable
-  bool get lambda;
+  bool? get lambda;
 
   /// Whether this method should be prefixed with `static`.
   ///
@@ -71,24 +69,20 @@
   /// Name of the method or function.
   ///
   /// May be `null` when being used as a [closure].
-  @nullable
-  String get name;
+  String? get name;
 
   /// Whether this is a getter or setter.
-  @nullable
-  MethodType get type;
+  MethodType? get type;
 
   /// Whether this method is `async`, `async*`, or `sync*`.
-  @nullable
-  MethodModifier get modifier;
+  MethodModifier? get modifier;
 
-  @nullable
-  Reference get returns;
+  Reference? get returns;
 
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitMethod(this, context);
 
@@ -107,6 +101,11 @@
   MethodBuilder._();
 
   @override
+  void update(void Function(MethodBuilder)? updates) {
+    updates?.call(this);
+  }
+
+  @override
   ListBuilder<Expression> annotations = ListBuilder<Expression>();
 
   @override
@@ -122,7 +121,7 @@
   ListBuilder<Parameter> requiredParameters = ListBuilder<Parameter>();
 
   /// Body of the method.
-  Code body;
+  Code? body;
 
   /// Whether the method should be prefixed with `external`.
   bool external = false;
@@ -130,7 +129,7 @@
   /// Whether this method is a simple lambda expression.
   ///
   /// If not specified this is inferred from the [body].
-  bool lambda;
+  bool? lambda;
 
   /// Whether this method should be prefixed with `static`.
   ///
@@ -138,15 +137,15 @@
   bool static = false;
 
   /// Name of the method or function.
-  String name;
+  String? name;
 
   /// Whether this is a getter or setter.
-  MethodType type;
+  MethodType? type;
 
   /// Whether this method is `async`, `async*`, or `sync*`.
-  MethodModifier modifier;
+  MethodModifier? modifier;
 
-  Reference returns;
+  Reference? returns;
 }
 
 enum MethodType {
@@ -168,8 +167,7 @@
   Parameter._();
 
   /// If not `null`, a default assignment if the parameter is optional.
-  @nullable
-  Code get defaultTo;
+  Code? get defaultTo;
 
   /// Name of the parameter.
   String get name;
@@ -192,8 +190,7 @@
   BuiltList<Reference> get types;
 
   /// Type of the parameter;
-  @nullable
-  Reference get type;
+  Reference? get type;
 
   /// Whether this parameter should be annotated with the `required` keyword.
   ///
@@ -216,11 +213,16 @@
 
   ParameterBuilder._();
 
+  @override
+  void update(void Function(ParameterBuilder)? updates) {
+    updates?.call(this);
+  }
+
   /// If not `null`, a default assignment if the parameter is optional.
-  Code defaultTo;
+  Code? defaultTo;
 
   /// Name of the parameter.
-  String name;
+  late final String name;
 
   /// Whether this parameter should be named, if optional.
   bool named = false;
@@ -240,7 +242,7 @@
   ListBuilder<Reference> types = ListBuilder<Reference>();
 
   /// Type of the parameter;
-  Reference type;
+  Reference? type;
 
   /// Whether this parameter should be annotated with the `required` keyword.
   ///
diff --git a/code_builder/lib/src/specs/method.g.dart b/code_builder/lib/src/specs/method.g.dart
index ba721b5..c44d5f9 100644
--- a/code_builder/lib/src/specs/method.g.dart
+++ b/code_builder/lib/src/specs/method.g.dart
@@ -18,61 +18,49 @@
   @override
   final BuiltList<Parameter> requiredParameters;
   @override
-  final Code body;
+  final Code? body;
   @override
   final bool external;
   @override
-  final bool lambda;
+  final bool? lambda;
   @override
   final bool static;
   @override
-  final String name;
+  final String? name;
   @override
-  final MethodType type;
+  final MethodType? type;
   @override
-  final MethodModifier modifier;
+  final MethodModifier? modifier;
   @override
-  final Reference returns;
+  final Reference? returns;
 
-  factory _$Method([void Function(MethodBuilder) updates]) =>
+  factory _$Method([void Function(MethodBuilder)? updates]) =>
       (new MethodBuilder()..update(updates)).build() as _$Method;
 
   _$Method._(
-      {this.annotations,
-      this.docs,
-      this.types,
-      this.optionalParameters,
-      this.requiredParameters,
+      {required this.annotations,
+      required this.docs,
+      required this.types,
+      required this.optionalParameters,
+      required this.requiredParameters,
       this.body,
-      this.external,
+      required this.external,
       this.lambda,
-      this.static,
+      required this.static,
       this.name,
       this.type,
       this.modifier,
       this.returns})
       : super._() {
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Method', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Method', 'docs');
-    }
-    if (types == null) {
-      throw new BuiltValueNullFieldError('Method', 'types');
-    }
-    if (optionalParameters == null) {
-      throw new BuiltValueNullFieldError('Method', 'optionalParameters');
-    }
-    if (requiredParameters == null) {
-      throw new BuiltValueNullFieldError('Method', 'requiredParameters');
-    }
-    if (external == null) {
-      throw new BuiltValueNullFieldError('Method', 'external');
-    }
-    if (static == null) {
-      throw new BuiltValueNullFieldError('Method', 'static');
-    }
+    BuiltValueNullFieldError.checkNotNull(annotations, 'Method', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Method', 'docs');
+    BuiltValueNullFieldError.checkNotNull(types, 'Method', 'types');
+    BuiltValueNullFieldError.checkNotNull(
+        optionalParameters, 'Method', 'optionalParameters');
+    BuiltValueNullFieldError.checkNotNull(
+        requiredParameters, 'Method', 'requiredParameters');
+    BuiltValueNullFieldError.checkNotNull(external, 'Method', 'external');
+    BuiltValueNullFieldError.checkNotNull(static, 'Method', 'static');
   }
 
   @override
@@ -152,12 +140,12 @@
 }
 
 class _$MethodBuilder extends MethodBuilder {
-  _$Method _$v;
+  _$Method? _$v;
 
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -169,7 +157,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -181,7 +169,7 @@
   @override
   ListBuilder<Reference> get types {
     _$this;
-    return super.types ??= new ListBuilder<Reference>();
+    return super.types;
   }
 
   @override
@@ -193,7 +181,7 @@
   @override
   ListBuilder<Parameter> get optionalParameters {
     _$this;
-    return super.optionalParameters ??= new ListBuilder<Parameter>();
+    return super.optionalParameters;
   }
 
   @override
@@ -205,7 +193,7 @@
   @override
   ListBuilder<Parameter> get requiredParameters {
     _$this;
-    return super.requiredParameters ??= new ListBuilder<Parameter>();
+    return super.requiredParameters;
   }
 
   @override
@@ -215,13 +203,13 @@
   }
 
   @override
-  Code get body {
+  Code? get body {
     _$this;
     return super.body;
   }
 
   @override
-  set body(Code body) {
+  set body(Code? body) {
     _$this;
     super.body = body;
   }
@@ -239,13 +227,13 @@
   }
 
   @override
-  bool get lambda {
+  bool? get lambda {
     _$this;
     return super.lambda;
   }
 
   @override
-  set lambda(bool lambda) {
+  set lambda(bool? lambda) {
     _$this;
     super.lambda = lambda;
   }
@@ -263,49 +251,49 @@
   }
 
   @override
-  String get name {
+  String? get name {
     _$this;
     return super.name;
   }
 
   @override
-  set name(String name) {
+  set name(String? name) {
     _$this;
     super.name = name;
   }
 
   @override
-  MethodType get type {
+  MethodType? get type {
     _$this;
     return super.type;
   }
 
   @override
-  set type(MethodType type) {
+  set type(MethodType? type) {
     _$this;
     super.type = type;
   }
 
   @override
-  MethodModifier get modifier {
+  MethodModifier? get modifier {
     _$this;
     return super.modifier;
   }
 
   @override
-  set modifier(MethodModifier modifier) {
+  set modifier(MethodModifier? modifier) {
     _$this;
     super.modifier = modifier;
   }
 
   @override
-  Reference get returns {
+  Reference? get returns {
     _$this;
     return super.returns;
   }
 
   @override
-  set returns(Reference returns) {
+  set returns(Reference? returns) {
     _$this;
     super.returns = returns;
   }
@@ -313,20 +301,21 @@
   _$MethodBuilder() : super._();
 
   MethodBuilder get _$this {
-    if (_$v != null) {
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
-      super.types = _$v.types?.toBuilder();
-      super.optionalParameters = _$v.optionalParameters?.toBuilder();
-      super.requiredParameters = _$v.requiredParameters?.toBuilder();
-      super.body = _$v.body;
-      super.external = _$v.external;
-      super.lambda = _$v.lambda;
-      super.static = _$v.static;
-      super.name = _$v.name;
-      super.type = _$v.type;
-      super.modifier = _$v.modifier;
-      super.returns = _$v.returns;
+    final $v = _$v;
+    if ($v != null) {
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
+      super.types = $v.types.toBuilder();
+      super.optionalParameters = $v.optionalParameters.toBuilder();
+      super.requiredParameters = $v.requiredParameters.toBuilder();
+      super.body = $v.body;
+      super.external = $v.external;
+      super.lambda = $v.lambda;
+      super.static = $v.static;
+      super.name = $v.name;
+      super.type = $v.type;
+      super.modifier = $v.modifier;
+      super.returns = $v.returns;
       _$v = null;
     }
     return this;
@@ -334,14 +323,12 @@
 
   @override
   void replace(Method other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Method;
   }
 
   @override
-  void update(void Function(MethodBuilder) updates) {
+  void update(void Function(MethodBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -357,15 +344,17 @@
               optionalParameters: optionalParameters.build(),
               requiredParameters: requiredParameters.build(),
               body: body,
-              external: external,
+              external: BuiltValueNullFieldError.checkNotNull(
+                  external, 'Method', 'external'),
               lambda: lambda,
-              static: static,
+              static: BuiltValueNullFieldError.checkNotNull(
+                  static, 'Method', 'static'),
               name: name,
               type: type,
               modifier: modifier,
               returns: returns);
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
@@ -390,7 +379,7 @@
 
 class _$Parameter extends Parameter {
   @override
-  final Code defaultTo;
+  final Code? defaultTo;
   @override
   final String name;
   @override
@@ -404,51 +393,36 @@
   @override
   final BuiltList<Reference> types;
   @override
-  final Reference type;
+  final Reference? type;
   @override
   final bool required;
   @override
   final bool covariant;
 
-  factory _$Parameter([void Function(ParameterBuilder) updates]) =>
+  factory _$Parameter([void Function(ParameterBuilder)? updates]) =>
       (new ParameterBuilder()..update(updates)).build() as _$Parameter;
 
   _$Parameter._(
       {this.defaultTo,
-      this.name,
-      this.named,
-      this.toThis,
-      this.annotations,
-      this.docs,
-      this.types,
+      required this.name,
+      required this.named,
+      required this.toThis,
+      required this.annotations,
+      required this.docs,
+      required this.types,
       this.type,
-      this.required,
-      this.covariant})
+      required this.required,
+      required this.covariant})
       : super._() {
-    if (name == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'name');
-    }
-    if (named == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'named');
-    }
-    if (toThis == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'toThis');
-    }
-    if (annotations == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'annotations');
-    }
-    if (docs == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'docs');
-    }
-    if (types == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'types');
-    }
-    if (required == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'required');
-    }
-    if (covariant == null) {
-      throw new BuiltValueNullFieldError('Parameter', 'covariant');
-    }
+    BuiltValueNullFieldError.checkNotNull(name, 'Parameter', 'name');
+    BuiltValueNullFieldError.checkNotNull(named, 'Parameter', 'named');
+    BuiltValueNullFieldError.checkNotNull(toThis, 'Parameter', 'toThis');
+    BuiltValueNullFieldError.checkNotNull(
+        annotations, 'Parameter', 'annotations');
+    BuiltValueNullFieldError.checkNotNull(docs, 'Parameter', 'docs');
+    BuiltValueNullFieldError.checkNotNull(types, 'Parameter', 'types');
+    BuiltValueNullFieldError.checkNotNull(required, 'Parameter', 'required');
+    BuiltValueNullFieldError.checkNotNull(covariant, 'Parameter', 'covariant');
   }
 
   @override
@@ -514,16 +488,16 @@
 }
 
 class _$ParameterBuilder extends ParameterBuilder {
-  _$Parameter _$v;
+  _$Parameter? _$v;
 
   @override
-  Code get defaultTo {
+  Code? get defaultTo {
     _$this;
     return super.defaultTo;
   }
 
   @override
-  set defaultTo(Code defaultTo) {
+  set defaultTo(Code? defaultTo) {
     _$this;
     super.defaultTo = defaultTo;
   }
@@ -567,7 +541,7 @@
   @override
   ListBuilder<Expression> get annotations {
     _$this;
-    return super.annotations ??= new ListBuilder<Expression>();
+    return super.annotations;
   }
 
   @override
@@ -579,7 +553,7 @@
   @override
   ListBuilder<String> get docs {
     _$this;
-    return super.docs ??= new ListBuilder<String>();
+    return super.docs;
   }
 
   @override
@@ -591,7 +565,7 @@
   @override
   ListBuilder<Reference> get types {
     _$this;
-    return super.types ??= new ListBuilder<Reference>();
+    return super.types;
   }
 
   @override
@@ -601,13 +575,13 @@
   }
 
   @override
-  Reference get type {
+  Reference? get type {
     _$this;
     return super.type;
   }
 
   @override
-  set type(Reference type) {
+  set type(Reference? type) {
     _$this;
     super.type = type;
   }
@@ -639,17 +613,18 @@
   _$ParameterBuilder() : super._();
 
   ParameterBuilder get _$this {
-    if (_$v != null) {
-      super.defaultTo = _$v.defaultTo;
-      super.name = _$v.name;
-      super.named = _$v.named;
-      super.toThis = _$v.toThis;
-      super.annotations = _$v.annotations?.toBuilder();
-      super.docs = _$v.docs?.toBuilder();
-      super.types = _$v.types?.toBuilder();
-      super.type = _$v.type;
-      super.required = _$v.required;
-      super.covariant = _$v.covariant;
+    final $v = _$v;
+    if ($v != null) {
+      super.defaultTo = $v.defaultTo;
+      super.name = $v.name;
+      super.named = $v.named;
+      super.toThis = $v.toThis;
+      super.annotations = $v.annotations.toBuilder();
+      super.docs = $v.docs.toBuilder();
+      super.types = $v.types.toBuilder();
+      super.type = $v.type;
+      super.required = $v.required;
+      super.covariant = $v.covariant;
       _$v = null;
     }
     return this;
@@ -657,14 +632,12 @@
 
   @override
   void replace(Parameter other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$Parameter;
   }
 
   @override
-  void update(void Function(ParameterBuilder) updates) {
+  void update(void Function(ParameterBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -675,17 +648,22 @@
       _$result = _$v ??
           new _$Parameter._(
               defaultTo: defaultTo,
-              name: name,
-              named: named,
-              toThis: toThis,
+              name: BuiltValueNullFieldError.checkNotNull(
+                  name, 'Parameter', 'name'),
+              named: BuiltValueNullFieldError.checkNotNull(
+                  named, 'Parameter', 'named'),
+              toThis: BuiltValueNullFieldError.checkNotNull(
+                  toThis, 'Parameter', 'toThis'),
               annotations: annotations.build(),
               docs: docs.build(),
               types: types.build(),
               type: type,
-              required: required,
-              covariant: covariant);
+              required: BuiltValueNullFieldError.checkNotNull(
+                  required, 'Parameter', 'required'),
+              covariant: BuiltValueNullFieldError.checkNotNull(
+                  covariant, 'Parameter', 'covariant'));
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'annotations';
         annotations.build();
diff --git a/code_builder/lib/src/specs/reference.dart b/code_builder/lib/src/specs/reference.dart
index 355d84c..6668e81 100644
--- a/code_builder/lib/src/specs/reference.dart
+++ b/code_builder/lib/src/specs/reference.dart
@@ -14,7 +14,7 @@
 import 'type_reference.dart';
 
 /// Short-hand for `Reference(symbol, url)`.
-Reference refer(String symbol, [String url]) => Reference(symbol, url);
+Reference refer(String symbol, [String? url]) => Reference(symbol, url);
 
 /// A reference to [symbol], such as a class, or top-level method or field.
 ///
@@ -25,10 +25,13 @@
   /// Relative, `package:` or `dart:` URL of the library.
   ///
   /// May be omitted (`null`) in order to express "same library".
-  final String url;
+  final String? url;
 
   /// Name of the class, method, or field.
-  final String symbol;
+  ///
+  /// May be `null` for references without symbols, for instance a function type
+  /// has no symbol.
+  final String? symbol;
 
   /// Create a reference to [symbol] in [url].
   const Reference(this.symbol, [this.url]);
@@ -36,7 +39,7 @@
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitReference(this, context);
 
diff --git a/code_builder/lib/src/specs/type_function.dart b/code_builder/lib/src/specs/type_function.dart
index c10ca51..ecaa4cf 100644
--- a/code_builder/lib/src/specs/type_function.dart
+++ b/code_builder/lib/src/specs/type_function.dart
@@ -28,13 +28,12 @@
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitFunctionType(this, context);
 
   /// Return type.
-  @nullable
-  Reference get returnType;
+  Reference? get returnType;
 
   @override
   BuiltList<Reference> get types;
@@ -49,10 +48,10 @@
   BuiltMap<String, Reference> get namedParameters;
 
   @override
-  String get url => null;
+  String? get url => null;
 
   @override
-  String get symbol => null;
+  String? get symbol => null;
 
   @override
   Reference get type => this;
@@ -61,8 +60,7 @@
   ///
   /// An emitter may ignore this if the output is not targeting a Dart language
   /// version that supports null safety.
-  @nullable
-  bool get isNullable;
+  bool? get isNullable;
 
   @override
   Expression newInstance(
@@ -109,7 +107,7 @@
 
   FunctionTypeBuilder._();
 
-  Reference returnType;
+  Reference? returnType;
 
   @override
   ListBuilder<Reference> types = ListBuilder<Reference>();
@@ -121,5 +119,9 @@
   MapBuilder<String, Reference> namedParameters =
       MapBuilder<String, Reference>();
 
-  bool isNullable;
+  bool? isNullable;
+
+  String? url;
+
+  String? symbol;
 }
diff --git a/code_builder/lib/src/specs/type_function.g.dart b/code_builder/lib/src/specs/type_function.g.dart
index 2c9906c..c84f537 100644
--- a/code_builder/lib/src/specs/type_function.g.dart
+++ b/code_builder/lib/src/specs/type_function.g.dart
@@ -8,7 +8,7 @@
 
 class _$FunctionType extends FunctionType {
   @override
-  final Reference returnType;
+  final Reference? returnType;
   @override
   final BuiltList<Reference> types;
   @override
@@ -18,31 +18,26 @@
   @override
   final BuiltMap<String, Reference> namedParameters;
   @override
-  final bool isNullable;
+  final bool? isNullable;
 
-  factory _$FunctionType([void Function(FunctionTypeBuilder) updates]) =>
+  factory _$FunctionType([void Function(FunctionTypeBuilder)? updates]) =>
       (new FunctionTypeBuilder()..update(updates)).build() as _$FunctionType;
 
   _$FunctionType._(
       {this.returnType,
-      this.types,
-      this.requiredParameters,
-      this.optionalParameters,
-      this.namedParameters,
+      required this.types,
+      required this.requiredParameters,
+      required this.optionalParameters,
+      required this.namedParameters,
       this.isNullable})
       : super._() {
-    if (types == null) {
-      throw new BuiltValueNullFieldError('FunctionType', 'types');
-    }
-    if (requiredParameters == null) {
-      throw new BuiltValueNullFieldError('FunctionType', 'requiredParameters');
-    }
-    if (optionalParameters == null) {
-      throw new BuiltValueNullFieldError('FunctionType', 'optionalParameters');
-    }
-    if (namedParameters == null) {
-      throw new BuiltValueNullFieldError('FunctionType', 'namedParameters');
-    }
+    BuiltValueNullFieldError.checkNotNull(types, 'FunctionType', 'types');
+    BuiltValueNullFieldError.checkNotNull(
+        requiredParameters, 'FunctionType', 'requiredParameters');
+    BuiltValueNullFieldError.checkNotNull(
+        optionalParameters, 'FunctionType', 'optionalParameters');
+    BuiltValueNullFieldError.checkNotNull(
+        namedParameters, 'FunctionType', 'namedParameters');
   }
 
   @override
@@ -91,16 +86,16 @@
 }
 
 class _$FunctionTypeBuilder extends FunctionTypeBuilder {
-  _$FunctionType _$v;
+  _$FunctionType? _$v;
 
   @override
-  Reference get returnType {
+  Reference? get returnType {
     _$this;
     return super.returnType;
   }
 
   @override
-  set returnType(Reference returnType) {
+  set returnType(Reference? returnType) {
     _$this;
     super.returnType = returnType;
   }
@@ -108,7 +103,7 @@
   @override
   ListBuilder<Reference> get types {
     _$this;
-    return super.types ??= new ListBuilder<Reference>();
+    return super.types;
   }
 
   @override
@@ -120,7 +115,7 @@
   @override
   ListBuilder<Reference> get requiredParameters {
     _$this;
-    return super.requiredParameters ??= new ListBuilder<Reference>();
+    return super.requiredParameters;
   }
 
   @override
@@ -132,7 +127,7 @@
   @override
   ListBuilder<Reference> get optionalParameters {
     _$this;
-    return super.optionalParameters ??= new ListBuilder<Reference>();
+    return super.optionalParameters;
   }
 
   @override
@@ -144,7 +139,7 @@
   @override
   MapBuilder<String, Reference> get namedParameters {
     _$this;
-    return super.namedParameters ??= new MapBuilder<String, Reference>();
+    return super.namedParameters;
   }
 
   @override
@@ -154,13 +149,13 @@
   }
 
   @override
-  bool get isNullable {
+  bool? get isNullable {
     _$this;
     return super.isNullable;
   }
 
   @override
-  set isNullable(bool isNullable) {
+  set isNullable(bool? isNullable) {
     _$this;
     super.isNullable = isNullable;
   }
@@ -168,13 +163,14 @@
   _$FunctionTypeBuilder() : super._();
 
   FunctionTypeBuilder get _$this {
-    if (_$v != null) {
-      super.returnType = _$v.returnType;
-      super.types = _$v.types?.toBuilder();
-      super.requiredParameters = _$v.requiredParameters?.toBuilder();
-      super.optionalParameters = _$v.optionalParameters?.toBuilder();
-      super.namedParameters = _$v.namedParameters?.toBuilder();
-      super.isNullable = _$v.isNullable;
+    final $v = _$v;
+    if ($v != null) {
+      super.returnType = $v.returnType;
+      super.types = $v.types.toBuilder();
+      super.requiredParameters = $v.requiredParameters.toBuilder();
+      super.optionalParameters = $v.optionalParameters.toBuilder();
+      super.namedParameters = $v.namedParameters.toBuilder();
+      super.isNullable = $v.isNullable;
       _$v = null;
     }
     return this;
@@ -182,14 +178,12 @@
 
   @override
   void replace(FunctionType other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$FunctionType;
   }
 
   @override
-  void update(void Function(FunctionTypeBuilder) updates) {
+  void update(void Function(FunctionTypeBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -206,7 +200,7 @@
               namedParameters: namedParameters.build(),
               isNullable: isNullable);
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'types';
         types.build();
diff --git a/code_builder/lib/src/specs/type_reference.dart b/code_builder/lib/src/specs/type_reference.dart
index 4424598..2dd75a5 100644
--- a/code_builder/lib/src/specs/type_reference.dart
+++ b/code_builder/lib/src/specs/type_reference.dart
@@ -29,12 +29,10 @@
   String get symbol;
 
   @override
-  @nullable
-  String get url;
+  String? get url;
 
   /// Optional bound generic.
-  @nullable
-  Reference get bound;
+  Reference? get bound;
 
   @override
   BuiltList<Reference> get types;
@@ -43,13 +41,12 @@
   ///
   /// An emitter may ignore this if the output is not targeting a Dart language
   /// version that supports null safety.
-  @nullable
-  bool get isNullable;
+  bool? get isNullable;
 
   @override
   R accept<R>(
     SpecVisitor<R> visitor, [
-    R context,
+    R? context,
   ]) =>
       visitor.visitType(this, context);
 
@@ -123,12 +120,12 @@
 
   TypeReferenceBuilder._();
 
-  String symbol;
+  String? symbol;
 
-  String url;
+  String? url;
 
   /// Optional bound generic.
-  Reference bound;
+  Reference? bound;
 
   @override
   ListBuilder<Reference> types = ListBuilder<Reference>();
@@ -137,5 +134,5 @@
   ///
   /// An emitter may ignore this if the output is not targeting a Dart language
   /// version that supports null safety.
-  bool isNullable;
+  bool? isNullable;
 }
diff --git a/code_builder/lib/src/specs/type_reference.g.dart b/code_builder/lib/src/specs/type_reference.g.dart
index 315f3e5..e62cbdc 100644
--- a/code_builder/lib/src/specs/type_reference.g.dart
+++ b/code_builder/lib/src/specs/type_reference.g.dart
@@ -10,26 +10,26 @@
   @override
   final String symbol;
   @override
-  final String url;
+  final String? url;
   @override
-  final Reference bound;
+  final Reference? bound;
   @override
   final BuiltList<Reference> types;
   @override
-  final bool isNullable;
+  final bool? isNullable;
 
-  factory _$TypeReference([void Function(TypeReferenceBuilder) updates]) =>
+  factory _$TypeReference([void Function(TypeReferenceBuilder)? updates]) =>
       (new TypeReferenceBuilder()..update(updates)).build() as _$TypeReference;
 
   _$TypeReference._(
-      {this.symbol, this.url, this.bound, this.types, this.isNullable})
+      {required this.symbol,
+      this.url,
+      this.bound,
+      required this.types,
+      this.isNullable})
       : super._() {
-    if (symbol == null) {
-      throw new BuiltValueNullFieldError('TypeReference', 'symbol');
-    }
-    if (types == null) {
-      throw new BuiltValueNullFieldError('TypeReference', 'types');
-    }
+    BuiltValueNullFieldError.checkNotNull(symbol, 'TypeReference', 'symbol');
+    BuiltValueNullFieldError.checkNotNull(types, 'TypeReference', 'types');
   }
 
   @override
@@ -72,40 +72,40 @@
 }
 
 class _$TypeReferenceBuilder extends TypeReferenceBuilder {
-  _$TypeReference _$v;
+  _$TypeReference? _$v;
 
   @override
-  String get symbol {
+  String? get symbol {
     _$this;
     return super.symbol;
   }
 
   @override
-  set symbol(String symbol) {
+  set symbol(String? symbol) {
     _$this;
     super.symbol = symbol;
   }
 
   @override
-  String get url {
+  String? get url {
     _$this;
     return super.url;
   }
 
   @override
-  set url(String url) {
+  set url(String? url) {
     _$this;
     super.url = url;
   }
 
   @override
-  Reference get bound {
+  Reference? get bound {
     _$this;
     return super.bound;
   }
 
   @override
-  set bound(Reference bound) {
+  set bound(Reference? bound) {
     _$this;
     super.bound = bound;
   }
@@ -113,7 +113,7 @@
   @override
   ListBuilder<Reference> get types {
     _$this;
-    return super.types ??= new ListBuilder<Reference>();
+    return super.types;
   }
 
   @override
@@ -123,13 +123,13 @@
   }
 
   @override
-  bool get isNullable {
+  bool? get isNullable {
     _$this;
     return super.isNullable;
   }
 
   @override
-  set isNullable(bool isNullable) {
+  set isNullable(bool? isNullable) {
     _$this;
     super.isNullable = isNullable;
   }
@@ -137,12 +137,13 @@
   _$TypeReferenceBuilder() : super._();
 
   TypeReferenceBuilder get _$this {
-    if (_$v != null) {
-      super.symbol = _$v.symbol;
-      super.url = _$v.url;
-      super.bound = _$v.bound;
-      super.types = _$v.types?.toBuilder();
-      super.isNullable = _$v.isNullable;
+    final $v = _$v;
+    if ($v != null) {
+      super.symbol = $v.symbol;
+      super.url = $v.url;
+      super.bound = $v.bound;
+      super.types = $v.types.toBuilder();
+      super.isNullable = $v.isNullable;
       _$v = null;
     }
     return this;
@@ -150,14 +151,12 @@
 
   @override
   void replace(TypeReference other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$TypeReference;
   }
 
   @override
-  void update(void Function(TypeReferenceBuilder) updates) {
+  void update(void Function(TypeReferenceBuilder)? updates) {
     if (updates != null) updates(this);
   }
 
@@ -167,13 +166,14 @@
     try {
       _$result = _$v ??
           new _$TypeReference._(
-              symbol: symbol,
+              symbol: BuiltValueNullFieldError.checkNotNull(
+                  symbol, 'TypeReference', 'symbol'),
               url: url,
               bound: bound,
               types: types.build(),
               isNullable: isNullable);
     } catch (_) {
-      String _$failedField;
+      late String _$failedField;
       try {
         _$failedField = 'types';
         types.build();
diff --git a/code_builder/lib/src/visitors.dart b/code_builder/lib/src/visitors.dart
index ed2ab4d..64a4428 100644
--- a/code_builder/lib/src/visitors.dart
+++ b/code_builder/lib/src/visitors.dart
@@ -22,31 +22,31 @@
 abstract class SpecVisitor<T> {
   const SpecVisitor._();
 
-  T visitAnnotation(Expression spec, [T context]);
+  T visitAnnotation(Expression spec, [T? context]);
 
-  T visitClass(Class spec, [T context]);
+  T visitClass(Class spec, [T? context]);
 
-  T visitExtension(Extension spec, [T context]);
+  T visitExtension(Extension spec, [T? context]);
 
-  T visitEnum(Enum spec, [T context]);
+  T visitEnum(Enum spec, [T? context]);
 
-  T visitConstructor(Constructor spec, String clazz, [T context]);
+  T visitConstructor(Constructor spec, String clazz, [T? context]);
 
-  T visitDirective(Directive spec, [T context]);
+  T visitDirective(Directive spec, [T? context]);
 
-  T visitField(Field spec, [T context]);
+  T visitField(Field spec, [T? context]);
 
-  T visitLibrary(Library spec, [T context]);
+  T visitLibrary(Library spec, [T? context]);
 
-  T visitFunctionType(FunctionType spec, [T context]);
+  T visitFunctionType(FunctionType spec, [T? context]);
 
-  T visitMethod(Method spec, [T context]);
+  T visitMethod(Method spec, [T? context]);
 
-  T visitReference(Reference spec, [T context]);
+  T visitReference(Reference spec, [T? context]);
 
-  T visitSpec(Spec spec, [T context]);
+  T visitSpec(Spec spec, [T? context]);
 
-  T visitType(TypeReference spec, [T context]);
+  T visitType(TypeReference spec, [T? context]);
 
-  T visitTypeParameters(Iterable<Reference> specs, [T context]);
+  T visitTypeParameters(Iterable<Reference> specs, [T? context]);
 }
diff --git a/code_builder/pubspec.yaml b/code_builder/pubspec.yaml
index ec8525b..09d83f8 100644
--- a/code_builder/pubspec.yaml
+++ b/code_builder/pubspec.yaml
@@ -1,25 +1,28 @@
 name: code_builder
-version: 3.7.0
-
+version: 4.0.0
 description: >-
   A fluent, builder-based library for generating valid Dart code
 homepage: https://github.com/dart-lang/code_builder
 
 environment:
-  sdk: '>=2.7.0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
-  built_collection: '>=3.0.0 <6.0.0'
-  built_value: '>=7.0.0 <9.0.0'
-  collection: ^1.14.0
-  matcher: ^0.12.0
-  meta: ^1.0.5
+  built_collection: ^5.0.0
+  built_value: ^8.0.0
+  collection: ^1.15.0
+  matcher: ^0.12.10
+  meta: ^1.3.0
 
 dev_dependencies:
-  build: ^1.0.0
-  build_runner: ^1.1.0
-  built_value_generator: ^7.0.0
-  dart_style: ^1.0.0
-  pedantic: ^1.0.0
-  source_gen: ^0.9.0
-  test: ^1.3.0
+  build: ^2.0.0
+  build_runner: ^1.12.2
+  built_value_generator: ^8.0.0
+  dart_style: ^2.0.0
+  pedantic: ^1.10.0
+  source_gen: ^1.0.0
+  test: ^1.16.0
+
+dependency_overrides:
+  # Due to dependency cycle
+  build_runner: ^1.12.2
diff --git a/code_builder/tool/src/builder.dart b/code_builder/tool/src/builder.dart
index 3863325..4934282 100644
--- a/code_builder/tool/src/builder.dart
+++ b/code_builder/tool/src/builder.dart
@@ -1,6 +1,8 @@
 // Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+//
+// @dart=2.9
 
 import 'package:build/build.dart';
 import 'package:built_value_generator/built_value_generator.dart';
diff --git a/collection/BUILD.gn b/collection/BUILD.gn
index a0e9b6e..d1dd9d5 100644
--- a/collection/BUILD.gn
+++ b/collection/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for collection-1.15.0
+# This file is generated by package_importer.py for collection-1.15.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/completion/BUILD.gn b/completion/BUILD.gn
index 926d6f7..876a50d 100644
--- a/completion/BUILD.gn
+++ b/completion/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for completion-1.0.0
+# This file is generated by package_importer.py for completion-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/convert/BUILD.gn b/convert/BUILD.gn
index 0022492..e0d1a05 100644
--- a/convert/BUILD.gn
+++ b/convert/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for convert-3.0.0
+# This file is generated by package_importer.py for convert-3.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/coverage/BUILD.gn b/coverage/BUILD.gn
index 0d76202..d2ca4b1 100644
--- a/coverage/BUILD.gn
+++ b/coverage/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for coverage-1.0.3
+# This file is generated by package_importer.py for coverage-1.0.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index 69c314b..c6d3e48 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for crypto-3.0.1
+# This file is generated by package_importer.py for crypto-3.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/csslib/BUILD.gn b/csslib/BUILD.gn
index 511076c..6b1d165 100644
--- a/csslib/BUILD.gn
+++ b/csslib/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for csslib-0.17.0
+# This file is generated by package_importer.py for csslib-0.17.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/dart_style/.github/workflows/test-package.yml b/dart_style/.github/workflows/test-package.yml
index 1cd8438..9a1d2ff 100644
--- a/dart_style/.github/workflows/test-package.yml
+++ b/dart_style/.github/workflows/test-package.yml
@@ -23,9 +23,9 @@
         sdk: [dev]
     steps:
       - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v0.1
+      - uses: dart-lang/setup-dart@v1.0
         with:
-          channel: ${{ matrix.sdk }}
+          sdk: ${{ matrix.sdk }}
       - id: install
         name: Install dependencies
         run: dart pub get
@@ -50,9 +50,9 @@
         sdk: [dev]
     steps:
       - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v0.1
+      - uses: dart-lang/setup-dart@v1.0
         with:
-          channel: ${{ matrix.sdk }}
+          sdk: ${{ matrix.sdk }}
       - id: install
         name: Install dependencies
         run: dart pub get
diff --git a/dart_style/BUILD.gn b/dart_style/BUILD.gn
index 7cd6f16..428b040 100644
--- a/dart_style/BUILD.gn
+++ b/dart_style/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for dart_style-1.3.14
+# This file is generated by package_importer.py for dart_style-2.0.1
 
 import("//build/dart/dart_library.gni")
 
 dart_library("dart_style") {
   package_name = "dart_style"
 
-  language_version = "2.11"
+  language_version = "2.12"
 
   disable_analysis = true
 
diff --git a/dart_style/CHANGELOG.md b/dart_style/CHANGELOG.md
index 6e44179..51ce9e8 100644
--- a/dart_style/CHANGELOG.md
+++ b/dart_style/CHANGELOG.md
@@ -1,3 +1,13 @@
+# 2.0.1
+
+* Support triple-shift `>>>` and `>>>=` operators (#992).
+* Support non-function type aliases (#993).
+* Correct constructor initializer indentation after `required` (#1010).
+
+# 2.0.0
+
+* Migrate to null safety.
+
 # 1.3.14
 
 * Add support for generic annotations.
diff --git a/dart_style/LICENSE b/dart_style/LICENSE
index 5c60afe..000cd7b 100644
--- a/dart_style/LICENSE
+++ b/dart_style/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2014, the Dart project authors. All rights reserved.
+Copyright 2014, the Dart project authors. 
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/dart_style/bin/format.dart b/dart_style/bin/format.dart
index c3cfee8..e757c00 100644
--- a/dart_style/bin/format.dart
+++ b/dart_style/bin/format.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
 import 'dart:io';
 
 import 'package:args/args.dart';
@@ -40,7 +39,7 @@
     usageError(parser, 'Can only use --verbose with --help.');
   }
 
-  List<int> selection;
+  List<int>? selection;
   try {
     selection = parseSelection(argResults, 'preserve');
   } on FormatException catch (exception) {
@@ -145,12 +144,12 @@
 }
 
 /// Prints [error] and usage help then exits with exit code 64.
-void usageError(ArgParser parser, String error) {
+Never usageError(ArgParser parser, String error) {
   printUsage(parser, error);
   exit(64);
 }
 
-void printUsage(ArgParser parser, [String error]) {
+void printUsage(ArgParser parser, [String? error]) {
   var output = stdout;
 
   var message = 'Idiomatically format Dart source code.';
diff --git a/dart_style/example/format.dart b/dart_style/example/format.dart
index ff1a7cd..77c79dc 100644
--- a/dart_style/example/format.dart
+++ b/dart_style/example/format.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.example.format;
-
 import 'dart:io';
 import 'dart:mirrors';
 
@@ -31,7 +28,8 @@
   runFormatter(source, pageWidth, isCompilationUnit: true);
 }
 
-void runFormatter(String source, int pageWidth, {bool isCompilationUnit}) {
+void runFormatter(String source, int pageWidth,
+    {required bool isCompilationUnit}) {
   try {
     var formatter = DartFormatter(pageWidth: pageWidth);
 
@@ -88,7 +86,7 @@
     var leadingIndent = 0;
     var indentMatch = indentPattern.firstMatch(description);
     if (indentMatch != null) {
-      leadingIndent = int.parse(indentMatch[1]);
+      leadingIndent = int.parse(indentMatch[1]!);
       description = description.substring(indentMatch.end);
     }
 
diff --git a/dart_style/lib/dart_style.dart b/dart_style/lib/dart_style.dart
index bc59401..4da21a8 100644
--- a/dart_style/lib/dart_style.dart
+++ b/dart_style/lib/dart_style.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style;
-
 export 'src/dart_formatter.dart';
 export 'src/exceptions.dart';
 export 'src/style_fix.dart';
diff --git a/dart_style/lib/src/argument_list_visitor.dart b/dart_style/lib/src/argument_list_visitor.dart
index 45f8cd9..a22864f 100644
--- a/dart_style/lib/src/argument_list_visitor.dart
+++ b/dart_style/lib/src/argument_list_visitor.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style.src.argument_list_visitor;
-
 import 'dart:math' as math;
 
 import 'package:analyzer/dart/ast/ast.dart';
@@ -36,12 +33,12 @@
   /// The contiguous list of block function arguments, if any.
   ///
   /// Otherwise, this is `null`.
-  final List<Expression> _functions;
+  final List<Expression>? _functions;
 
   /// If there are block function arguments, this is the arguments after them.
   ///
   /// Otherwise, this is `null`.
-  final ArgumentSublist _argumentsAfterFunctions;
+  final ArgumentSublist? _argumentsAfterFunctions;
 
   /// Returns `true` if there is only a single positional argument.
   bool get _isSingle =>
@@ -129,16 +126,17 @@
       }
 
       for (var i = 0; i < functionsStart; i++) {
-        if (arguments[i] is! NamedExpression) continue;
+        var argument = arguments[i];
+        if (argument is! NamedExpression) continue;
 
-        if (isArrow(arguments[i])) {
+        if (isArrow(argument)) {
           functionsStart = null;
           break;
         }
       }
 
       for (var i = functionsEnd; i < arguments.length; i++) {
-        if (isArrow(arguments[i])) {
+        if (isArrow(arguments[i] as NamedExpression)) {
           functionsStart = null;
           break;
         }
@@ -174,7 +172,10 @@
       this._allArguments,
       this._arguments,
       this._functions,
-      this._argumentsAfterFunctions);
+      this._argumentsAfterFunctions) {
+    assert(_functions == null || _argumentsAfterFunctions != null,
+        'If _functions is passed, _argumentsAfterFunctions must be too.');
+  }
 
   /// Builds chunks for the argument list.
   void visit() {
@@ -189,20 +190,21 @@
 
     _visitor.builder.endSpan();
 
-    if (_functions != null) {
+    var functions = _functions;
+    if (functions != null) {
       // TODO(rnystrom): It might look better to treat the parameter list of the
       // first function as if it were an argument in the preceding argument list
       // instead of just having this little solo split here. That would try to
       // keep the parameter list with other arguments when possible, and, I
       // think, generally look nicer.
-      if (_functions.first == _allArguments.first) {
+      if (functions.first == _allArguments.first) {
         _visitor.soloZeroSplit();
       } else {
         _visitor.soloSplit();
       }
 
-      for (var argument in _functions) {
-        if (argument != _functions.first) _visitor.space();
+      for (var argument in functions) {
+        if (argument != functions.first) _visitor.space();
 
         _visitor.visit(argument);
 
@@ -213,7 +215,7 @@
       }
 
       _visitor.builder.startSpan();
-      _argumentsAfterFunctions.visit(_visitor);
+      _argumentsAfterFunctions!.visit(_visitor);
       _visitor.builder.endSpan();
     }
 
@@ -225,9 +227,7 @@
   /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty
   /// block body.
   static bool _isBlockFunction(Expression expression) {
-    if (expression is NamedExpression) {
-      expression = (expression as NamedExpression).expression;
-    }
+    if (expression is NamedExpression) expression = expression.expression;
 
     // Allow functions wrapped in dotted method calls like "a.b.c(() { ... })".
     if (expression is MethodInvocation) {
@@ -245,39 +245,37 @@
 
     // Allow immediately-invoked functions like "() { ... }()".
     if (expression is FunctionExpressionInvocation) {
-      var invocation = expression as FunctionExpressionInvocation;
-      if (invocation.argumentList.arguments.isNotEmpty) return false;
+      if (expression.argumentList.arguments.isNotEmpty) return false;
 
-      expression = invocation.function;
+      expression = expression.function;
     }
 
     // Unwrap parenthesized expressions.
     while (expression is ParenthesizedExpression) {
-      expression = (expression as ParenthesizedExpression).expression;
+      expression = expression.expression;
     }
 
     // Must be a function.
     if (expression is! FunctionExpression) return false;
 
     // With a curly body.
-    var function = expression as FunctionExpression;
-    if (function.body is! BlockFunctionBody) return false;
+    if (expression.body is! BlockFunctionBody) return false;
 
     // That isn't empty.
-    var body = function.body as BlockFunctionBody;
+    var body = expression.body as BlockFunctionBody;
     return body.block.statements.isNotEmpty ||
         body.block.rightBracket.precedingComments != null;
   }
 
   /// Returns `true` if [expression] is a valid method invocation target for
   /// an invocation that wraps a function literal argument.
-  static bool _isValidWrappingTarget(Expression expression) {
+  static bool _isValidWrappingTarget(Expression? expression) {
     // Allow bare function calls.
     if (expression == null) return true;
 
     // Allow property accesses.
     while (expression is PropertyAccess) {
-      expression = (expression as PropertyAccess).target;
+      expression = expression.target;
     }
 
     if (expression is PrefixedIdentifier) return true;
@@ -318,12 +316,12 @@
   final int _trailingBlocks;
 
   /// The rule used to split the bodies of all block arguments.
-  Rule get blockRule => _blockRule;
-  Rule _blockRule;
+  Rule? get blockRule => _blockRule;
+  Rule? _blockRule;
 
   /// The most recent chunk that split before an argument.
-  Chunk get previousSplit => _previousSplit;
-  Chunk _previousSplit;
+  Chunk? get previousSplit => _previousSplit;
+  Chunk? _previousSplit;
 
   factory ArgumentSublist(
       List<Expression> allArguments, List<Expression> arguments) {
@@ -381,7 +379,7 @@
   }
 
   /// Writes the positional arguments, if any.
-  PositionalRule _visitPositional(SourceVisitor visitor) {
+  PositionalRule? _visitPositional(SourceVisitor visitor) {
     if (_positional.isEmpty) return null;
 
     // Allow splitting after "(".
@@ -395,7 +393,7 @@
   }
 
   /// Writes the named arguments, if any.
-  void _visitNamed(SourceVisitor visitor, PositionalRule positionalRule) {
+  void _visitNamed(SourceVisitor visitor, PositionalRule? positionalRule) {
     if (_named.isEmpty) return;
 
     // Only count the blocks in the named rule.
@@ -443,11 +441,12 @@
   void _visitArgument(
       SourceVisitor visitor, ArgumentRule rule, Expression argument) {
     // If we're about to write a block argument, handle it specially.
-    if (_blocks.containsKey(argument)) {
+    var argumentBlock = _blocks[argument];
+    if (argumentBlock != null) {
       rule.disableSplitOnInnerRules();
 
       // Tell it to use the rule we've already created.
-      visitor.beforeBlock(_blocks[argument], blockRule, previousSplit);
+      visitor.beforeBlock(argumentBlock, blockRule!, previousSplit);
     } else if (_allArguments.length > 1) {
       // Edge case: Only bump the nesting if there are multiple arguments. This
       // lets us avoid spurious indentation in cases like:
@@ -471,7 +470,7 @@
       visitor.visit(argument);
     }
 
-    if (_blocks.containsKey(argument)) {
+    if (argumentBlock != null) {
       rule.enableSplitOnInnerRules();
     } else if (_allArguments.length > 1) {
       visitor.builder.endBlockArgumentNesting();
@@ -490,9 +489,9 @@
   ///
   /// Block-formatted arguments can get special indentation to make them look
   /// more statement-like.
-  static Token _blockToken(Expression expression) {
+  static Token? _blockToken(Expression expression) {
     if (expression is NamedExpression) {
-      expression = (expression as NamedExpression).expression;
+      expression = expression.expression;
     }
 
     // TODO(rnystrom): Should we step into parenthesized expressions?
diff --git a/dart_style/lib/src/call_chain_visitor.dart b/dart_style/lib/src/call_chain_visitor.dart
index 80b1b51..4151cb7 100644
--- a/dart_style/lib/src/call_chain_visitor.dart
+++ b/dart_style/lib/src/call_chain_visitor.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style.src.call_chain_visitor;
-
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 
@@ -81,7 +78,7 @@
   ///         })
   ///         .d()
   ///         .e();
-  final List<_MethodSelector> _blockCalls;
+  final List<_MethodSelector>? _blockCalls;
 
   /// If there is one or more block calls and a single chained expression after
   /// that, this will be that expression.
@@ -96,7 +93,7 @@
   /// need to split before its `.` and this accommodates the common pattern of
   /// a trailing `toList()` or `toSet()` after a series of higher-order methods
   /// on an iterable.
-  final _Selector _hangingCall;
+  final _Selector? _hangingCall;
 
   /// Whether or not a [Rule] is currently active for the call chain.
   bool _ruleEnabled = false;
@@ -106,7 +103,7 @@
 
   /// After the properties are visited (if there are any), this will be the
   /// rule used to split between them.
-  PositionalRule _propertyRule;
+  PositionalRule? _propertyRule;
 
   /// Creates a new call chain visitor for [visitor] for the method chain
   /// contained in [node].
@@ -135,15 +132,15 @@
     calls.removeRange(0, properties.length);
 
     // Separate out the block calls, if there are any.
-    List<_MethodSelector> blockCalls;
-    _Selector hangingCall;
+    List<_MethodSelector>? blockCalls;
+    _Selector? hangingCall;
 
     var inBlockCalls = false;
     for (var call in calls) {
       if (call.isBlockCall(visitor)) {
         inBlockCalls = true;
         blockCalls ??= [];
-        blockCalls.add(call);
+        blockCalls.add(call as _MethodSelector);
       } else if (inBlockCalls) {
         // We found a non-block call after a block call.
         if (call == calls.last) {
@@ -181,7 +178,7 @@
   /// created for the call chain and the caller must end it. Used by cascades
   /// to force a cascade after a method chain to be more deeply nested than
   /// the methods.
-  void visit({bool unnest}) {
+  void visit({bool? unnest}) {
     unnest ??= true;
 
     _visitor.builder.nestExpression();
@@ -216,7 +213,7 @@
       }
 
       for (var property in _properties) {
-        _propertyRule.beforeArgument(_visitor.zeroSplit());
+        _propertyRule!.beforeArgument(_visitor.zeroSplit());
         property.write(this);
       }
 
@@ -245,12 +242,13 @@
 
     // If there are block calls, end the chain and write those without any
     // extra indentation.
-    if (_blockCalls != null) {
+    var blockCalls = _blockCalls;
+    if (blockCalls != null) {
       _enableRule();
       _visitor.zeroSplit();
       _disableRule();
 
-      for (var blockCall in _blockCalls) {
+      for (var blockCall in blockCalls) {
         blockCall.write(this);
       }
 
@@ -289,7 +287,7 @@
 
     // Unwrap parentheses.
     while (expression is ParenthesizedExpression) {
-      expression = (expression as ParenthesizedExpression).expression;
+      expression = expression.expression;
     }
 
     // Don't split right after a collection literal.
@@ -305,7 +303,7 @@
 
     // If the expression ends in an argument list, base the splitting on the
     // last argument.
-    ArgumentList argumentList;
+    ArgumentList? argumentList;
     if (expression is MethodInvocation) {
       argumentList = expression.argumentList;
     } else if (expression is InstanceCreationExpression) {
@@ -324,7 +322,7 @@
     if (_visitor.hasCommaAfter(argument)) return false;
 
     if (argument is NamedExpression) {
-      argument = (argument as NamedExpression).expression;
+      argument = argument.expression;
     }
 
     // TODO(rnystrom): This logic is similar (but not identical) to
@@ -386,7 +384,7 @@
 
     // If the properties split, force the calls to split too.
     var rule = Rule();
-    if (_propertyRule != null) _propertyRule.setNamedArgsRule(rule);
+    _propertyRule?.setNamedArgsRule(rule);
 
     if (lazy) {
       _visitor.builder.startLazyRule(rule);
@@ -548,11 +546,11 @@
 
   // Selectors.
   if (node is MethodInvocation && node.target != null) {
-    return _unwrapSelector(node.target, _MethodSelector(node), calls);
+    return _unwrapSelector(node.target!, _MethodSelector(node), calls);
   }
 
   if (node is PropertyAccess && node.target != null) {
-    return _unwrapSelector(node.target, _PropertySelector(node), calls);
+    return _unwrapSelector(node.target!, _PropertySelector(node), calls);
   }
 
   if (node is PrefixedIdentifier) {
@@ -560,8 +558,8 @@
   }
 
   // Postfix expressions.
-  if (node is IndexExpression) {
-    return _unwrapPostfix(node, node.target, calls);
+  if (node is IndexExpression && node.target != null) {
+    return _unwrapPostfix(node, node.target!, calls);
   }
 
   if (node is FunctionExpressionInvocation) {
diff --git a/dart_style/lib/src/chunk.dart b/dart_style/lib/src/chunk.dart
index 30a6d92..90d5636 100644
--- a/dart_style/lib/src/chunk.dart
+++ b/dart_style/lib/src/chunk.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style.src.chunk;
-
 import 'fast_hash.dart';
 import 'nesting_level.dart';
 import 'rule/rule.dart';
@@ -16,13 +13,13 @@
 
   /// The offset from the beginning of [text] where the selection starts, or
   /// `null` if the selection does not start within this chunk.
-  int get selectionStart => _selectionStart;
-  int _selectionStart;
+  int? get selectionStart => _selectionStart;
+  int? _selectionStart;
 
   /// The offset from the beginning of [text] where the selection ends, or
   /// `null` if the selection does not start within this chunk.
-  int get selectionEnd => _selectionEnd;
-  int _selectionEnd;
+  int? get selectionEnd => _selectionEnd;
+  int? _selectionEnd;
 
   /// Sets [selectionStart] to be [start] characters into [text].
   void startSelection(int start) {
@@ -77,8 +74,8 @@
   ///
   /// For top level chunks that are not inside any block, this also includes
   /// leading indentation.
-  int get indent => _indent;
-  int _indent;
+  int? get indent => _indent;
+  int? _indent;
 
   /// The expression nesting level following this chunk.
   ///
@@ -90,13 +87,15 @@
   ///     someFunctionName(argument, argument,
   ///         argument, anotherFunction(argument,
   ///             argument));
-  NestingLevel get nesting => _nesting;
-  NestingLevel _nesting;
+  NestingLevel? get nesting => _nesting;
+  NestingLevel? _nesting;
 
   /// If this chunk marks the beginning of a block, this contains the child
   /// chunks and other data about that nested block.
-  ChunkBlock get block => _block;
-  ChunkBlock _block;
+  ///
+  /// This should only be accessed when [isBlock] is `true`.
+  ChunkBlock get block => _block!;
+  ChunkBlock? _block;
 
   /// Whether this chunk has a [block].
   bool get isBlock => _block != null;
@@ -112,8 +111,8 @@
   /// The [Rule] that controls when a split should occur after this chunk.
   ///
   /// Multiple splits may share a [Rule].
-  Rule get rule => _rule;
-  Rule _rule;
+  Rule? get rule => _rule;
+  Rule? _rule;
 
   /// Whether or not an extra blank line should be output after this chunk if
   /// it's split.
@@ -125,7 +124,7 @@
   /// However, this getter does not expose that. It will return `false` if the
   /// chunk is still indeterminate.
   bool get isDouble => _isDouble ?? false;
-  bool _isDouble;
+  bool? _isDouble;
 
   /// If `true`, then the line after this chunk should always be at column
   /// zero regardless of any indentation or expression nesting.
@@ -139,7 +138,7 @@
   bool get flushLeftAfter {
     if (!isBlock) return _flushLeft;
 
-    return _block.chunks.last.flushLeftAfter;
+    return block.chunks.last.flushLeftAfter;
   }
 
   /// Whether this chunk should append an extra space if it does not split.
@@ -150,13 +149,10 @@
 
   /// Whether this chunk marks the end of a range of chunks that can be line
   /// split independently of the following chunks.
-  bool get canDivide {
-    // Have to call markDivide() before accessing this.
-    assert(_canDivide != null);
-    return _canDivide;
-  }
-
-  bool _canDivide;
+  ///
+  /// You must call markDivide() before accessing this.
+  bool get canDivide => _canDivide;
+  late final bool _canDivide;
 
   /// The number of characters in this chunk when unsplit.
   int get length => _text.length + (spaceWhenUnsplit ? 1 : 0);
@@ -166,10 +162,10 @@
   /// Does not include this chunk's own length, just the length of its child
   /// block chunks (recursively).
   int get unsplitBlockLength {
-    if (_block == null) return 0;
+    if (!isBlock) return 0;
 
     var length = 0;
-    for (var chunk in _block.chunks) {
+    for (var chunk in block.chunks) {
       length += chunk.length + chunk.unsplitBlockLength;
     }
 
@@ -182,6 +178,12 @@
   /// Creates a new chunk starting with [_text].
   Chunk(this._text);
 
+  /// Creates a dummy chunk.
+  ///
+  /// This is returned in some places by [ChunkBuilder] when there is no useful
+  /// chunk to yield and it will not end up being used by the caller anyway.
+  Chunk.dummy() : _text = '(dummy)';
+
   /// Discard the split for the chunk and put it back into the state where more
   /// text can be appended.
   void allowText() {
@@ -201,7 +203,7 @@
   /// preserved whitespace often overlap. When that happens, this has logic to
   /// combine that information into a single split.
   void applySplit(Rule rule, int indent, NestingLevel nesting,
-      {bool flushLeft, bool isDouble, bool space}) {
+      {bool? flushLeft, bool? isDouble, bool? space}) {
     flushLeft ??= false;
     space ??= false;
     if (rule.isHardened) {
@@ -223,7 +225,7 @@
   }
 
   /// Turns this chunk into one that can contain a block of child chunks.
-  void makeBlock(Chunk blockArgument) {
+  void makeBlock(Chunk? blockArgument) {
     assert(_block == null);
     _block = ChunkBlock(blockArgument);
   }
@@ -231,18 +233,23 @@
   /// Returns `true` if the block body owned by this chunk should be expression
   /// indented given a set of rule values provided by [getValue].
   bool indentBlock(int Function(Rule) getValue) {
-    if (_block == null) return false;
-    if (_block.argument == null) return false;
+    if (!isBlock) return false;
 
-    return _block.argument.rule
-        .isSplit(getValue(_block.argument.rule), _block.argument);
+    var argument = block.argument;
+    if (argument == null) return false;
+
+    var rule = argument.rule;
+
+    // There may be no rule if the block occurs inside a string interpolation.
+    // In that case, it's not clear if anything will look particularly nice, but
+    // expression nesting is probably marginally better.
+    if (rule == null) return true;
+
+    return rule.isSplit(getValue(rule), argument);
   }
 
   // Mark whether this chunk can divide the range of chunks.
-  void markDivide(canDivide) {
-    // Should only do this once.
-    assert(_canDivide == null);
-
+  void markDivide(bool canDivide) {
     _canDivide = canDivide;
   }
 
@@ -257,14 +264,15 @@
     if (_isDouble == true) parts.add('double');
     if (_flushLeft == true) parts.add('flush');
 
-    if (_rule == null) {
+    var rule = _rule;
+    if (rule == null) {
       parts.add('(no split)');
     } else {
       parts.add(rule.toString());
       if (rule.isHardened) parts.add('(hard)');
 
-      if (_rule.constrainedRules.isNotEmpty) {
-        parts.add("-> ${_rule.constrainedRules.join(' ')}");
+      if (rule.constrainedRules.isNotEmpty) {
+        parts.add("-> ${rule.constrainedRules.join(' ')}");
       }
     }
 
@@ -280,7 +288,7 @@
   ///
   /// That chunk is owned by the argument list and if it splits, this collection
   /// may need extra expression-level indentation.
-  final Chunk argument;
+  final Chunk? argument;
 
   /// The child chunks in this block.
   final List<Chunk> chunks = [];
@@ -403,5 +411,5 @@
   bool get isInline => linesBefore == 0 && !isLineComment;
 
   SourceComment(this.text, this.linesBefore,
-      {this.isLineComment, this.flushLeft});
+      {required this.isLineComment, required this.flushLeft});
 }
diff --git a/dart_style/lib/src/chunk_builder.dart b/dart_style/lib/src/chunk_builder.dart
index ec1c589..c2ab67b 100644
--- a/dart_style/lib/src/chunk_builder.dart
+++ b/dart_style/lib/src/chunk_builder.dart
@@ -39,7 +39,7 @@
 
   /// The builder for the code surrounding the block that this writer is for, or
   /// `null` if this is writing the top-level code.
-  final ChunkBuilder _parent;
+  final ChunkBuilder? _parent;
 
   final SourceCode _source;
 
@@ -181,7 +181,7 @@
   /// If [nest] is `false`, ignores any current expression nesting. Otherwise,
   /// uses the current nesting level. If unsplit, it expands to a space if
   /// [space] is `true`.
-  Chunk split({bool flushLeft, bool isDouble, bool nest, bool space}) {
+  Chunk split({bool? flushLeft, bool? isDouble, bool? nest, bool? space}) {
     space ??= false;
 
     // If we are not allowed to split at all, don't. Returning null for the
@@ -189,7 +189,7 @@
     // discarded because no chunk references it.
     if (_preventSplitNesting > 0) {
       if (space) _pendingWhitespace = Whitespace.space;
-      return null;
+      return Chunk.dummy();
     }
 
     return _writeSplit(_rules.last,
@@ -243,9 +243,9 @@
     //     new // b
     //     Foo();
     //
-    // When that happens, we need to make sure the preserve the split at the
-    // end of the first sequence of comments if there is one.
-    if (_pendingWhitespace == null) {
+    // When that happens, we need to make sure to preserve the split at the end
+    // of the first sequence of comments if there is one.
+    if (_pendingWhitespace == Whitespace.afterHardSplit) {
       comments.first.linesBefore = 1;
       _pendingWhitespace = Whitespace.none;
     }
@@ -305,11 +305,11 @@
       _writeCommentText(comment);
 
       if (comment.selectionStart != null) {
-        startSelectionFromEnd(comment.text.length - comment.selectionStart);
+        startSelectionFromEnd(comment.text.length - comment.selectionStart!);
       }
 
       if (comment.selectionEnd != null) {
-        endSelectionFromEnd(comment.text.length - comment.selectionEnd);
+        endSelectionFromEnd(comment.text.length - comment.selectionEnd!);
       }
 
       // Make sure there is at least one newline after a line comment and allow
@@ -359,7 +359,7 @@
       return;
     }
 
-    var lines = match.group(1).split('\n').toList();
+    var lines = match[1]!.split('\n').toList();
     var leastIndentation = comment.text.length;
 
     for (var i = 0; i < lines.length; i++) {
@@ -370,13 +370,13 @@
       if (i > 0 && i < lines.length - 1) {
         var match = _javaDocLine.firstMatch(line);
         if (match != null) {
-          line = match.group(1);
+          line = match[1]!;
         }
       }
 
       // Find the line with the least indentation.
       if (line.isNotEmpty) {
-        var indentation = _leadingIndentation.firstMatch(line).group(1).length;
+        var indentation = _leadingIndentation.firstMatch(line)![1]!.length;
         leastIndentation = math.min(leastIndentation, indentation);
       }
 
@@ -440,7 +440,7 @@
   /// Creates a new indentation level [spaces] deeper than the current one.
   ///
   /// If omitted, [spaces] defaults to [Indent.block].
-  void indent([int spaces]) {
+  void indent([int? spaces]) {
     _nesting.indent(spaces);
   }
 
@@ -468,14 +468,14 @@
     var span = Span(openSpan.cost);
     for (var i = openSpan.start; i < end; i++) {
       var chunk = _chunks[i];
-      if (!chunk.rule.isHardened) chunk.spans.add(span);
+      if (!chunk.rule!.isHardened) chunk.spans.add(span);
     }
   }
 
   /// Starts a new [Rule].
   ///
   /// If omitted, defaults to a new [Rule].
-  void startRule([Rule rule]) {
+  void startRule([Rule? rule]) {
     rule ??= Rule();
 
     // If there are any pending lazy rules, start them now so that the proper
@@ -503,7 +503,7 @@
   /// entire expression.
   ///
   /// If [rule] is omitted, defaults to a new [Rule].
-  void startLazyRule([Rule rule]) {
+  void startLazyRule([Rule? rule]) {
     rule ??= Rule();
 
     _lazyRules.add(rule);
@@ -536,7 +536,7 @@
   /// If [indent] is omitted, defaults to [Indent.expression]. If [now] is
   /// `true`, commits the nesting change immediately instead of waiting until
   /// after the next chunk of text is written.
-  void nestExpression({int indent, bool now}) {
+  void nestExpression({int? indent, bool? now}) {
     now ??= false;
 
     _nesting.nest(indent);
@@ -550,7 +550,7 @@
   ///
   /// If [now] is `false`, does not commit the nesting change until after the
   /// next chunk of text is written.
-  void unnest({bool now}) {
+  void unnest({bool? now}) {
     now ??= true;
 
     _nesting.unnest();
@@ -591,7 +591,7 @@
   /// Starts a new block as a child of the current chunk.
   ///
   /// Nested blocks are handled using their own independent [LineWriter].
-  ChunkBuilder startBlock(Chunk argumentChunk) {
+  ChunkBuilder startBlock(Chunk? argumentChunk) {
     var chunk = _chunks.last;
     chunk.makeBlock(argumentChunk);
 
@@ -612,7 +612,7 @@
   /// `true`, the block is considered to always split.
   ///
   /// Returns the previous writer for the surrounding block.
-  ChunkBuilder endBlock(Rule ignoredSplit, {bool forceSplit}) {
+  ChunkBuilder endBlock(Rule? ignoredSplit, {required bool forceSplit}) {
     _divideChunks();
 
     // If we don't already know if the block is going to split, see if it
@@ -627,7 +627,7 @@
         }
 
         if (chunk.rule != null &&
-            chunk.rule.isHardened &&
+            chunk.rule!.isHardened &&
             chunk.rule != ignoredSplit) {
           forceSplit = true;
           break;
@@ -635,14 +635,13 @@
       }
     }
 
-    _parent._endChildBlock(
+    _parent!._endChildBlock(
         firstFlushLeft: _firstFlushLeft, forceSplit: forceSplit);
-
-    return _parent;
+    return _parent!;
   }
 
   /// Finishes off the last chunk in a child block of this parent.
-  void _endChildBlock({bool firstFlushLeft, bool forceSplit}) {
+  void _endChildBlock({bool? firstFlushLeft, required bool forceSplit}) {
     // If there is a hard newline within the block, force the surrounding rule
     // for it so that we apply that constraint.
     if (forceSplit) forceRules();
@@ -652,7 +651,7 @@
     chunk.applySplit(rule, _nesting.indentation, _blockArgumentNesting.last,
         flushLeft: firstFlushLeft);
 
-    if (chunk.rule.isHardened) _handleHardSplit();
+    if (chunk.rule!.isHardened) _handleHardSplit();
   }
 
   /// Finishes writing and returns a [SourceCode] containing the final output
@@ -838,9 +837,9 @@
   /// If [flushLeft] is `true`, then the split will always cause the next line
   /// to be at column zero. Otherwise, it uses the normal indentation and
   /// nesting behavior.
-  void _writeHardSplit({bool isDouble, bool flushLeft, bool nest = false}) {
+  void _writeHardSplit({bool? isDouble, bool? flushLeft, bool nest = false}) {
     // A hard split overrides any other whitespace.
-    _pendingWhitespace = null;
+    _pendingWhitespace = Whitespace.afterHardSplit;
     _writeSplit(Rule.hard(),
         flushLeft: flushLeft, isDouble: isDouble, nest: nest);
   }
@@ -849,21 +848,20 @@
   ///
   /// Returns the chunk.
   Chunk _writeSplit(Rule rule,
-      {bool flushLeft, bool isDouble, bool nest, bool space}) {
+      {bool? flushLeft, bool? isDouble, bool? nest, bool? space}) {
     nest ??= true;
     space ??= false;
 
     if (_chunks.isEmpty) {
       if (flushLeft != null) _firstFlushLeft = flushLeft;
-
-      return null;
+      return Chunk.dummy();
     }
 
     _chunks.last.applySplit(
         rule, _nesting.indentation, nest ? _nesting.nesting : NestingLevel(),
         flushLeft: flushLeft, isDouble: isDouble, space: space);
 
-    if (_chunks.last.rule.isHardened) _handleHardSplit();
+    if (_chunks.last.rule!.isHardened) _handleHardSplit();
     return _chunks.last;
   }
 
@@ -884,8 +882,8 @@
     if (i == _chunks.length - 1) return false;
 
     var chunk = _chunks[i];
-    if (!chunk.rule.isHardened) return false;
-    if (chunk.nesting.isNested) return false;
+    if (!chunk.rule!.isHardened) return false;
+    if (chunk.nesting!.isNested) return false;
     if (chunk.isBlock) return false;
 
     return true;
@@ -950,7 +948,7 @@
     // Discard spans in hardened chunks since we know for certain they will
     // split anyway.
     for (var chunk in _chunks) {
-      if (chunk.rule != null && chunk.rule.isHardened) {
+      if (chunk.rule != null && chunk.rule!.isHardened) {
         chunk.spans.clear();
       }
     }
diff --git a/dart_style/lib/src/cli/format_command.dart b/dart_style/lib/src/cli/format_command.dart
index 5b14a7e..6f6627e 100644
--- a/dart_style/lib/src/cli/format_command.dart
+++ b/dart_style/lib/src/cli/format_command.dart
@@ -22,7 +22,7 @@
 
   @override
   String get invocation =>
-      '${runner.executableName} $name [options...] <files or directories...>';
+      '${runner!.executableName} $name [options...] <files or directories...>';
 
   FormatCommand({bool verbose = false}) {
     defineOptions(argParser, oldCli: false, verbose: verbose);
@@ -30,6 +30,8 @@
 
   @override
   Future<int> run() async {
+    var argResults = this.argResults!;
+
     if (argResults['version']) {
       print(dartStyleVersion);
       return 0;
@@ -39,14 +41,14 @@
       'all': Show.all,
       'changed': Show.changed,
       'none': Show.none
-    }[argResults['show']];
+    }[argResults['show']]!;
 
     var output = const {
       'write': Output.write,
       'show': Output.show,
       'none': Output.none,
       'json': Output.json,
-    }[argResults['output']];
+    }[argResults['output']]!;
 
     var summary = Summary.none;
     switch (argResults['summary'] as String) {
@@ -114,7 +116,7 @@
       }
     }
 
-    List<int> selection;
+    List<int>? selection;
     try {
       selection = parseSelection(argResults, 'selection');
     } on FormatException catch (exception) {
diff --git a/dart_style/lib/src/cli/formatter_options.dart b/dart_style/lib/src/cli/formatter_options.dart
index d2972a8..a60cc7f 100644
--- a/dart_style/lib/src/cli/formatter_options.dart
+++ b/dart_style/lib/src/cli/formatter_options.dart
@@ -13,7 +13,7 @@
 import 'summary.dart';
 
 // Note: The following line of code is modified by tool/grind.dart.
-const dartStyleVersion = '1.3.14';
+const dartStyleVersion = '2.0.1';
 
 /// Global options that affect how the formatter produces and uses its outputs.
 class FormatterOptions {
@@ -28,7 +28,7 @@
   final bool followLinks;
 
   /// The style fixes to apply while formatting.
-  final Iterable<StyleFix> fixes;
+  final List<StyleFix> fixes;
 
   /// Which affected files should be shown.
   final Show show;
@@ -45,14 +45,17 @@
       {this.indent = 0,
       this.pageWidth = 80,
       this.followLinks = false,
-      this.fixes,
+      Iterable<StyleFix>? fixes,
       this.show = Show.changed,
       this.output = Output.write,
       this.summary = Summary.none,
-      this.setExitIfChanged = false});
+      this.setExitIfChanged = false})
+      : fixes = [...?fixes];
 
   /// Called when [file] is about to be formatted.
-  void beforeFile(File file, String label) {
+  ///
+  /// If stdin is being formatted, then [file] is `null`.
+  void beforeFile(File? file, String label) {
     summary.beforeFile(file, label);
   }
 
@@ -60,8 +63,10 @@
   ///
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
-  void afterFile(File file, String displayPath, SourceCode result,
-      {bool changed}) {
+  ///
+  /// If stdin is being formatted, then [file] is `null`.
+  void afterFile(File? file, String displayPath, SourceCode result,
+      {required bool changed}) {
     summary.afterFile(this, file, displayPath, result, changed: changed);
 
     // Save the results to disc.
diff --git a/dart_style/lib/src/cli/options.dart b/dart_style/lib/src/cli/options.dart
index 30ebcc8..94f999f 100644
--- a/dart_style/lib/src/cli/options.dart
+++ b/dart_style/lib/src/cli/options.dart
@@ -124,7 +124,7 @@
   }
 }
 
-List<int> parseSelection(ArgResults argResults, String optionName) {
+List<int>? parseSelection(ArgResults argResults, String optionName) {
   var option = argResults[optionName];
   if (option == null) return null;
 
diff --git a/dart_style/lib/src/cli/output.dart b/dart_style/lib/src/cli/output.dart
index da8d241..f7d9bd7 100644
--- a/dart_style/lib/src/cli/output.dart
+++ b/dart_style/lib/src/cli/output.dart
@@ -24,7 +24,9 @@
   const Output._();
 
   /// Write the file to disc.
-  bool writeFile(File file, String displayPath, SourceCode result) => false;
+  ///
+  /// If stdin is being formatted, then [file] is `null`.
+  bool writeFile(File? file, String displayPath, SourceCode result) => false;
 
   /// Print the file to the terminal in some way.
   void showFile(String path, SourceCode result) {}
@@ -34,12 +36,12 @@
   const _WriteOutput() : super._();
 
   @override
-  bool writeFile(File file, String displayPath, SourceCode result) {
+  bool writeFile(File? file, String displayPath, SourceCode result) {
     try {
-      file.writeAsStringSync(result.text);
+      file!.writeAsStringSync(result.text);
     } on FileSystemException catch (err) {
       stderr.writeln('Could not overwrite $displayPath: '
-          '${err.osError.message} (error code ${err.osError.errorCode})');
+          '${err.osError!.message} (error code ${err.osError!.errorCode})');
     }
 
     return true;
diff --git a/dart_style/lib/src/cli/show.dart b/dart_style/lib/src/cli/show.dart
index 810759f..e3a8863 100644
--- a/dart_style/lib/src/cli/show.dart
+++ b/dart_style/lib/src/cli/show.dart
@@ -36,7 +36,8 @@
   /// Describes a file that was processed.
   ///
   /// Returns whether or not this file should be displayed.
-  bool file(String path, {bool changed, bool overwritten}) => true;
+  bool file(String path, {required bool changed, required bool overwritten}) =>
+      true;
 
   /// Describes the directory whose contents are about to be processed.
   void directory(String path) {}
@@ -47,7 +48,7 @@
   /// Describes the hidden [path] that wasn't processed.
   void hiddenPath(String path) {}
 
-  void _showFileChange(String path, {bool overwritten}) {
+  void _showFileChange(String path, {required bool overwritten}) {
     if (overwritten) {
       print('Formatted $path');
     } else {
@@ -58,7 +59,7 @@
 
 mixin _ShowFileMixin on Show {
   @override
-  bool file(String path, {bool changed, bool overwritten}) {
+  bool file(String path, {required bool changed, required bool overwritten}) {
     if (changed) {
       _showFileChange(path, overwritten: overwritten);
     } else {
@@ -102,7 +103,7 @@
   const _ChangedShow() : super._();
 
   @override
-  bool file(String path, {bool changed, bool overwritten}) {
+  bool file(String path, {required bool changed, required bool overwritten}) {
     if (changed) _showFileChange(path, overwritten: overwritten);
     return changed;
   }
@@ -124,7 +125,7 @@
       p.relative(file, from: directory);
 
   @override
-  bool file(String path, {bool changed, bool overwritten}) {
+  bool file(String path, {required bool changed, required bool overwritten}) {
     if (changed) print(path);
     return true;
   }
diff --git a/dart_style/lib/src/cli/summary.dart b/dart_style/lib/src/cli/summary.dart
index 2d238ab..f1fc122 100644
--- a/dart_style/lib/src/cli/summary.dart
+++ b/dart_style/lib/src/cli/summary.dart
@@ -22,15 +22,19 @@
   const Summary._();
 
   /// Called when [file] is about to be formatted.
-  void beforeFile(File file, String displayPath) {}
+  ///
+  /// If stdin is being formatted, then [file] is `null`.
+  void beforeFile(File? file, String displayPath) {}
 
   /// Describe the processed file at [path] whose formatted result is [output].
   ///
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
-  void afterFile(FormatterOptions options, File file, String displayPath,
+  ///
+  /// If stdin is being formatted, then [file] is `null`.
+  void afterFile(FormatterOptions options, File? file, String displayPath,
       SourceCode output,
-      {bool changed}) {}
+      {required bool changed}) {}
 
   void show() {}
 }
@@ -52,9 +56,9 @@
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
   @override
-  void afterFile(FormatterOptions options, File file, String displayPath,
+  void afterFile(FormatterOptions options, File? file, String displayPath,
       SourceCode output,
-      {bool changed}) {
+      {required bool changed}) {
     _files++;
     if (changed) _changed++;
   }
@@ -96,7 +100,7 @@
     assert(_ongoing.isEmpty);
 
     var files = _elapsed.keys.toList();
-    files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a]));
+    files.sort((a, b) => _elapsed[b]!.compareTo(_elapsed[a]!));
 
     for (var file in files) {
       print('${_elapsed[file]}: $file');
@@ -110,7 +114,7 @@
 
   /// Called when [file] is about to be formatted.
   @override
-  void beforeFile(File file, String displayPath) {
+  void beforeFile(File? file, String displayPath) {
     _ongoing[displayPath] = DateTime.now();
   }
 
@@ -119,10 +123,10 @@
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
   @override
-  void afterFile(FormatterOptions options, File file, String displayPath,
+  void afterFile(FormatterOptions options, File? file, String displayPath,
       SourceCode output,
-      {bool changed}) {
-    var elapsed = DateTime.now().difference(_ongoing.remove(displayPath));
+      {required bool changed}) {
+    var elapsed = DateTime.now().difference(_ongoing.remove(displayPath)!);
     if (elapsed.inMilliseconds >= 10) {
       _elapsed[displayPath] = elapsed;
     } else {
diff --git a/dart_style/lib/src/dart_formatter.dart b/dart_style/lib/src/dart_formatter.dart
index e842bb2..7f437a9 100644
--- a/dart_style/lib/src/dart_formatter.dart
+++ b/dart_style/lib/src/dart_formatter.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style.src.dart_formatter;
-
 import 'dart:math' as math;
 
 import 'package:analyzer/dart/analysis/features.dart';
@@ -29,7 +26,7 @@
   /// If not explicitly provided, this is inferred from the source text. If the
   /// first newline is `\r\n` (Windows), it will use that. Otherwise, it uses
   /// Unix-style line endings (`\n`).
-  String lineEnding;
+  String? lineEnding;
 
   /// The number of characters allowed in a single line.
   final int pageWidth;
@@ -37,7 +34,7 @@
   /// The number of characters of indentation to prefix the output lines with.
   final int indent;
 
-  final Set<StyleFix> fixes = {};
+  final Set<StyleFix> fixes;
 
   /// Creates a new formatter for Dart code.
   ///
@@ -50,11 +47,10 @@
   ///
   /// While formatting, also applies any of the given [fixes].
   DartFormatter(
-      {this.lineEnding, int pageWidth, int indent, Iterable<StyleFix> fixes})
+      {this.lineEnding, int? pageWidth, int? indent, Iterable<StyleFix>? fixes})
       : pageWidth = pageWidth ?? 80,
-        indent = indent ?? 0 {
-    if (fixes != null) this.fixes.addAll(fixes);
-  }
+        indent = indent ?? 0,
+        fixes = {...?fixes};
 
   /// Formats the given [source] string containing an entire Dart compilation
   /// unit.
@@ -91,8 +87,12 @@
     // TODO(paulberry): consider plumbing in experiment enable flags from the
     // command line.
     var featureSet = FeatureSet.fromEnableFlags2(
-        sdkLanguageVersion: Version(2, 10, 0),
-        flags: ['non-nullable', 'generic-metadata']);
+        sdkLanguageVersion: Version(2, 13, 0),
+        flags: [
+          'generic-metadata',
+          'nonfunction-type-aliases',
+          'triple-shift'
+        ]);
 
     var inputOffset = 0;
     var text = source.text;
@@ -106,7 +106,7 @@
         uri: source.uri,
         isCompilationUnit: false,
         selectionStart: source.selectionStart != null
-            ? source.selectionStart + inputOffset
+            ? source.selectionStart! + inputOffset
             : null,
         selectionLength: source.selectionLength,
       );
@@ -151,7 +151,7 @@
       node = body.block.statements[0];
 
       // Make sure we consumed all of the source.
-      var token = node.endToken.next;
+      var token = node.endToken.next!;
       if (token.type != TokenType.CLOSE_CURLY_BRACKET) {
         var stringSource = StringSource(text, source.uri);
         var error = AnalysisError(
diff --git a/dart_style/lib/src/debug.dart b/dart_style/lib/src/debug.dart
index 9068af9..f796af3 100644
--- a/dart_style/lib/src/debug.dart
+++ b/dart_style/lib/src/debug.dart
@@ -3,12 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Internal debugging utilities.
-library dart_style.src.debug;
-
 import 'dart:math' as math;
 
 import 'chunk.dart';
 import 'line_splitting/rule_set.dart';
+import 'rule/rule.dart';
 
 /// Set this to `true` to turn on diagnostic output while building chunks.
 bool traceChunkBuilder = false;
@@ -127,19 +126,19 @@
       }
     }
 
-    if (chunk.rule == null) {
+    var rule = chunk.rule;
+    if (rule == null) {
       row.add('');
       row.add('(no rule)');
       row.add('');
     } else {
-      writeIf(chunk.rule.cost != 0, () => '\$${chunk.rule.cost}');
+      writeIf(rule.cost != 0, () => '\$${rule.cost}');
 
-      var ruleString = chunk.rule.toString();
-      if (chunk.rule.isHardened) ruleString += '!';
+      var ruleString = rule.toString();
+      if (rule.isHardened) ruleString += '!';
       row.add(ruleString);
 
-      var constrainedRules =
-          chunk.rule.constrainedRules.toSet().intersection(rules);
+      var constrainedRules = rule.constrainedRules.toSet().intersection(rules);
       writeIf(constrainedRules.isNotEmpty,
           () => "-> ${constrainedRules.join(" ")}");
     }
@@ -147,10 +146,9 @@
     writeIf(chunk.indent != null && chunk.indent != 0,
         () => 'indent ${chunk.indent}');
 
-    writeIf(chunk.nesting != null && chunk.nesting.indent != 0,
-        () => 'nest ${chunk.nesting}');
+    writeIf(chunk.nesting?.indent != 0, () => 'nest ${chunk.nesting}');
 
-    writeIf(chunk.flushLeft != null && chunk.flushLeft, () => 'flush');
+    writeIf(chunk.flushLeft, () => 'flush');
 
     writeIf(chunk.canDivide, () => 'divide');
 
@@ -193,8 +191,7 @@
 
 /// Shows all of the constraints between the rules used by [chunks].
 void dumpConstraints(List<Chunk> chunks) {
-  var rules =
-      chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
+  var rules = chunks.map((chunk) => chunk.rule).whereType<Rule>().toSet();
 
   for (var rule in rules) {
     var constrainedValues = [];
diff --git a/dart_style/lib/src/exceptions.dart b/dart_style/lib/src/exceptions.dart
index 8e8dcfe..86488a4 100644
--- a/dart_style/lib/src/exceptions.dart
+++ b/dart_style/lib/src/exceptions.dart
@@ -17,7 +17,7 @@
   const FormatterException(this.errors);
 
   /// Creates a human-friendly representation of the analysis errors.
-  String message({bool color}) {
+  String message({bool? color}) {
     var buffer = StringBuffer();
     buffer.writeln('Could not format because the source could not be parsed:');
 
diff --git a/dart_style/lib/src/io.dart b/dart_style/lib/src/io.dart
index 893ad95..c0064b4 100644
--- a/dart_style/lib/src/io.dart
+++ b/dart_style/lib/src/io.dart
@@ -14,8 +14,8 @@
 import 'exceptions.dart';
 import 'source_code.dart';
 
-/// Reads input from stdin until it's closed, and the formats it.
-void formatStdin(FormatterOptions options, List<int> selection, String name) {
+/// Reads and formats input from stdin until closed.
+void formatStdin(FormatterOptions options, List<int>? selection, String name) {
   var selectionStart = 0;
   var selectionLength = 0;
 
@@ -129,7 +129,7 @@
 /// Runs the formatter on [file].
 ///
 /// Returns `true` if successful or `false` if an error occurred.
-bool processFile(FormatterOptions options, File file, {String displayPath}) {
+bool processFile(FormatterOptions options, File file, {String? displayPath}) {
   displayPath ??= file.path;
 
   var formatter = DartFormatter(
diff --git a/dart_style/lib/src/line_splitting/line_splitter.dart b/dart_style/lib/src/line_splitting/line_splitter.dart
index 137c21e..8ef5926 100644
--- a/dart_style/lib/src/line_splitting/line_splitter.dart
+++ b/dart_style/lib/src/line_splitting/line_splitter.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.line_splitting.line_splitter;
-
 import '../chunk.dart';
 import '../debug.dart' as debug;
 import '../line_writer.dart';
@@ -121,9 +118,6 @@
   /// and can stop looking.
   final _queue = SolveStateQueue();
 
-  /// The lowest cost solution found so far.
-  SolveState _bestSolution;
-
   /// Creates a new splitter for [_writer] that tries to fit [chunks] into the
   /// page width.
   LineSplitter(this.writer, List<Chunk> chunks, int blockIndentation,
@@ -133,7 +127,7 @@
         // Collect the set of rules that we need to select values for.
         rules = chunks
             .map((chunk) => chunk.rule)
-            .where((rule) => rule != null)
+            .whereType<Rule>()
             .toSet()
             .toList(growable: false),
         blockIndentation = blockIndentation,
@@ -165,20 +159,22 @@
     // Start with a completely unbound, unsplit solution.
     _queue.add(SolveState(this, RuleSet(rules.length)));
 
+    SolveState? bestSolution;
+
     var attempts = 0;
     while (_queue.isNotEmpty) {
       var state = _queue.removeFirst();
 
-      if (state.isBetterThan(_bestSolution)) {
-        _bestSolution = state;
+      if (state.isBetterThan(bestSolution)) {
+        bestSolution = state;
 
         // Since we sort solutions by cost the first solution we find that
         // fits is the winner.
-        if (_bestSolution.overflowChars == 0) break;
+        if (bestSolution.overflowChars == 0) break;
       }
 
       if (debug.traceSplitter) {
-        var best = state == _bestSolution ? ' (best)' : '';
+        var best = state == bestSolution ? ' (best)' : '';
         debug.log('$state$best');
         debug.dumpLines(chunks, firstLineIndent, state.splits);
         debug.log();
@@ -191,12 +187,12 @@
     }
 
     if (debug.traceSplitter) {
-      debug.log('$_bestSolution (winner)');
-      debug.dumpLines(chunks, firstLineIndent, _bestSolution.splits);
+      debug.log('$bestSolution (winner)');
+      debug.dumpLines(chunks, firstLineIndent, bestSolution!.splits);
       debug.log();
     }
 
-    return _bestSolution.splits;
+    return bestSolution!.splits;
   }
 
   void enqueue(SolveState state) {
diff --git a/dart_style/lib/src/line_splitting/rule_set.dart b/dart_style/lib/src/line_splitting/rule_set.dart
index 2d365ab..acffb83 100644
--- a/dart_style/lib/src/line_splitting/rule_set.dart
+++ b/dart_style/lib/src/line_splitting/rule_set.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.line_splitting.rule_set;
-
 import '../rule/rule.dart';
 
 /// An optimized data structure for storing a set of values for some rules.
@@ -16,7 +13,7 @@
 /// Internally, this then just stores the values in a sparse list whose indices
 /// are the indices of the rules.
 class RuleSet {
-  List<int> _values;
+  List<int?> _values;
 
   RuleSet(int numRules) : this._(List.filled(numRules, null));
 
@@ -27,7 +24,7 @@
     // Treat hardened rules as implicitly bound.
     if (rule.isHardened) return true;
 
-    return _values[rule.index] != null;
+    return _values[rule.index!] != null;
   }
 
   /// Gets the bound value for [rule] or [Rule.unsplit] if it is not bound.
@@ -35,7 +32,7 @@
     // Hardened rules are implicitly bound.
     if (rule.isHardened) return rule.fullySplitValue;
 
-    var value = _values[rule.index];
+    var value = _values[rule.index!];
     if (value != null) return value;
 
     return Rule.unsplit;
@@ -67,7 +64,7 @@
       List<Rule> rules, Rule rule, int value, void Function(Rule) onSplitRule) {
     assert(!rule.isHardened);
 
-    _values[rule.index] = value;
+    _values[rule.index!] = value;
 
     // Test this rule against the other rules being bound.
     for (var other in rule.constrainedRules) {
@@ -76,7 +73,7 @@
       if (other.isHardened) {
         otherValue = other.fullySplitValue;
       } else {
-        otherValue = _values[other.index];
+        otherValue = _values[other.index!];
       }
 
       var constraint = rule.constrain(value, other);
@@ -127,18 +124,17 @@
 /// chosen column is for the following line.
 ///
 /// Internally, this uses a list where each element corresponds to the column
-/// of the chunk at that index in the chunk list, or `null` if that chunk did
-/// not split. This had about a 10% perf improvement over using a [Set] of
-/// splits.
+/// of the chunk at that index in the chunk list, or `-1` if that chunk did not
+/// split. This had about a 10% perf improvement over using a [Set] of splits.
 class SplitSet {
   final List<int> _columns;
 
   /// The cost of the solution that led to these splits.
   int get cost => _cost;
-  int _cost;
+  late final int _cost;
 
   /// Creates a new empty split set for a line with [numChunks].
-  SplitSet(int numChunks) : _columns = List.filled(numChunks - 1, null);
+  SplitSet(int numChunks) : _columns = List.filled(numChunks - 1, -1);
 
   /// Marks the line after chunk [index] as starting at [column].
   void add(int index, int column) {
@@ -147,7 +143,7 @@
 
   /// Returns `true` if the chunk at [splitIndex] should be split.
   bool shouldSplitAt(int index) =>
-      index < _columns.length && _columns[index] != null;
+      index < _columns.length && _columns[index] != -1;
 
   /// Gets the zero-based starting column for the chunk at [index].
   int getColumn(int index) => _columns[index];
@@ -156,7 +152,6 @@
   ///
   /// This can only be called once.
   void setCost(int cost) {
-    assert(_cost == null);
     _cost = cost;
   }
 
@@ -164,7 +159,7 @@
   String toString() {
     var result = [];
     for (var i = 0; i < _columns.length; i++) {
-      if (_columns[i] != null) {
+      if (_columns[i] != -1) {
         result.add('$i:${_columns[i]}');
       }
     }
diff --git a/dart_style/lib/src/line_splitting/solve_state.dart b/dart_style/lib/src/line_splitting/solve_state.dart
index 8f202a2..a90a246 100644
--- a/dart_style/lib/src/line_splitting/solve_state.dart
+++ b/dart_style/lib/src/line_splitting/solve_state.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.line_splitting.solve_state;
-
 import '../chunk.dart';
 import '../debug.dart' as debug;
 import '../nesting_level.dart';
@@ -31,12 +28,12 @@
   /// The set of [Rule]s that are bound by [_ruleValues].
   ///
   /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
-  Set<Rule> _boundRules;
+  late Set<Rule> _boundRules;
 
   /// The set of [Rule]s that are not bound by [_ruleValues].
   ///
   /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
-  Set<Rule> _unboundRules;
+  late Set<Rule> _unboundRules;
 
   /// The unbound rules in this state that can be bound to produce new more
   /// refined states.
@@ -68,12 +65,12 @@
 
   /// The set of splits chosen for this state.
   SplitSet get splits => _splits;
-  SplitSet _splits;
+  late final SplitSet _splits;
 
   /// The number of characters that do not fit inside the page with this set of
   /// splits.
   int get overflowChars => _overflowChars;
-  int _overflowChars;
+  int _overflowChars = 0;
 
   /// Whether we can treat this state as a complete solution by leaving its
   /// unbound rules unsplit.
@@ -86,7 +83,7 @@
 
   /// The constraints the bound rules in this state have on the remaining
   /// unbound rules.
-  Map<Rule, int> _constraints;
+  late final Map<Rule, int> _constraints = _initConstraints();
 
   /// The unbound rule values that are disallowed because they would place
   /// invalid constraints on the currently bound values.
@@ -99,14 +96,16 @@
   ///
   /// It's important to track this, because we can't allow to states to overlap
   /// if one permits more values for some unbound rule than the other does.
-  Map<Rule, Set<int>> _unboundConstraints;
+  late final Map<Rule, Set<int>> _unboundConstraints =
+      _initUnboundConstraints();
 
   /// The bound rules that appear inside lines also containing unbound rules.
   ///
   /// By appearing in the same line, it means these bound rules may affect the
   /// results of binding those unbound rules. This is used to tell if two
   /// states may diverge by binding unbound rules or not.
-  Set<Rule> _boundRulesInUnboundLines;
+  late final Set<Rule> _boundRulesInUnboundLines =
+      _initBoundRulesInUnboundLines();
 
   SolveState(this._splitter, this._ruleValues) {
     _calculateSplits();
@@ -119,7 +118,7 @@
 
   /// Returns `true` if this state is a better solution to use as the final
   /// result than [other].
-  bool isBetterThan(SolveState other) {
+  bool isBetterThan(SolveState? other) {
     // If this state contains an unbound rule that we know can't be left
     // unsplit, we can't pick this as a solution.
     if (!_isComplete) return false;
@@ -197,10 +196,10 @@
         for (var value = 1; value < rule.numValues; value++) {
           var boundRules = unsplitRules.clone();
 
-          List<Rule> mustSplitRules;
+          List<Rule>? mustSplitRules;
           var valid = boundRules.tryBind(_splitter.rules, rule, value, (rule) {
             mustSplitRules ??= [];
-            mustSplitRules.add(rule);
+            mustSplitRules!.add(rule);
           });
 
           // Make sure we don't violate the constraints of the bound rules.
@@ -211,7 +210,7 @@
           // If some unbound rules are constrained to split, remember that.
           if (mustSplitRules != null) {
             state._isComplete = false;
-            state._liveRules.addAll(mustSplitRules);
+            state._liveRules.addAll(mustSplitRules!);
           }
 
           _splitter.enqueue(state);
@@ -236,8 +235,6 @@
   bool _isOverlapping(SolveState other) {
     // Lines that contain both bound and unbound rules must have the same
     // bound values.
-    _ensureBoundRulesInUnboundLines();
-    other._ensureBoundRulesInUnboundLines();
     if (_boundRulesInUnboundLines.length !=
         other._boundRulesInUnboundLines.length) {
       return false;
@@ -250,23 +247,19 @@
       }
     }
 
-    _ensureConstraints();
-    other._ensureConstraints();
     if (_constraints.length != other._constraints.length) return false;
 
     for (var rule in _constraints.keys) {
       if (_constraints[rule] != other._constraints[rule]) return false;
     }
 
-    _ensureUnboundConstraints();
-    other._ensureUnboundConstraints();
     if (_unboundConstraints.length != other._unboundConstraints.length) {
       return false;
     }
 
     for (var rule in _unboundConstraints.keys) {
-      var disallowed = _unboundConstraints[rule];
-      var otherDisallowed = other._unboundConstraints[rule];
+      var disallowed = _unboundConstraints[rule]!;
+      var otherDisallowed = other._unboundConstraints[rule]!;
 
       if (disallowed.length != otherDisallowed.length) return false;
       for (var value in disallowed) {
@@ -285,9 +278,9 @@
     var usedNestingLevels = <NestingLevel>{};
     for (var i = 0; i < _splitter.chunks.length - 1; i++) {
       var chunk = _splitter.chunks[i];
-      if (chunk.rule.isSplit(getValue(chunk.rule), chunk)) {
-        usedNestingLevels.add(chunk.nesting);
-        chunk.nesting.clearTotalUsedIndent();
+      if (chunk.rule!.isSplit(getValue(chunk.rule!), chunk)) {
+        usedNestingLevels.add(chunk.nesting!);
+        chunk.nesting!.clearTotalUsedIndent();
       }
     }
 
@@ -298,14 +291,14 @@
     _splits = SplitSet(_splitter.chunks.length);
     for (var i = 0; i < _splitter.chunks.length - 1; i++) {
       var chunk = _splitter.chunks[i];
-      if (chunk.rule.isSplit(getValue(chunk.rule), chunk)) {
+      if (chunk.rule!.isSplit(getValue(chunk.rule!), chunk)) {
         var indent = 0;
         if (!chunk.flushLeftAfter) {
           // Add in the chunk's indent.
-          indent = _splitter.blockIndentation + chunk.indent;
+          indent = _splitter.blockIndentation + chunk.indent!;
 
           // And any expression nesting.
-          indent += chunk.nesting.totalUsedIndent;
+          indent += chunk.nesting!.totalUsedIndent;
 
           if (chunk.indentBlock(getValue)) indent += Indent.expression;
         }
@@ -318,13 +311,9 @@
   /// Evaluates the cost (i.e. the relative "badness") of splitting the line
   /// into [lines] physical lines based on the current set of rules.
   void _calculateCost() {
-    assert(_splits != null);
-
     // Calculate the length of each line and apply the cost of any spans that
     // get split.
     var cost = 0;
-    _overflowChars = 0;
-
     var length = _splitter.firstLineIndent;
 
     // The unbound rules in use by the current line. This will be null after
@@ -396,9 +385,10 @@
         // But there are a couple of squirrely cases where it's hard to prevent
         // by construction. Instead, this outlaws it by penalizing it very
         // heavily if it happens to get this far.
+        var totalIndent = chunk.nesting!.totalUsedIndent;
         if (previousNesting != null &&
-            chunk.nesting.totalUsedIndent != 0 &&
-            chunk.nesting.totalUsedIndent == previousNesting.totalUsedIndent &&
+            totalIndent != 0 &&
+            totalIndent == previousNesting.totalUsedIndent &&
             !identical(chunk.nesting, previousNesting)) {
           _overflowChars += 10000;
         }
@@ -435,7 +425,7 @@
   ///
   /// Only does this if [rule] is a valid soft rule. Returns `true` if any new
   /// live rules were added.
-  bool _addLiveRules(Rule rule) {
+  bool _addLiveRules(Rule? rule) {
     if (rule == null) return false;
 
     var added = false;
@@ -449,22 +439,19 @@
     return added;
   }
 
-  /// Lazily initializes the [_boundInUnboundLines], which is needed to compare
+  /// Used to lazy initialize [_boundInUnboundLines], which is needed to compare
   /// two states for overlap.
   ///
   /// We do this lazily because the calculation is a bit slow, and is only
   /// needed when we have two states with the same score.
-  void _ensureBoundRulesInUnboundLines() {
-    if (_boundRulesInUnboundLines != null) return;
-
-    _boundRulesInUnboundLines = <Rule>{};
-
+  Set<Rule> _initBoundRulesInUnboundLines() {
+    var rules = <Rule>{};
     var boundInLine = <Rule>{};
     var hasUnbound = false;
 
     for (var i = 0; i < _splitter.chunks.length - 1; i++) {
       if (splits.shouldSplitAt(i)) {
-        if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine);
+        if (hasUnbound) rules.addAll(boundInLine);
 
         boundInLine.clear();
         hasUnbound = false;
@@ -480,17 +467,16 @@
       }
     }
 
-    if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine);
+    if (hasUnbound) rules.addAll(boundInLine);
+    return rules;
   }
 
-  /// Lazily initializes the [_constraints], which is needed to compare two
-  /// states for overlap.
+  /// Used to lazy initializes the [_constraints], which is needed to compare
+  /// two states for overlap.
   ///
   /// We do this lazily because the calculation is a bit slow, and is only
   /// needed when we have two states with the same score.
-  void _ensureConstraints() {
-    if (_constraints != null) return;
-
+  Map<Rule, int> _initConstraints() {
     _unboundRules = <Rule>{};
     _boundRules = <Rule>{};
 
@@ -502,7 +488,7 @@
       }
     }
 
-    _constraints = {};
+    var constraints = <Rule, int>{};
 
     for (var bound in _boundRules) {
       for (var unbound in bound.constrainedRules) {
@@ -511,28 +497,24 @@
         var value = _ruleValues.getValue(bound);
         var constraint = bound.constrain(value, unbound);
         if (constraint != null) {
-          _constraints[unbound] = constraint;
+          constraints[unbound] = constraint;
         }
       }
     }
+
+    return constraints;
   }
 
-  /// Lazily initializes the [_unboundConstraints], which is needed to compare
-  /// two states for overlap.
+  /// Used to lazy initialize the [_unboundConstraints], which is needed to
+  /// compare two states for overlap.
   ///
   /// We do this lazily because the calculation is a bit slow, and is only
   /// needed when we have two states with the same score.
-  void _ensureUnboundConstraints() {
-    if (_unboundConstraints != null) return;
-
-    // _ensureConstraints should be called first which initializes these.
-    assert(_boundRules != null);
-    assert(_unboundRules != null);
-
-    _unboundConstraints = {};
-
+  Map<Rule, Set<int>> _initUnboundConstraints() {
+    var unboundConstraints = <Rule, Set<int>>{};
     for (var unbound in _unboundRules) {
-      Set<int> disallowedValues;
+      // Lazily create and add the set to the constraints only if needed.
+      late final disallowedValues = unboundConstraints[unbound] = {};
 
       for (var bound in unbound.constrainedRules) {
         if (!_boundRules.contains(bound)) continue;
@@ -555,15 +537,12 @@
             continue;
           }
 
-          if (disallowedValues == null) {
-            disallowedValues = <int>{};
-            _unboundConstraints[unbound] = disallowedValues;
-          }
-
           disallowedValues.add(value);
         }
       }
     }
+
+    return unboundConstraints;
   }
 
   @override
@@ -590,9 +569,8 @@
 
     buffer.write('   \$${splits.cost}');
 
-    if (overflowChars > 0) buffer.write(' (${overflowChars} over)');
+    if (overflowChars > 0) buffer.write(' ($overflowChars over)');
     if (!_isComplete) buffer.write(' (incomplete)');
-    if (splits == null) buffer.write(' invalid');
 
     return buffer.toString();
   }
diff --git a/dart_style/lib/src/line_splitting/solve_state_queue.dart b/dart_style/lib/src/line_splitting/solve_state_queue.dart
index a299e15..c8efdbb 100644
--- a/dart_style/lib/src/line_splitting/solve_state_queue.dart
+++ b/dart_style/lib/src/line_splitting/solve_state_queue.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.line_splitting.solve_state_queue;
-
 import 'line_splitter.dart';
 import 'solve_state.dart';
 
@@ -22,10 +19,10 @@
   /// number of "tree levels" in the heap is only done for aesthetic reasons.
   static const int _initialCapacity = 7;
 
-  LineSplitter _splitter;
+  late final LineSplitter _splitter;
 
   /// List implementation of a heap.
-  List<SolveState> _queue = List<SolveState>.filled(_initialCapacity, null);
+  List<SolveState?> _queue = List.filled(_initialCapacity, null);
 
   /// Number of elements in queue.
   /// The heap is implemented in the first [_length] entries of [_queue].
@@ -34,9 +31,6 @@
   bool get isNotEmpty => _length != 0;
 
   void bindSplitter(LineSplitter splitter) {
-    // Only do this once.
-    assert(_splitter == null);
-
     _splitter = splitter;
   }
 
@@ -50,7 +44,7 @@
       var newCapacity = _queue.length * 2 + 1;
       if (newCapacity < _initialCapacity) newCapacity = _initialCapacity;
 
-      var newQueue = List<SolveState>.filled(newCapacity, null);
+      var newQueue = List<SolveState?>.filled(newCapacity, null);
       newQueue.setRange(0, _length, _queue);
       _queue = newQueue;
     }
@@ -62,12 +56,12 @@
     assert(_length > 0);
 
     // Remove the highest priority state.
-    var result = _queue[0];
+    var result = _queue[0]!;
     _length--;
 
     // Fill the gap with the one at the end of the list and re-heapify.
     if (_length > 0) {
-      var last = _queue[_length];
+      var last = _queue[_length]!;
       _queue[_length] = null;
       _bubbleDown(last, 0);
     }
@@ -135,7 +129,7 @@
     // also have lower priority.
     do {
       var index = position - 1;
-      var enqueued = _queue[index];
+      var enqueued = _queue[index]!;
 
       var comparison = _compareScore(enqueued, state);
 
@@ -185,7 +179,7 @@
   void _bubbleUp(SolveState element, int index) {
     while (index > 0) {
       var parentIndex = (index - 1) ~/ 2;
-      var parent = _queue[parentIndex];
+      var parent = _queue[parentIndex]!;
 
       if (_compare(element, parent) > 0) break;
 
@@ -205,8 +199,8 @@
 
     while (rightChildIndex < _length) {
       var leftChildIndex = rightChildIndex - 1;
-      var leftChild = _queue[leftChildIndex];
-      var rightChild = _queue[rightChildIndex];
+      var leftChild = _queue[leftChildIndex]!;
+      var rightChild = _queue[rightChildIndex]!;
 
       var comparison = _compare(leftChild, rightChild);
       var minChildIndex;
@@ -234,7 +228,7 @@
 
     var leftChildIndex = rightChildIndex - 1;
     if (leftChildIndex < _length) {
-      var child = _queue[leftChildIndex];
+      var child = _queue[leftChildIndex]!;
       var comparison = _compare(element, child);
 
       if (comparison > 0) {
diff --git a/dart_style/lib/src/line_writer.dart b/dart_style/lib/src/line_writer.dart
index 748bb9b..e8874a8 100644
--- a/dart_style/lib/src/line_writer.dart
+++ b/dart_style/lib/src/line_writer.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style.src.line_writer;
-
 import 'chunk.dart';
 import 'dart_formatter.dart';
 import 'debug.dart' as debug;
@@ -35,19 +32,19 @@
   ///
   /// This will be `null` if there is no selection or the writer hasn't reached
   /// the beginning of the selection yet.
-  int _selectionStart;
+  int? _selectionStart;
 
   /// The offset in [_buffer] where the selection ends in the formatted code.
   ///
   /// This will be `null` if there is no selection or the writer hasn't reached
   /// the end of the selection yet.
-  int _selectionEnd;
+  int? _selectionEnd;
 
   /// The number of characters that have been written to the output.
   int get length => _buffer.length;
 
   LineWriter(DartFormatter formatter, this._chunks)
-      : _lineEnding = formatter.lineEnding,
+      : _lineEnding = formatter.lineEnding!,
         pageWidth = formatter.pageWidth,
         _blockIndentation = 0,
         _blockCache = {};
@@ -114,7 +111,7 @@
 
       // Get ready for the next line.
       newlines = chunk.isDouble ? 2 : 1;
-      indent = chunk.indent;
+      indent = chunk.indent!;
       flushLeft = chunk.flushLeft;
       start = i + 1;
     }
@@ -134,7 +131,7 @@
   /// Takes the chunks from [start] to [end] with leading [indent], removes
   /// them, and runs the [LineSplitter] on them.
   int _completeLine(int newlines, int indent, int start, int end,
-      {bool flushLeft}) {
+      {required bool flushLeft}) {
     // Write the newlines required by the previous line.
     for (var j = 0; j < newlines; j++) {
       _buffer.write(_lineEnding);
@@ -175,11 +172,11 @@
           // If this block contains one of the selection markers, tell the
           // writer where it ended up in the final output.
           if (block.selectionStart != null) {
-            _selectionStart = length + block.selectionStart;
+            _selectionStart = length + block.selectionStart!;
           }
 
           if (block.selectionEnd != null) {
-            _selectionEnd = length + block.selectionEnd;
+            _selectionEnd = length + block.selectionEnd!;
           }
 
           _buffer.write(block.text);
@@ -220,11 +217,11 @@
   /// contains a selection marker.
   void _writeChunk(Chunk chunk) {
     if (chunk.selectionStart != null) {
-      _selectionStart = length + chunk.selectionStart;
+      _selectionStart = length + chunk.selectionStart!;
     }
 
     if (chunk.selectionEnd != null) {
-      _selectionEnd = length + chunk.selectionEnd;
+      _selectionEnd = length + chunk.selectionEnd!;
     }
 
     _buffer.write(chunk.text);
@@ -269,13 +266,13 @@
   /// if it was contained within this split list of chunks.
   ///
   /// Otherwise, this is `null`.
-  final int selectionStart;
+  final int? selectionStart;
 
   /// Where in the resulting buffer the selection end point should appear if it
   /// was contained within this split list of chunks.
   ///
   /// Otherwise, this is `null`.
-  final int selectionEnd;
+  final int? selectionEnd;
 
   FormatResult(this.text, this.cost, this.selectionStart, this.selectionEnd);
 }
diff --git a/dart_style/lib/src/nesting_builder.dart b/dart_style/lib/src/nesting_builder.dart
index 8ed62f1..4740114 100644
--- a/dart_style/lib/src/nesting_builder.dart
+++ b/dart_style/lib/src/nesting_builder.dart
@@ -51,7 +51,7 @@
   ///
   /// Here, if we discard the expression nesting before we reach the "{", then
   /// it won't get indented as it should.
-  NestingLevel _pendingNesting;
+  NestingLevel? _pendingNesting;
 
   /// The current number of characters of block indentation.
   int get indentation => _stack.last;
@@ -66,7 +66,7 @@
   /// Creates a new indentation level [spaces] deeper than the current one.
   ///
   /// If omitted, [spaces] defaults to [Indent.block].
-  void indent([int spaces]) {
+  void indent([int? spaces]) {
     spaces ??= Indent.block;
 
     // Indentation should only change outside of nesting.
@@ -95,11 +95,11 @@
   /// if the previous line has a lower level of nesting.
   ///
   /// If [indent] is omitted, defaults to [Indent.expression].
-  void nest([int indent]) {
+  void nest([int? indent]) {
     indent ??= Indent.expression;
 
     if (_pendingNesting != null) {
-      _pendingNesting = _pendingNesting.nest(indent);
+      _pendingNesting = _pendingNesting!.nest(indent);
     } else {
       _pendingNesting = _nesting.nest(indent);
     }
@@ -108,7 +108,7 @@
   /// Discards the most recent level of expression nesting.
   void unnest() {
     if (_pendingNesting != null) {
-      _pendingNesting = _pendingNesting.parent;
+      _pendingNesting = _pendingNesting!.parent;
     } else {
       _pendingNesting = _nesting.parent;
     }
@@ -121,7 +121,7 @@
   void commitNesting() {
     if (_pendingNesting == null) return;
 
-    _nesting = _pendingNesting;
+    _nesting = _pendingNesting!;
     _pendingNesting = null;
   }
 }
diff --git a/dart_style/lib/src/nesting_level.dart b/dart_style/lib/src/nesting_level.dart
index c1088c6..acbdfe3 100644
--- a/dart_style/lib/src/nesting_level.dart
+++ b/dart_style/lib/src/nesting_level.dart
@@ -24,8 +24,8 @@
 class NestingLevel extends FastHash {
   /// The nesting level surrounding this one, or `null` if this is represents
   /// top level code in a block.
-  NestingLevel get parent => _parent;
-  NestingLevel _parent;
+  NestingLevel? get parent => _parent;
+  NestingLevel? _parent;
 
   /// The number of characters that this nesting level is indented relative to
   /// the containing level.
@@ -37,8 +37,8 @@
   /// its parents, after determining which nesting levels are actually used.
   ///
   /// This is only valid during line splitting.
-  int get totalUsedIndent => _totalUsedIndent;
-  int _totalUsedIndent;
+  int get totalUsedIndent => _totalUsedIndent!;
+  int? _totalUsedIndent;
 
   bool get isNested => _parent != null;
 
@@ -53,22 +53,25 @@
   /// Clears the previously calculated total indent of this nesting level.
   void clearTotalUsedIndent() {
     _totalUsedIndent = null;
-    if (_parent != null) _parent.clearTotalUsedIndent();
+    _parent?.clearTotalUsedIndent();
   }
 
   /// Calculates the total amount of indentation from this nesting level and
   /// all of its parents assuming only [usedNesting] levels are in use.
   void refreshTotalUsedIndent(Set<NestingLevel> usedNesting) {
-    if (_totalUsedIndent != null) return;
+    var totalIndent = _totalUsedIndent;
+    if (totalIndent != null) return;
 
-    _totalUsedIndent = 0;
+    totalIndent = 0;
 
     if (_parent != null) {
-      _parent.refreshTotalUsedIndent(usedNesting);
-      _totalUsedIndent += _parent.totalUsedIndent;
+      _parent!.refreshTotalUsedIndent(usedNesting);
+      totalIndent += _parent!.totalUsedIndent;
     }
 
-    if (usedNesting.contains(this)) _totalUsedIndent += indent;
+    if (usedNesting.contains(this)) totalIndent += indent;
+
+    _totalUsedIndent = totalIndent;
   }
 
   @override
diff --git a/dart_style/lib/src/rule/argument.dart b/dart_style/lib/src/rule/argument.dart
index a483e4e..7ff8378 100644
--- a/dart_style/lib/src/rule/argument.dart
+++ b/dart_style/lib/src/rule/argument.dart
@@ -1,19 +1,16 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.rule.argument;
-
 import '../chunk.dart';
 import 'rule.dart';
 
 /// Base class for a rule that handles argument or parameter lists.
 abstract class ArgumentRule extends Rule {
   /// The chunks prior to each positional argument.
-  final List<Chunk> _arguments = [];
+  final List<Chunk?> _arguments = [];
 
   /// The rule used to split collections in the argument list, if any.
-  Rule _collectionRule;
+  Rule? _collectionRule;
 
   /// The number of leading collection arguments.
   ///
@@ -43,20 +40,18 @@
   @override
   void addConstrainedRules(Set<Rule> rules) {
     super.addConstrainedRules(rules);
-    if (_collectionRule != null) rules.add(_collectionRule);
+    if (_collectionRule != null) rules.add(_collectionRule!);
   }
 
   @override
   void forgetUnusedRules() {
     super.forgetUnusedRules();
-    if (_collectionRule != null && _collectionRule.index == null) {
-      _collectionRule = null;
-    }
+    if (_collectionRule?.index == null) _collectionRule = null;
   }
 
   /// Remembers [chunk] as containing the split that occurs right before an
   /// argument in the list.
-  void beforeArgument(Chunk chunk) {
+  void beforeArgument(Chunk? chunk) {
     _arguments.add(chunk);
   }
 
@@ -93,14 +88,14 @@
 class PositionalRule extends ArgumentRule {
   /// If there are named arguments following these positional ones, this will
   /// be their rule.
-  Rule _namedArgsRule;
+  Rule? _namedArgsRule;
 
   /// Creates a new rule for a positional argument list.
   ///
   /// If [_collectionRule] is given, it is the rule used to split the collection
   /// arguments in the list.
   PositionalRule(
-      Rule collectionRule, int leadingCollections, int trailingCollections)
+      Rule? collectionRule, int leadingCollections, int trailingCollections)
       : super(collectionRule, leadingCollections, trailingCollections);
 
   @override
@@ -126,15 +121,13 @@
   @override
   void addConstrainedRules(Set<Rule> rules) {
     super.addConstrainedRules(rules);
-    if (_namedArgsRule != null) rules.add(_namedArgsRule);
+    if (_namedArgsRule != null) rules.add(_namedArgsRule!);
   }
 
   @override
   void forgetUnusedRules() {
     super.forgetUnusedRules();
-    if (_namedArgsRule != null && _namedArgsRule.index == null) {
-      _namedArgsRule = null;
-    }
+    if (_namedArgsRule?.index == null) _namedArgsRule = null;
   }
 
   @override
@@ -186,14 +179,14 @@
   ///          argument,
   ///          argument, named: argument);
   @override
-  int constrain(int value, Rule other) {
+  int? constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
 
     // Handle the relationship between the positional and named args.
     if (other == _namedArgsRule) {
       // If the positional args are one-per-line, the named args are too.
-      if (value == fullySplitValue) return _namedArgsRule.fullySplitValue;
+      if (value == fullySplitValue) return _namedArgsRule!.fullySplitValue;
 
       // Otherwise, if there is any split in the positional arguments, don't
       // allow the named arguments on the same line as them.
@@ -257,7 +250,7 @@
   int get numValues => 3;
 
   NamedRule(
-      Rule collectionRule, int leadingCollections, int trailingCollections)
+      Rule? collectionRule, int leadingCollections, int trailingCollections)
       : super(collectionRule, leadingCollections, trailingCollections);
 
   @override
@@ -270,7 +263,7 @@
   }
 
   @override
-  int constrain(int value, Rule other) {
+  int? constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
 
diff --git a/dart_style/lib/src/rule/metadata.dart b/dart_style/lib/src/rule/metadata.dart
index c552101..3d59e96 100644
--- a/dart_style/lib/src/rule/metadata.dart
+++ b/dart_style/lib/src/rule/metadata.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
-library dart_style.src.rule.metadata;
-
 import 'argument.dart';
 import 'rule.dart';
 
@@ -17,8 +14,8 @@
 /// Also, if the annotations split, we force the entire parameter list to fully
 /// split, both named and positional.
 class MetadataRule extends Rule {
-  Rule _positionalRule;
-  Rule _namedRule;
+  Rule? _positionalRule;
+  Rule? _namedRule;
 
   /// Remembers that [rule] is the [PositionalRule] used by the argument list
   /// containing the parameter metadata using this rule.
@@ -35,7 +32,7 @@
   /// Constrains the surrounding argument list rules to fully split if the
   /// metadata does.
   @override
-  int constrain(int value, Rule other) {
+  int? constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
 
@@ -43,27 +40,22 @@
     if (value == Rule.unsplit) return null;
 
     // Otherwise, they have to split.
-    if (other == _positionalRule) return _positionalRule.fullySplitValue;
-    if (other == _namedRule) return _namedRule.fullySplitValue;
+    if (other == _positionalRule) return _positionalRule!.fullySplitValue;
+    if (other == _namedRule) return _namedRule!.fullySplitValue;
 
     return null;
   }
 
   @override
   void addConstrainedRules(Set<Rule> rules) {
-    if (_positionalRule != null) rules.add(_positionalRule);
-    if (_namedRule != null) rules.add(_namedRule);
+    if (_positionalRule != null) rules.add(_positionalRule!);
+    if (_namedRule != null) rules.add(_namedRule!);
   }
 
   @override
   void forgetUnusedRules() {
     super.forgetUnusedRules();
-    if (_positionalRule != null && _positionalRule.index == null) {
-      _positionalRule = null;
-    }
-
-    if (_namedRule != null && _namedRule.index == null) {
-      _namedRule = null;
-    }
+    if (_positionalRule?.index == null) _positionalRule = null;
+    if (_namedRule?.index == null) _namedRule = null;
   }
 }
diff --git a/dart_style/lib/src/rule/rule.dart b/dart_style/lib/src/rule/rule.dart
index 7813bf1..660b8d0 100644
--- a/dart_style/lib/src/rule/rule.dart
+++ b/dart_style/lib/src/rule/rule.dart
@@ -2,8 +2,6 @@
 // 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.
 
-library dart_style.src.rule.rule;
-
 import '../chunk.dart';
 import '../fast_hash.dart';
 
@@ -40,7 +38,7 @@
 
   /// During line splitting [LineSplitter] sets this to the index of this
   /// rule in its list of rules.
-  int index;
+  int? index;
 
   /// If `true`, the rule has been "hardened" meaning it's been placed into a
   /// permanent "must fully split" state.
@@ -75,7 +73,7 @@
   /// rules.
   bool get splitsOnInnerRules => true;
 
-  Rule([int cost]) : _cost = cost ?? Cost.normal;
+  Rule([int? cost]) : _cost = cost ?? Cost.normal;
 
   /// Creates a new rule that is already fully split.
   Rule.hard() : _cost = 0 {
@@ -114,8 +112,8 @@
   /// Allows relationships between rules like "if I split, then this should
   /// split too". Returns a non-negative value to force [other] to take that
   /// value. Returns -1 to allow [other] to take any non-zero value. Returns
-  /// null to not constrain other.
-  int constrain(int value, Rule other) {
+  /// `null` to not constrain other.
+  int? constrain(int value, Rule other) {
     // By default, any containing rule will be fully split if this one is split.
     if (value == Rule.unsplit) return null;
     if (_implied.contains(other)) return other.fullySplitValue;
@@ -151,35 +149,40 @@
     // Lazy initialize this on first use. Note: Assumes this is only called
     // after the chunks have been written and any constraints have been wired
     // up.
-    if (_constrainedRules == null) {
-      _constrainedRules = _implied.toSet();
-      addConstrainedRules(_constrainedRules);
-    }
+    var rules = _constrainedRules;
+    if (rules != null) return rules;
 
-    return _constrainedRules;
+    rules = _implied.toSet();
+    addConstrainedRules(rules);
+    _constrainedRules = rules;
+    return rules;
   }
 
-  Set<Rule> _constrainedRules;
+  Set<Rule>? _constrainedRules;
 
   /// The transitive closure of all of the rules this rule places constraints
   /// on, directly or indirectly, including itself.
   Set<Rule> get allConstrainedRules {
-    if (_allConstrainedRules == null) {
-      void visit(Rule rule) {
-        if (_allConstrainedRules.contains(rule)) return;
+    var rules = _allConstrainedRules;
+    if (rules != null) return rules;
 
-        _allConstrainedRules.add(rule);
-        rule.constrainedRules.forEach(visit);
-      }
-
-      _allConstrainedRules = {};
-      visit(this);
-    }
-
-    return _allConstrainedRules;
+    rules = {};
+    _traverseConstraints(rules, this);
+    _allConstrainedRules = rules;
+    return rules;
   }
 
-  Set<Rule> _allConstrainedRules;
+  /// Traverses the constraint graph of [rule] adding everything to [rules].
+  void _traverseConstraints(Set<Rule> rules, Rule rule) {
+    if (rules.contains(rule)) return;
+
+    rules.add(rule);
+    for (var rule in rule.constrainedRules) {
+      _traverseConstraints(rules, rule);
+    }
+  }
+
+  Set<Rule>? _allConstrainedRules;
 
   @override
   String toString() => '$id';
diff --git a/dart_style/lib/src/source_code.dart b/dart_style/lib/src/source_code.dart
index 949daff..87c5aa5 100644
--- a/dart_style/lib/src/source_code.dart
+++ b/dart_style/lib/src/source_code.dart
@@ -10,7 +10,7 @@
   /// The [uri] where the source code is from.
   ///
   /// Used in error messages if the code cannot be parsed.
-  final String uri;
+  final String? uri;
 
   /// The Dart source code text.
   final String text;
@@ -20,10 +20,10 @@
 
   /// The offset in [text] where the selection begins, or `null` if there is
   /// no selection.
-  final int selectionStart;
+  final int? selectionStart;
 
   /// The number of selected characters or `null` if there is no selection.
-  final int selectionLength;
+  final int? selectionLength;
 
   /// Gets the source code before the beginning of the selection.
   ///
@@ -38,7 +38,7 @@
   /// If there is no selection, returns an empty string.
   String get selectedText {
     if (selectionStart == null) return '';
-    return text.substring(selectionStart, selectionStart + selectionLength);
+    return text.substring(selectionStart!, selectionStart! + selectionLength!);
   }
 
   /// Gets the source code following the selection.
@@ -46,7 +46,7 @@
   /// If there is no selection, returns an empty string.
   String get textAfterSelection {
     if (selectionStart == null) return '';
-    return text.substring(selectionStart + selectionLength);
+    return text.substring(selectionStart! + selectionLength!);
   }
 
   SourceCode(this.text,
@@ -61,21 +61,21 @@
     }
 
     if (selectionStart != null) {
-      if (selectionStart < 0) {
+      if (selectionStart! < 0) {
         throw ArgumentError('selectionStart must be non-negative.');
       }
 
-      if (selectionStart > text.length) {
+      if (selectionStart! > text.length) {
         throw ArgumentError('selectionStart must be within text.');
       }
     }
 
     if (selectionLength != null) {
-      if (selectionLength < 0) {
+      if (selectionLength! < 0) {
         throw ArgumentError('selectionLength must be non-negative.');
       }
 
-      if (selectionStart + selectionLength > text.length) {
+      if (selectionStart! + selectionLength! > text.length) {
         throw ArgumentError('selectionLength must end within text.');
       }
     }
diff --git a/dart_style/lib/src/source_visitor.dart b/dart_style/lib/src/source_visitor.dart
index ebbae38..7ad10c9 100644
--- a/dart_style/lib/src/source_visitor.dart
+++ b/dart_style/lib/src/source_visitor.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2014, 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.
-
-library dart_style.src.source_visitor;
-
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_ast_factory.dart';
 import 'package:analyzer/dart/ast/token.dart';
@@ -52,14 +49,13 @@
   /// splitting up named constructors.
   static bool looksLikeStaticCall(Expression node) {
     if (node is! MethodInvocation) return false;
-    var invocation = node as MethodInvocation;
-    if (invocation.target == null) return false;
+    if (node.target == null) return false;
 
     // A prefixed unnamed constructor call:
     //
     //     prefix.Foo();
-    if (invocation.target is SimpleIdentifier &&
-        _looksLikeClassName(invocation.methodName.name)) {
+    if (node.target is SimpleIdentifier &&
+        _looksLikeClassName(node.methodName.name)) {
       return true;
     }
 
@@ -67,10 +63,8 @@
     //
     //     Foo.named();
     //     prefix.Foo.named();
-    var target = invocation.target;
-    if (target is PrefixedIdentifier) {
-      target = (target as PrefixedIdentifier).identifier;
-    }
+    var target = node.target;
+    if (target is PrefixedIdentifier) target = target.identifier;
 
     return target is SimpleIdentifier && _looksLikeClassName(target.name);
   }
@@ -142,7 +136,7 @@
   /// The character offset of the end of the selection, if there is a selection.
   ///
   /// This is calculated and cached by [_findSelectionEnd].
-  int _selectionEnd;
+  int? _selectionEnd;
 
   /// How many levels deep inside a constant context the visitor currently is.
   int _constNesting = 0;
@@ -200,9 +194,8 @@
 
   /// Initialize a newly created visitor to write source code representing
   /// the visited nodes to the given [writer].
-  SourceVisitor(this._formatter, this._lineInfo, this._source) {
-    builder = ChunkBuilder(_formatter, _source);
-  }
+  SourceVisitor(this._formatter, this._lineInfo, this._source)
+      : builder = ChunkBuilder(_formatter, _source);
 
   /// Runs the visitor on [node], formatting its contents.
   ///
@@ -215,7 +208,7 @@
     visit(node);
 
     // Output trailing comments.
-    writePrecedingCommentsAndNewlines(node.endToken.next);
+    writePrecedingCommentsAndNewlines(node.endToken.next!);
 
     assert(_constNesting == 0, 'Should have exited all const contexts.');
 
@@ -306,7 +299,7 @@
     if (node.arguments != null) {
       // Metadata annotations are always const contexts.
       _constNesting++;
-      visitArgumentList(node.arguments, nestExpression: false);
+      visitArgumentList(node.arguments!, nestExpression: false);
       _constNesting--;
     }
 
@@ -369,7 +362,7 @@
     token(node.assertKeyword);
 
     var arguments = <Expression>[node.condition];
-    if (node.message != null) arguments.add(node.message);
+    if (node.message != null) arguments.add(node.message!);
 
     // If the argument list has a trailing comma, format it like a collection
     // literal where each argument goes on its own line, they are indented +2,
@@ -393,7 +386,7 @@
       token(node.assertKeyword);
 
       var arguments = [node.condition];
-      if (node.message != null) arguments.add(node.message);
+      if (node.message != null) arguments.add(node.message!);
 
       // If the argument list has a trailing comma, format it like a collection
       // literal where each argument goes on its own line, they are indented +2,
@@ -648,7 +641,7 @@
 
     // If the target is a call with a trailing comma in the argument list,
     // treat it like a collection literal.
-    ArgumentList arguments;
+    ArgumentList? arguments;
     if (expression is InvocationExpression) {
       arguments = expression.argumentList;
     } else if (expression is InstanceCreationExpression) {
@@ -828,6 +821,13 @@
 
   @override
   void visitConditionalExpression(ConditionalExpression node) {
+    // TODO(rnystrom): Consider revisiting whether users prefer this after 2.13.
+    /*
+    // Flatten else-if style chained conditionals.
+    var shouldNest = node.parent is! ConditionalExpression ||
+        (node.parent as ConditionalExpression).elseExpression != node;
+    if (shouldNest) builder.nestExpression();
+    */
     builder.nestExpression();
 
     // Start lazily so we don't force the operator to split if a line comment
@@ -863,6 +863,11 @@
     builder.endRule();
     builder.endSpan();
     builder.endBlockArgumentNesting();
+
+    // TODO(rnystrom): Consider revisiting whether users prefer this after 2.13.
+    /*
+    if (shouldNest) builder.unnest();
+    */
     builder.unnest();
   }
 
@@ -944,8 +949,12 @@
       //           super();
       space();
       if (node.initializers.length > 1) {
-        _writeText(node.parameters.parameters.last.isOptional ? ' ' : '  ',
-            node.separator.offset);
+        var padding = '  ';
+        if (node.parameters.parameters.last.isNamed ||
+            node.parameters.parameters.last.isOptionalPositional) {
+          padding = ' ';
+        }
+        _writeText(padding, node.separator!.offset);
       }
 
       // ":".
@@ -1044,15 +1053,15 @@
       if (_formatter.fixes.contains(StyleFix.namedDefaultSeparator)) {
         // Change the separator to "=".
         space();
-        writePrecedingCommentsAndNewlines(node.separator);
-        _writeText('=', node.separator.offset);
+        writePrecedingCommentsAndNewlines(node.separator!);
+        _writeText('=', node.separator!.offset);
       } else {
         // The '=' separator is preceded by a space, ":" is not.
-        if (node.separator.type == TokenType.EQ) space();
+        if (node.separator!.type == TokenType.EQ) space();
         token(node.separator);
       }
 
-      soloSplit(_assignmentCost(node.defaultValue));
+      soloSplit(_assignmentCost(node.defaultValue!));
       visit(node.defaultValue);
 
       builder.unnest();
@@ -1204,7 +1213,7 @@
     if (expression is PropertyAccess) {
       return expression.realTarget;
     } else if (expression is MethodInvocation) {
-      return expression.realTarget;
+      return expression.realTarget!;
     } else if (expression is IndexExpression) {
       return expression.realTarget;
     }
@@ -1234,7 +1243,7 @@
           _insertCascadeTargetIntoExpression(expressionTarget, cascadeTarget),
           // If we've reached the end, replace the `..` operator with `.`
           expressionTarget == cascadeTarget
-              ? _synthesizeToken(TokenType.PERIOD, expression.operator)
+              ? _synthesizeToken(TokenType.PERIOD, expression.operator!)
               : expression.operator,
           expression.methodName,
           expression.typeArguments,
@@ -1244,9 +1253,8 @@
 
       // A null-aware cascade treats the `?` in `?..` as part of the token, but
       // for a non-cascade index, it is a separate `?` token.
-      if (expression.period != null &&
-          expression.period.type == TokenType.QUESTION_PERIOD_PERIOD) {
-        question = _synthesizeToken(TokenType.QUESTION, expression.period);
+      if (expression.period?.type == TokenType.QUESTION_PERIOD_PERIOD) {
+        question = _synthesizeToken(TokenType.QUESTION, expression.period!);
       }
 
       return astFactory.indexExpressionForTarget2(
@@ -1264,7 +1272,7 @@
   /// Parenthesize the target of the given statement's expression (assumed to
   /// be a CascadeExpression) before removing the cascade.
   void _fixCascadeByParenthesizingTarget(ExpressionStatement statement) {
-    CascadeExpression cascade = statement.expression;
+    var cascade = statement.expression as CascadeExpression;
     assert(cascade.cascadeSections.length == 1);
 
     // Write any leading comments and whitespace immediately, as they should
@@ -1827,7 +1835,7 @@
 
       space();
 
-      visit(node.functionType);
+      visit(node.type);
     });
   }
 
@@ -1841,10 +1849,10 @@
     // Treat a chain of if-else elements as a single unit so that we don't
     // unnecessarily indent each subsequent section of the chain.
     var ifElements = [
-      for (CollectionElement thisNode = node;
+      for (CollectionElement? thisNode = node;
           thisNode is IfElement;
-          thisNode = (thisNode as IfElement).elseElement)
-        thisNode as IfElement
+          thisNode = thisNode.elseElement)
+        thisNode
     ];
 
     // If the body of the then or else branch is a spread of a collection
@@ -1884,7 +1892,7 @@
     var elseSpreadBracket =
         _findSpreadCollectionBracket(ifElements.last.elseElement);
     if (elseSpreadBracket != null) {
-      spreadBrackets[ifElements.last.elseElement] = elseSpreadBracket;
+      spreadBrackets[ifElements.last.elseElement!] = elseSpreadBracket;
       beforeBlock(elseSpreadBracket, spreadRule, null);
     }
 
@@ -2012,7 +2020,7 @@
       }
 
       token(node.elseKeyword);
-      visitClause(node.elseStatement);
+      visitClause(node.elseStatement!);
     }
   }
 
@@ -2089,10 +2097,10 @@
     var includeKeyword = true;
 
     if (node.keyword != null) {
-      if (node.keyword.keyword == Keyword.NEW &&
+      if (node.keyword!.keyword == Keyword.NEW &&
           _formatter.fixes.contains(StyleFix.optionalNew)) {
         includeKeyword = false;
-      } else if (node.keyword.keyword == Keyword.CONST &&
+      } else if (node.keyword!.keyword == Keyword.CONST &&
           _formatter.fixes.contains(StyleFix.optionalConst) &&
           _constNesting > 0) {
         includeKeyword = false;
@@ -2103,7 +2111,7 @@
       token(node.keyword, after: space);
     } else {
       // Don't lose comments before the discarded keyword, if any.
-      writePrecedingCommentsAndNewlines(node.keyword);
+      writePrecedingCommentsAndNewlines(node.keyword!);
     }
 
     builder.startSpan(Cost.constructorName);
@@ -2282,21 +2290,20 @@
 
     // If there is only a single superclass constraint, format it like an
     // "extends" in a class.
-    if (node.onClause != null &&
-        node.onClause.superclassConstraints.length == 1) {
+    var onClause = node.onClause;
+    if (onClause != null && onClause.superclassConstraints.length == 1) {
       soloSplit();
-      token(node.onClause.onKeyword);
+      token(onClause.onKeyword);
       space();
-      visit(node.onClause.superclassConstraints.single);
+      visit(onClause.superclassConstraints.single);
     }
 
     builder.startRule(CombinatorRule());
 
     // If there are multiple superclass constraints, format them like the
     // "implements" clause.
-    if (node.onClause != null &&
-        node.onClause.superclassConstraints.length > 1) {
-      visit(node.onClause);
+    if (onClause != null && onClause.superclassConstraints.length > 1) {
+      visit(onClause);
     }
 
     visit(node.implementsClause);
@@ -2471,11 +2478,11 @@
         // Parameters can use "var" instead of "dynamic". Since we are inserting
         // "dynamic" in that case, remove the "var".
         if (node.keyword != null) {
-          if (node.keyword.type != Keyword.VAR) {
+          if (node.keyword!.type != Keyword.VAR) {
             modifier(node.keyword);
           } else {
             // Keep any comment attached to "var".
-            writePrecedingCommentsAndNewlines(node.keyword);
+            writePrecedingCommentsAndNewlines(node.keyword!);
           }
         }
 
@@ -2484,8 +2491,8 @@
         // without a name. Add "dynamic" in that case.
 
         // Ensure comments on the identifier comes before the inserted type.
-        token(node.identifier.token, before: () {
-          _writeText('dynamic', node.identifier.offset);
+        token(node.identifier!.token, before: () {
+          _writeText('dynamic', node.identifier!.offset);
           split();
         });
       } else {
@@ -2599,7 +2606,7 @@
     var components = node.components;
     for (var component in components) {
       // The '.' separator
-      if (component.previous.lexeme == '.') {
+      if (component.previous!.lexeme == '.') {
         token(component.previous);
       }
       token(component);
@@ -2690,7 +2697,8 @@
     var hasMultipleVariables =
         (node.parent as VariableDeclarationList).variables.length > 1;
 
-    _visitAssignment(node.equals, node.initializer, nest: hasMultipleVariables);
+    _visitAssignment(node.equals!, node.initializer!,
+        nest: hasMultipleVariables);
   }
 
   @override
@@ -2764,7 +2772,7 @@
 
   /// Visit a [node], and if not null, optionally preceded or followed by the
   /// specified functions.
-  void visit(AstNode node, {void Function() before, void Function() after}) {
+  void visit(AstNode? node, {void Function()? before, void Function()? after}) {
     if (node == null) return;
 
     if (before != null) before();
@@ -2801,7 +2809,7 @@
   /// the parameter.
   void visitParameterMetadata(
       NodeList<Annotation> metadata, void Function() visitParameter) {
-    if (metadata == null || metadata.isEmpty) {
+    if (metadata.isEmpty) {
       visitParameter();
       return;
     }
@@ -2835,7 +2843,7 @@
   /// the surrounding named argument rule. That way, this can ensure that a
   /// split between the name and argument forces the argument list to split
   /// too.
-  void visitNamedArgument(NamedExpression node, [NamedRule rule]) {
+  void visitNamedArgument(NamedExpression node, [NamedRule? rule]) {
     builder.nestExpression();
     builder.startSpan();
     visit(node.name);
@@ -2959,7 +2967,7 @@
     visit(node.name);
     builder.endSpan();
 
-    TypeParameterList typeParameters;
+    TypeParameterList? typeParameters;
     if (node is FunctionDeclaration) {
       typeParameters = node.functionExpression.typeParameters;
     } else {
@@ -2981,9 +2989,9 @@
   /// space before it if it's not empty.
   ///
   /// If [beforeBody] is provided, it is invoked before the body is visited.
-  void _visitBody(TypeParameterList typeParameters,
-      FormalParameterList parameters, FunctionBody body,
-      [void Function() beforeBody]) {
+  void _visitBody(TypeParameterList? typeParameters,
+      FormalParameterList? parameters, FunctionBody body,
+      [void Function()? beforeBody]) {
     // If the body is "=>", add an extra level of indentation around the
     // parameters and a rule that spans the parameters and the "=>". This
     // ensures that if the parameters wrap, they wrap more deeply than the "=>"
@@ -3025,7 +3033,7 @@
   /// Visits the type parameters (if any) and formal parameters of a method
   /// declaration, function declaration, or generic function type.
   void _visitParameterSignature(
-      TypeParameterList typeParameters, FormalParameterList parameters) {
+      TypeParameterList? typeParameters, FormalParameterList? parameters) {
     // Start the nesting for the parameters here, so they indent past the
     // type parameters too, if any.
     builder.nestExpression();
@@ -3064,10 +3072,10 @@
   /// Visit a list of [nodes] if not null, optionally separated and/or preceded
   /// and followed by the given functions.
   void visitNodes(Iterable<AstNode> nodes,
-      {void Function() before,
-      void Function() between,
-      void Function() after}) {
-    if (nodes == null || nodes.isEmpty) return;
+      {void Function()? before,
+      void Function()? between,
+      void Function()? after}) {
+    if (nodes.isEmpty) return;
 
     if (before != null) before();
 
@@ -3082,8 +3090,8 @@
 
   /// Visit a comma-separated list of [nodes] if not null.
   void visitCommaSeparatedNodes(Iterable<AstNode> nodes,
-      {void Function() between}) {
-    if (nodes == null || nodes.isEmpty) return;
+      {void Function()? between}) {
+    if (nodes.isEmpty) return;
 
     between ??= space;
 
@@ -3095,7 +3103,7 @@
       visit(node);
 
       // The comma after the node.
-      if (node.endToken.next.lexeme == ',') token(node.endToken.next);
+      if (node.endToken.next!.lexeme == ',') token(node.endToken.next);
     }
   }
 
@@ -3104,16 +3112,16 @@
   ///
   /// This is also used for argument lists with a trailing comma which are
   /// considered "collection-like". In that case, [node] is `null`.
-  void _visitCollectionLiteral(TypedLiteral node, Token leftBracket,
+  void _visitCollectionLiteral(TypedLiteral? node, Token leftBracket,
       Iterable<AstNode> elements, Token rightBracket,
-      [int cost]) {
+      [int? cost]) {
     if (node != null) {
       // See if `const` should be removed.
       if (node.constKeyword != null &&
           _constNesting > 0 &&
           _formatter.fixes.contains(StyleFix.optionalConst)) {
         // Don't lose comments before the discarded keyword, if any.
-        writePrecedingCommentsAndNewlines(node.constKeyword);
+        writePrecedingCommentsAndNewlines(node.constKeyword!);
       } else {
         modifier(node.constKeyword);
       }
@@ -3165,7 +3173,7 @@
       if (element != elements.first) {
         if (preserveNewlines) {
           // See if the next element is on the next line.
-          if (_endLine(element.beginToken.previous) !=
+          if (_endLine(element.beginToken.previous!) !=
               _startLine(element.beginToken)) {
             oneOrTwoNewlines();
 
@@ -3219,7 +3227,7 @@
 
     // Find the parameter immediately preceding the optional parameters (if
     // there are any).
-    FormalParameter lastRequired;
+    FormalParameter? lastRequired;
     for (var i = 0; i < parameters.parameters.length; i++) {
       if (parameters.parameters[i] is DefaultFormalParameter) {
         if (i > 0) lastRequired = parameters.parameters[i - 1];
@@ -3286,10 +3294,10 @@
   /// In that case [functionKeywordPosition] should be the source position
   /// used for the inserted "Function" text.
   void _visitGenericFunctionType(
-      AstNode returnType,
-      Token functionKeyword,
-      int functionKeywordPosition,
-      TypeParameterList typeParameters,
+      AstNode? returnType,
+      Token? functionKeyword,
+      int? functionKeywordPosition,
+      TypeParameterList? typeParameters,
       FormalParameterList parameters) {
     builder.startLazyRule();
     builder.nestExpression();
@@ -3298,7 +3306,7 @@
     if (functionKeyword != null) {
       token(functionKeyword);
     } else {
-      _writeText('Function', functionKeywordPosition);
+      _writeText('Function', functionKeywordPosition!);
     }
 
     builder.unnest();
@@ -3312,7 +3320,7 @@
   /// If [equals] is `null`, then [equalsPosition] must be a
   /// position to use for the inserted text "=".
   void _visitGenericTypeAliasHeader(Token typedefKeyword, AstNode name,
-      AstNode typeParameters, Token equals, int equalsPosition) {
+      AstNode? typeParameters, Token? equals, int? equalsPosition) {
     token(typedefKeyword);
     space();
 
@@ -3329,7 +3337,7 @@
     if (equals != null) {
       token(equals);
     } else {
-      _writeText('=', equalsPosition);
+      _writeText('=', equalsPosition!);
     }
 
     builder.endRule();
@@ -3362,7 +3370,7 @@
   ///     //   ^
   ///
   /// Otherwise, returns `null`.
-  Token _findSpreadCollectionBracket(AstNode node) {
+  Token? _findSpreadCollectionBracket(AstNode? node) {
     if (node is SpreadElement) {
       var expression = node.expression;
       if (expression is ListLiteral) {
@@ -3445,15 +3453,8 @@
     // See if this literal is associated with an argument list or if element
     // that wants to handle splitting and indenting it. If not, we'll use a
     // default rule.
-    Rule rule;
-    if (_blockRules.containsKey(leftBracket)) {
-      rule = _blockRules[leftBracket];
-    }
-
-    Chunk argumentChunk;
-    if (_blockPreviousChunks.containsKey(leftBracket)) {
-      argumentChunk = _blockPreviousChunks[leftBracket];
-    }
+    var rule = _blockRules[leftBracket];
+    var argumentChunk = _blockPreviousChunks[leftBracket];
 
     // Create a rule for whether or not to split the block contents.
     builder.startRule(rule);
@@ -3468,7 +3469,7 @@
   /// given, ignores that rule inside the body when determining if it should
   /// split.
   void _endLiteralBody(Token rightBracket,
-      {Rule ignoredRule, bool forceSplit}) {
+      {Rule? ignoredRule, bool? forceSplit}) {
     forceSplit ??= false;
 
     // Put comments before the closing delimiter inside the block.
@@ -3520,14 +3521,14 @@
   }
 
   /// If [keyword] is `const`, begins a new constant context.
-  void _startPossibleConstContext(Token keyword) {
+  void _startPossibleConstContext(Token? keyword) {
     if (keyword != null && keyword.keyword == Keyword.CONST) {
       _constNesting++;
     }
   }
 
   /// If [keyword] is `const`, ends the current outermost constant context.
-  void _endPossibleConstContext(Token keyword) {
+  void _endPossibleConstContext(Token? keyword) {
     if (keyword != null && keyword.keyword == Keyword.CONST) {
       _constNesting--;
     }
@@ -3554,7 +3555,7 @@
   /// splitting rule for the block. These are used for handling block-like
   /// expressions inside argument lists and spread collections inside if
   /// elements.
-  void beforeBlock(Token token, Rule rule, [Chunk previousChunk]) {
+  void beforeBlock(Token token, Rule rule, [Chunk? previousChunk]) {
     _blockRules[token] = rule;
     if (previousChunk != null) _blockPreviousChunks[token] = previousChunk;
   }
@@ -3587,7 +3588,7 @@
   /// [FunctionExpression].
   bool _isInLambda(AstNode node) =>
       node.parent is FunctionExpression &&
-      node.parent.parent is! FunctionDeclaration;
+      node.parent!.parent is! FunctionDeclaration;
 
   /// Writes the string literal [string] to the output.
   ///
@@ -3599,7 +3600,7 @@
     writePrecedingCommentsAndNewlines(string);
 
     // Split each line of a multiline string into separate chunks.
-    var lines = string.lexeme.split(_formatter.lineEnding);
+    var lines = string.lexeme.split(_formatter.lineEnding!);
     var offset = string.offset;
 
     _writeText(lines.first, offset);
@@ -3622,16 +3623,14 @@
   bool hasCommaAfter(AstNode node) => _commaAfter(node) != null;
 
   /// The comma token immediately following [node] if there is one, or `null`.
-  Token _commaAfter(AstNode node) {
-    if (node.endToken.next.type == TokenType.COMMA) {
-      return node.endToken.next;
-    }
+  Token? _commaAfter(AstNode node) {
+    var next = node.endToken.next!;
+    if (next.type == TokenType.COMMA) return next;
 
     // TODO(sdk#38990): endToken doesn't include the "?" on a nullable
     // function-typed formal, so check for that case and handle it.
-    if (node.endToken.next.type == TokenType.QUESTION &&
-        node.endToken.next.next.type == TokenType.COMMA) {
-      return node.endToken.next.next;
+    if (next.type == TokenType.QUESTION && next.next!.type == TokenType.COMMA) {
+      return next.next;
     }
 
     return null;
@@ -3639,7 +3638,7 @@
 
   /// Emit the given [modifier] if it's non null, followed by non-breaking
   /// whitespace.
-  void modifier(Token modifier) {
+  void modifier(Token? modifier) {
     token(modifier, after: space);
   }
 
@@ -3690,7 +3689,7 @@
   Chunk zeroSplit() => builder.split();
 
   /// Writes a single space split with its own rule.
-  Rule soloSplit([int cost]) {
+  Rule soloSplit([int? cost]) {
     var rule = Rule(cost);
     builder.startRule(rule);
     split();
@@ -3711,7 +3710,7 @@
   /// Does nothing if [token] is `null`. If [before] is given, it will be
   /// executed before the token is outout. Likewise, [after] will be called
   /// after the token is output.
-  void token(Token token, {void Function() before, void Function() after}) {
+  void token(Token? token, {void Function()? before, void Function()? after}) {
     if (token == null) return;
 
     writePrecedingCommentsAndNewlines(token);
@@ -3725,13 +3724,13 @@
 
   /// Writes all formatted whitespace and comments that appear before [token].
   bool writePrecedingCommentsAndNewlines(Token token) {
-    var comment = token.precedingComments;
+    Token? comment = token.precedingComments;
 
     // For performance, avoid calculating newlines between tokens unless
     // actually needed.
     if (comment == null) {
       if (builder.needsToPreserveNewlines) {
-        builder.preserveNewlines(_startLine(token) - _endLine(token.previous));
+        builder.preserveNewlines(_startLine(token) - _endLine(token.previous!));
       }
 
       return false;
@@ -3740,14 +3739,14 @@
     // If the token's comments are being moved by a fix, do not write them here.
     if (_suppressPrecedingCommentsAndNewLines.contains(token)) return false;
 
-    var previousLine = _endLine(token.previous);
+    var previousLine = _endLine(token.previous!);
     var tokenLine = _startLine(token);
 
     // Edge case: The analyzer includes the "\n" in the script tag's lexeme,
     // which confuses some of these calculations. We don't want to allow a
     // blank line between the script tag and a following comment anyway, so
     // just override the script tag's line.
-    if (token.previous.type == TokenType.SCRIPT_TAG) previousLine = tokenLine;
+    if (token.previous!.type == TokenType.SCRIPT_TAG) previousLine = tokenLine;
 
     var comments = <SourceComment>[];
     while (comment != null) {
@@ -3755,7 +3754,7 @@
 
       // Don't preserve newlines at the top of the file.
       if (comment == token.precedingComments &&
-          token.previous.type == TokenType.EOF) {
+          token.previous!.type == TokenType.EOF) {
         previousLine = commentLine;
       }
 
@@ -3824,14 +3823,14 @@
   ///
   /// Returns `null` if the selection start has already been processed or is
   /// not within that range.
-  int _getSelectionStartWithin(int offset, int length) {
+  int? _getSelectionStartWithin(int offset, int length) {
     // If there is no selection, do nothing.
     if (_source.selectionStart == null) return null;
 
     // If we've already passed it, don't consider it again.
     if (_passedSelectionStart) return null;
 
-    var start = _source.selectionStart - offset;
+    var start = _source.selectionStart! - offset;
 
     // If it started in whitespace before this text, push it forward to the
     // beginning of the non-whitespace text.
@@ -3851,7 +3850,7 @@
   ///
   /// Returns `null` if the selection endpoint has already been processed or is
   /// not within that range.
-  int _getSelectionEndWithin(int offset, int length) {
+  int? _getSelectionEndWithin(int offset, int length) {
     // If there is no selection, do nothing.
     if (_source.selectionLength == null) return null;
 
@@ -3882,28 +3881,32 @@
   ///
   /// Removes any trailing whitespace from the selection.
   int _findSelectionEnd() {
-    if (_selectionEnd != null) return _selectionEnd;
+    if (_selectionEnd != null) return _selectionEnd!;
 
-    _selectionEnd = _source.selectionStart + _source.selectionLength;
+    var end = _source.selectionStart! + _source.selectionLength!;
 
     // If the selection bumps to the end of the source, pin it there.
-    if (_selectionEnd == _source.text.length) return _selectionEnd;
+    if (end == _source.text.length) {
+      _selectionEnd = end;
+      return end;
+    }
 
     // Trim off any trailing whitespace. We want the selection to "rubberband"
     // around the selected non-whitespace tokens since the whitespace will
     // be munged by the formatter itself.
-    while (_selectionEnd > _source.selectionStart) {
+    while (end > _source.selectionStart!) {
       // Stop if we hit anything other than space, tab, newline or carriage
       // return.
-      var char = _source.text.codeUnitAt(_selectionEnd - 1);
+      var char = _source.text.codeUnitAt(end - 1);
       if (char != 0x20 && char != 0x09 && char != 0x0a && char != 0x0d) {
         break;
       }
 
-      _selectionEnd--;
+      end--;
     }
 
-    return _selectionEnd;
+    _selectionEnd = end;
+    return end;
   }
 
   /// Gets the 1-based line number that the beginning of [token] lies on.
diff --git a/dart_style/lib/src/whitespace.dart b/dart_style/lib/src/whitespace.dart
index bf7eab2..d9f226d 100644
--- a/dart_style/lib/src/whitespace.dart
+++ b/dart_style/lib/src/whitespace.dart
@@ -71,6 +71,10 @@
   /// less prescriptive over the user's whitespace.
   static const oneOrTwoNewlines = Whitespace._('oneOrTwoNewlines');
 
+  /// A hard split was just written whose whitespace takes precedence over any
+  /// previous pending whitespace.
+  static const afterHardSplit = Whitespace._('afterHardSplit');
+
   final String name;
 
   /// Gets the minimum number of newlines contained in this whitespace.
diff --git a/dart_style/pubspec.yaml b/dart_style/pubspec.yaml
index e387681..d2e2f82 100644
--- a/dart_style/pubspec.yaml
+++ b/dart_style/pubspec.yaml
@@ -1,16 +1,16 @@
 name: dart_style
 # Note: See tool/grind.dart for how to bump the version.
-version: 1.3.14
+version: 2.0.1
 description: >-
   Opinionated, automatic Dart source code formatter.
   Provides an API and a CLI tool.
 repository: https://github.com/dart-lang/dart_style
 
 environment:
-  sdk: '>=2.11.99 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dependencies:
-  analyzer: ^1.0.0
+  analyzer: ^1.3.0
   args: '>=1.0.0 <3.0.0'
   path: ^1.0.0
   pub_semver: '>=1.4.4 <3.0.0'
@@ -19,11 +19,13 @@
 dev_dependencies:
   grinder: ^0.9.0-nullsafety.0
   js: ^0.6.0
-  node_preamble: ^1.0.0
+  # TODO(rnystrom): Disabled for now because node_preamble is not migrated yet
+  # and publishing to npm hasn't been used in a while.
+  #  node_preamble: ^1.0.0
   pedantic: ^1.0.0
-  test: ^1.16.0
-  test_descriptor: ^1.0.0
-  test_process: ^1.0.0
+  test: ^1.16.8
+  test_descriptor: ^2.0.0
+  test_process: ^2.0.0
   yaml: '>=2.0.0 <4.0.0'
 
 executables:
diff --git a/dart_style/tool/grind.dart b/dart_style/tool/grind.dart
index 2ba2966..cb8f8b6 100644
--- a/dart_style/tool/grind.dart
+++ b/dart_style/tool/grind.dart
@@ -2,11 +2,7 @@
 // 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 'dart:convert';
-import 'dart:io';
-
 import 'package:grinder/grinder.dart';
-import 'package:node_preamble/preamble.dart' as preamble;
 import 'package:pub_semver/pub_semver.dart';
 import 'package:yaml/yaml.dart' as yaml;
 
@@ -28,6 +24,9 @@
   Dart.run('bin/format.dart', arguments: ['-w', '.']);
 }
 
+// TODO(rnystrom): Disabled for now because node_preamble is not migrated and
+// this isn't used anyway.
+/*
 @Task('Publish to npm')
 void npm() {
   var out = 'dist';
@@ -62,6 +61,7 @@
   }));
   run('npm', arguments: ['publish', out]);
 }
+*/
 
 /// Gets ready to publish a new version of the package.
 ///
diff --git a/dds/.gitignore b/dds/.gitignore
deleted file mode 100644
index e308da7..0000000
--- a/dds/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# See https://dart.dev/guides/libraries/private-files
-.dart_tool/
-.packages
-build/
-pubspec.lock
diff --git a/dds/.pubspec.yaml.swn b/dds/.pubspec.yaml.swn
deleted file mode 100644
index 60dc524..0000000
--- a/dds/.pubspec.yaml.swn
+++ /dev/null
Binary files differ
diff --git a/dds/.pubspec.yaml.swo b/dds/.pubspec.yaml.swo
deleted file mode 100644
index 505fdc9..0000000
--- a/dds/.pubspec.yaml.swo
+++ /dev/null
Binary files differ
diff --git a/dds/BUILD.gn b/dds/BUILD.gn
index ff2a409..ff7c57d 100644
--- a/dds/BUILD.gn
+++ b/dds/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for dds-1.7.6
+# This file is generated by package_importer.py for dds-2.0.1
 
 import("//build/dart/dart_library.gni")
 
@@ -11,14 +11,19 @@
 
   deps = [
     "//third_party/dart-pkg/pub/async",
+    "//third_party/dart-pkg/pub/collection",
+    "//third_party/dart-pkg/pub/devtools_shared",
     "//third_party/dart-pkg/pub/json_rpc_2",
     "//third_party/dart-pkg/pub/meta",
+    "//third_party/dart-pkg/pub/path",
     "//third_party/dart-pkg/pub/pedantic",
     "//third_party/dart-pkg/pub/shelf",
     "//third_party/dart-pkg/pub/shelf_proxy",
+    "//third_party/dart-pkg/pub/shelf_static",
     "//third_party/dart-pkg/pub/shelf_web_socket",
     "//third_party/dart-pkg/pub/sse",
     "//third_party/dart-pkg/pub/stream_channel",
+    "//third_party/dart-pkg/pub/usage",
     "//third_party/dart-pkg/pub/vm_service",
     "//third_party/dart-pkg/pub/web_socket_channel",
   ]
@@ -29,7 +34,23 @@
     "src/client.dart",
     "src/client_manager.dart",
     "src/constants.dart",
+    "src/dap/adapters/dart.dart",
+    "src/dap/adapters/dart_cli.dart",
+    "src/dap/base_debug_adapter.dart",
+    "src/dap/isolate_manager.dart",
+    "src/dap/logging.dart",
+    "src/dap/protocol_common.dart",
+    "src/dap/protocol_generated.dart",
+    "src/dap/protocol_special.dart",
+    "src/dap/protocol_stream.dart",
+    "src/dap/protocol_stream_transformers.dart",
+    "src/dap/server.dart",
     "src/dds_impl.dart",
+    "src/devtools/devtools_client.dart",
+    "src/devtools/devtools_handler.dart",
+    "src/devtools/file_system.dart",
+    "src/devtools/server_api.dart",
+    "src/devtools/usage.dart",
     "src/expression_evaluator.dart",
     "src/isolate_manager.dart",
     "src/logging_repository.dart",
diff --git a/dds/CHANGELOG.md b/dds/CHANGELOG.md
index e374a78..84e2e6a 100644
--- a/dds/CHANGELOG.md
+++ b/dds/CHANGELOG.md
@@ -1,3 +1,15 @@
+# 2.0.1
+- Update `package:vm_service` to ^7.0.0.
+
+# 2.0.0
+- **Breaking change:** add null safety support.
+- **Breaking change:** minimum Dart SDK revision bumped to 2.12.0.
+
+# 1.8.0
+- Add support for launching DevTools from DDS.
+- Fixed issue where two clients subscribing to the same stream in close succession
+  could result in DDS sending multiple `streamListen` requests to the VM service.
+
 # 1.7.6
 - Update dependencies.
 
diff --git a/dds/LICENSE b/dds/LICENSE
index 18daf2b..467a982 100644
--- a/dds/LICENSE
+++ b/dds/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2020, the Dart project authors. All rights reserved.
+Copyright 2020, the Dart project authors.
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/dds/bin/dds.dart b/dds/bin/dds.dart
index 9937d26..b34c090 100644
--- a/dds/bin/dds.dart
+++ b/dds/bin/dds.dart
@@ -2,8 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
+import 'dart:convert';
 import 'dart:io';
 
 import 'package:dds/dds.dart';
@@ -16,6 +15,9 @@
 ///   - DDS bind address
 ///   - DDS port
 ///   - Disable service authentication codes
+///   - Start DevTools
+///   - DevTools build directory
+///   - Enable logging
 Future<void> main(List<String> args) async {
   if (args.isEmpty) return;
 
@@ -24,7 +26,7 @@
   final remoteVmServiceUri = Uri.parse(args.first);
 
   // Resolve the address which is potentially provided by the user.
-  InternetAddress address;
+  late InternetAddress address;
   final addresses = await InternetAddress.lookup(args[1]);
   // Prefer IPv4 addresses.
   for (int i = 0; i < addresses.length; i++) {
@@ -37,16 +39,37 @@
     port: int.parse(args[2]),
   );
   final disableServiceAuthCodes = args[3] == 'true';
+
+  final startDevTools = args[4] == 'true';
+  Uri? devToolsBuildDirectory;
+  if (args[5].isNotEmpty) {
+    devToolsBuildDirectory = Uri.file(args[5]);
+  }
+  final logRequests = args[6] == 'true';
   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(
+    final dds = await DartDevelopmentService.startDartDevelopmentService(
       remoteVmServiceUri,
       serviceUri: serviceUri,
       enableAuthCodes: !disableServiceAuthCodes,
+      devToolsConfiguration: startDevTools && devToolsBuildDirectory != null
+          ? DevToolsConfiguration(
+              enable: startDevTools,
+              customBuildDirectoryPath: devToolsBuildDirectory,
+            )
+          : null,
+      logRequests: logRequests,
     );
-    stderr.write('DDS started');
-  } catch (e) {
-    stderr.writeln('Failed to start DDS:\n$e');
+    stderr.write(json.encode({
+      'state': 'started',
+      if (dds.devToolsUri != null) 'devToolsUri': dds.devToolsUri.toString(),
+    }));
+  } catch (e, st) {
+    stderr.write(json.encode({
+      'state': 'error',
+      'error': '$e',
+      'stacktrace': '$st',
+    }));
   }
 }
diff --git a/dds/example/example.dart b/dds/example/example.dart
index 561dca9..883c678 100644
--- a/dds/example/example.dart
+++ b/dds/example/example.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'package:dds/dds.dart';
 import 'package:vm_service/vm_service_io.dart';
 
diff --git a/dds/lib/dds.dart b/dds/lib/dds.dart
index f7c7a05..e8f05bb 100644
--- a/dds/lib/dds.dart
+++ b/dds/lib/dds.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 /// A library used to spawn the Dart Developer Service, used to communicate
 /// with a Dart VM Service instance.
 library dds;
@@ -41,13 +39,12 @@
   /// default.
   static Future<DartDevelopmentService> startDartDevelopmentService(
     Uri remoteVmServiceUri, {
-    Uri serviceUri,
+    Uri? serviceUri,
     bool enableAuthCodes = true,
     bool ipv6 = false,
+    DevToolsConfiguration? devToolsConfiguration,
+    bool logRequests = false,
   }) async {
-    if (remoteVmServiceUri == null) {
-      throw ArgumentError.notNull('remoteVmServiceUri');
-    }
     if (remoteVmServiceUri.scheme != 'http') {
       throw ArgumentError(
         'remoteVmServiceUri must have an HTTP scheme. Actual: ${remoteVmServiceUri.scheme}',
@@ -63,12 +60,15 @@
       // If provided an address to bind to, ensure it uses a protocol consistent
       // with that used to spawn DDS.
       final addresses = await InternetAddress.lookup(serviceUri.host);
-      final address = addresses.firstWhere(
-        (a) => (a.type ==
-            (ipv6 ? InternetAddressType.IPv6 : InternetAddressType.IPv4)),
-        orElse: () => null,
-      );
-      if (address == null) {
+
+      try {
+        // Check to see if there's a valid address.
+        addresses.firstWhere(
+          (a) => (a.type ==
+              (ipv6 ? InternetAddressType.IPv6 : InternetAddressType.IPv4)),
+        );
+      } on StateError {
+        // Could not find a valid address.
         throw ArgumentError(
           "serviceUri '$serviceUri' is not an IPv${ipv6 ? "6" : "4"} address.",
         );
@@ -80,6 +80,8 @@
       serviceUri,
       enableAuthCodes,
       ipv6,
+      devToolsConfiguration,
+      logRequests,
     );
     await service.startService();
     return service;
@@ -111,19 +113,24 @@
   /// [DartDevelopmentService] via HTTP.
   ///
   /// Returns `null` if the service is not running.
-  Uri get uri;
+  Uri? get uri;
 
   /// The [Uri] VM service clients can use to communicate with this
   /// [DartDevelopmentService] via server-sent events (SSE).
   ///
   /// Returns `null` if the service is not running.
-  Uri get sseUri;
+  Uri? get sseUri;
 
   /// The [Uri] VM service clients can use to communicate with this
   /// [DartDevelopmentService] via a [WebSocket].
   ///
   /// Returns `null` if the service is not running.
-  Uri get wsUri;
+  Uri? get wsUri;
+
+  /// The HTTP [Uri] of the hosted DevTools instance.
+  ///
+  /// Returns `null` if DevTools is not running.
+  Uri? get devToolsUri;
 
   /// Set to `true` if this instance of [DartDevelopmentService] is accepting
   /// requests.
@@ -168,3 +175,13 @@
   final int errorCode;
   final String message;
 }
+
+class DevToolsConfiguration {
+  const DevToolsConfiguration({
+    required this.customBuildDirectoryPath,
+    this.enable = false,
+  });
+
+  final bool enable;
+  final Uri customBuildDirectoryPath;
+}
diff --git a/dds/lib/src/binary_compatible_peer.dart b/dds/lib/src/binary_compatible_peer.dart
index 2e1af84..b701f5e 100644
--- a/dds/lib/src/binary_compatible_peer.dart
+++ b/dds/lib/src/binary_compatible_peer.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'dart:async';
 import 'dart:convert';
 import 'dart:typed_data';
diff --git a/dds/lib/src/client.dart b/dds/lib/src/client.dart
index f81bd25..1df3a3a 100644
--- a/dds/lib/src/client.dart
+++ b/dds/lib/src/client.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'dart:async';
 
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
@@ -21,27 +19,25 @@
 /// Representation of a single DDS client which manages the connection and
 /// DDS request intercepting / forwarding.
 class DartDevelopmentServiceClient {
-  factory DartDevelopmentServiceClient.fromWebSocket(
+  DartDevelopmentServiceClient.fromWebSocket(
     DartDevelopmentService dds,
     WebSocketChannel ws,
     json_rpc.Peer vmServicePeer,
-  ) =>
-      DartDevelopmentServiceClient._(
-        dds,
-        ws,
-        vmServicePeer,
-      );
+  ) : this._(
+          dds as DartDevelopmentServiceImpl,
+          ws,
+          vmServicePeer,
+        );
 
-  factory DartDevelopmentServiceClient.fromSSEConnection(
+  DartDevelopmentServiceClient.fromSSEConnection(
     DartDevelopmentService dds,
     SseConnection sse,
     json_rpc.Peer vmServicePeer,
-  ) =>
-      DartDevelopmentServiceClient._(
-        dds,
-        sse,
-        vmServicePeer,
-      );
+  ) : this._(
+          dds as DartDevelopmentServiceImpl,
+          sse,
+          vmServicePeer,
+        );
 
   DartDevelopmentServiceClient._(
     this.dds,
@@ -169,7 +165,8 @@
         (parameters) => {
               'type': 'Size',
               'size': StreamManager
-                  .loggingRepositories[StreamManager.kLoggingStream].bufferSize,
+                  .loggingRepositories[StreamManager.kLoggingStream]!
+                  .bufferSize,
             });
 
     _clientPeer.registerMethod('setLogHistorySize', (parameters) {
@@ -179,7 +176,7 @@
           "'size' must be greater or equal to zero",
         );
       }
-      StreamManager.loggingRepositories[StreamManager.kLoggingStream]
+      StreamManager.loggingRepositories[StreamManager.kLoggingStream]!
           .resize(size);
       return RPCResponses.success;
     });
@@ -195,8 +192,8 @@
     });
 
     _clientPeer.registerMethod('getSupportedProtocols', (parameters) async {
-      final Map<String, dynamic> supportedProtocols =
-          await _vmServicePeer.sendRequest('getSupportedProtocols');
+      final Map<String, dynamic> supportedProtocols = (await _vmServicePeer
+          .sendRequest('getSupportedProtocols')) as Map<String, dynamic>;
       final ddsVersion = DartDevelopmentService.protocolVersion.split('.');
       final ddsProtocol = {
         'protocolName': 'DDS',
@@ -291,17 +288,17 @@
   String get defaultClientName => 'client$_id';
 
   /// The current name associated with this client.
-  String get name => _name;
+  String? get name => _name;
 
   // NOTE: this should not be called directly except from:
   //   - `ClientManager._clearClientName`
   //   - `ClientManager._setClientNameHelper`
-  set name(String n) => _name = n ?? defaultClientName;
-  String _name;
+  set name(String? n) => _name = n ?? defaultClientName;
+  String? _name;
 
   final DartDevelopmentServiceImpl dds;
   final StreamChannel connection;
   final Map<String, String> services = {};
   final json_rpc.Peer _vmServicePeer;
-  json_rpc.Peer _clientPeer;
+  late json_rpc.Peer _clientPeer;
 }
diff --git a/dds/lib/src/client_manager.dart b/dds/lib/src/client_manager.dart
index c911c51..807e5fd 100644
--- a/dds/lib/src/client_manager.dart
+++ b/dds/lib/src/client_manager.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
 
 import 'client.dart';
@@ -98,7 +96,7 @@
       pauseTypeMask |= PauseTypeMasks.pauseOnExitMask;
     }
 
-    clientResumePermissions[client.name].permissionsMask = pauseTypeMask;
+    clientResumePermissions[client.name!]!.permissionsMask = pauseTypeMask;
     return RPCResponses.success;
   }
 
@@ -111,10 +109,10 @@
     _clearClientName(client);
     client.name = name.isEmpty ? client.defaultClientName : name;
     clientResumePermissions.putIfAbsent(
-      client.name,
+      client.name!,
       () => _ClientResumePermissions(),
     );
-    clientResumePermissions[client.name].clients.add(client);
+    clientResumePermissions[client.name!]!.clients.add(client);
   }
 
   /// Resets the client's name while also cleaning up resume permissions and
@@ -155,7 +153,7 @@
     }
   }
 
-  DartDevelopmentServiceClient findFirstClientThatHandlesService(
+  DartDevelopmentServiceClient? findFirstClientThatHandlesService(
       String service) {
     for (final client in clients) {
       if (client.services.containsKey(service)) {
@@ -173,7 +171,7 @@
 
   /// Mapping of client names to all clients of that name and their resume
   /// permissions.
-  final Map<String, _ClientResumePermissions> clientResumePermissions = {};
+  final Map<String?, _ClientResumePermissions> clientResumePermissions = {};
 
   final DartDevelopmentServiceImpl dds;
 }
diff --git a/dds/lib/src/constants.dart b/dds/lib/src/constants.dart
index 2466390..63f2b7f 100644
--- a/dds/lib/src/constants.dart
+++ b/dds/lib/src/constants.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 abstract class RPCResponses {
   static const success = <String, dynamic>{
     'type': 'Success',
@@ -16,6 +14,10 @@
   };
 }
 
+// Give connections time to reestablish before considering them closed.
+// Required to reestablish connections killed by UberProxy.
+const sseKeepAlive = Duration(seconds: 30);
+
 abstract class PauseTypeMasks {
   static const pauseOnStartMask = 1 << 0;
   static const pauseOnReloadMask = 1 << 1;
diff --git a/dds/lib/src/dap/adapters/dart.dart b/dds/lib/src/dap/adapters/dart.dart
new file mode 100644
index 0000000..85ca410
--- /dev/null
+++ b/dds/lib/src/dap/adapters/dart.dart
@@ -0,0 +1,520 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:collection/collection.dart';
+import 'package:vm_service/vm_service.dart' as vm;
+
+import '../base_debug_adapter.dart';
+import '../isolate_manager.dart';
+import '../logging.dart';
+import '../protocol_generated.dart';
+import '../protocol_stream.dart';
+
+/// A base DAP Debug Adapter implementation for running and debugging Dart-based
+/// applications (including Flutter and Tests).
+///
+/// This class implements all functionality common to Dart, Flutter and Test
+/// debug sessions, including things like breakpoints and expression eval.
+///
+/// Sub-classes should handle the launching/attaching of apps and any custom
+/// behaviour (such as Flutter's Hot Reload). This is generally done by overriding
+/// `fooImpl` methods that are called during the handling of a `fooRequest` from
+/// the client.
+///
+/// A DebugAdapter instance will be created per application being debugged (in
+/// multi-session mode, one DebugAdapter corresponds to one incoming TCP
+/// connection, though a client may make multiple of these connections if it
+/// wants to debug multiple scripts concurrently, such as with a compound launch
+/// configuration in VS Code).
+///
+/// The lifecycle is described in the DAP spec here:
+/// https://microsoft.github.io/debug-adapter-protocol/overview#initialization
+///
+/// In summary:
+///
+/// The client will create a connection to the server (which will create an
+///   instance of the debug adapter) and send an `initializeRequest` message,
+///   wait for the server to return a response and then an initializedEvent
+/// The client will then send breakpoints and exception config
+///   (`setBreakpointsRequest`, `setExceptionBreakpoints`) and then a
+///   `configurationDoneRequest`.
+/// Finally, the client will send a `launchRequest` or `attachRequest` to start
+///   running/attaching to the script.
+///
+/// The client will continue to send requests during the debug session that may
+/// be in response to user actions (for example changing breakpoints or typing
+/// an expression into an evaluation console) or to events sent by the server
+/// (for example when the server sends a `StoppedEvent` it may cause the client
+/// to then send a `stackTraceRequest` or `scopesRequest` to get variables).
+abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
+    extends BaseDebugAdapter<T> {
+  late final T args;
+  final _debuggerInitializedCompleter = Completer<void>();
+  final _configurationDoneCompleter = Completer<void>();
+
+  /// Managers VM Isolates and their events, including fanning out any requests
+  /// to set breakpoints etc. from the client to all Isolates.
+  late IsolateManager _isolateManager;
+
+  /// All active VM Service subscriptions.
+  ///
+  /// TODO(dantup): This may be changed to use StreamManager as part of using
+  /// DDS in this process.
+  final _subscriptions = <StreamSubscription<vm.Event>>[];
+
+  /// The VM service of the app being debugged.
+  ///
+  /// `null` if the session is running in noDebug mode of the connection has not
+  /// yet been made.
+  vm.VmServiceInterface? vmService;
+
+  /// Whether the current debug session is an attach request (as opposed to a
+  /// launch request). Not available until after launchRequest or attachRequest
+  /// have been called.
+  late final bool isAttach;
+
+  DartDebugAdapter(ByteStreamServerChannel channel, Logger? logger)
+      : super(channel, logger) {
+    _isolateManager = IsolateManager(this);
+  }
+
+  /// Completes when the debugger initialization has completed. Used to delay
+  /// processing isolate events while initialization is still running to avoid
+  /// race conditions (for example if an isolate unpauses before we have
+  /// processed its initial paused state).
+  Future<void> get debuggerInitialized => _debuggerInitializedCompleter.future;
+
+  /// attachRequest is called by the client when it wants us to to attach to
+  /// an existing app. This will only be called once (and only one of this or
+  /// launchRequest will be called).
+  @override
+  FutureOr<void> attachRequest(
+    Request request,
+    T args,
+    void Function(void) sendResponse,
+  ) async {
+    this.args = args;
+    isAttach = true;
+
+    // Don't start launching until configurationDone.
+    if (!_configurationDoneCompleter.isCompleted) {
+      logger?.call('Waiting for configurationDone request...');
+      await _configurationDoneCompleter.future;
+    }
+
+    // TODO(dantup): Implement attach support.
+    throw UnimplementedError();
+
+    // Delegate to the sub-class to attach to the process.
+    // await attachImpl();
+    //
+    // sendResponse(null);
+  }
+
+  /// configurationDone is called by the client when it has finished sending
+  /// any initial configuration (such as breakpoints and exception pause
+  /// settings).
+  ///
+  /// We delay processing `launchRequest`/`attachRequest` until this request has
+  /// been sent to ensure we're not still getting breakpoints (which are sent
+  /// per-file) while we're launching and initializing over the VM Service.
+  @override
+  FutureOr<void> configurationDoneRequest(
+    Request request,
+    ConfigurationDoneArguments? args,
+    void Function(void) sendResponse,
+  ) async {
+    _configurationDoneCompleter.complete();
+    sendResponse(null);
+  }
+
+  /// Connects to the VM Service at [uri] and initializes debugging.
+  ///
+  /// This method will be called by sub-classes when they are ready to start
+  /// a debug session and may provide a URI given by the user (in the case
+  /// of attach) or from something like a vm-service-info file or Flutter
+  /// app.debugPort message.
+  ///
+  /// The URI protocol will be changed to ws/wss but otherwise not normalised.
+  /// The caller should handle any other normalisation (such as adding /ws to
+  /// the end if required).
+  Future<void> connectDebugger(Uri uri) async {
+    // The VM Service library always expects the WebSockets URI so fix the
+    // scheme (http -> ws, https -> wss).
+    final isSecure = uri.isScheme('https') || uri.isScheme('wss');
+    uri = uri.replace(scheme: isSecure ? 'wss' : 'ws');
+
+    logger?.call('Connecting to debugger at $uri');
+    sendOutput('console', 'Connecting to VM Service at $uri\n');
+    final vmService =
+        await _vmServiceConnectUri(uri.toString(), logger: logger);
+    logger?.call('Connected to debugger at $uri!');
+
+    // TODO(dantup): VS Code currently depends on a custom dart.debuggerUris
+    // event to notify it of VM Services that become available (for example to
+    // register with the DevTools server). If this is still required, it will
+    // need implementing here (and also documented as a customisation and
+    // perhaps gated on a capability/argument).
+    this.vmService = vmService;
+
+    _subscriptions.addAll([
+      vmService.onIsolateEvent.listen(_handleIsolateEvent),
+      vmService.onDebugEvent.listen(_handleDebugEvent),
+      vmService.onLoggingEvent.listen(_handleLoggingEvent),
+      // TODO(dantup): Implement these.
+      // vmService.onExtensionEvent.listen(_handleExtensionEvent),
+      // vmService.onServiceEvent.listen(_handleServiceEvent),
+      // vmService.onStdoutEvent.listen(_handleStdoutEvent),
+      // vmService.onStderrEvent.listen(_handleStderrEvent),
+    ]);
+    await Future.wait([
+      vmService.streamListen(vm.EventStreams.kIsolate),
+      vmService.streamListen(vm.EventStreams.kDebug),
+      vmService.streamListen(vm.EventStreams.kLogging),
+      // vmService.streamListen(vm.EventStreams.kExtension),
+      // vmService.streamListen(vm.EventStreams.kService),
+      // vmService.streamListen(vm.EventStreams.kStdout),
+      // vmService.streamListen(vm.EventStreams.kStderr),
+    ]);
+
+    final vmInfo = await vmService.getVM();
+    logger?.call('Connected to ${vmInfo.name} on ${vmInfo.operatingSystem}');
+
+    // Let the subclass do any existing setup once we have a connection.
+    await debuggerConnected(vmInfo);
+
+    // Process any existing isolates that may have been created before the
+    // streams above were set up.
+    final existingIsolateRefs = vmInfo.isolates;
+    final existingIsolates = existingIsolateRefs != null
+        ? await Future.wait(existingIsolateRefs
+            .map((isolateRef) => isolateRef.id)
+            .whereNotNull()
+            .map(vmService.getIsolate))
+        : <vm.Isolate>[];
+    await Future.wait(existingIsolates.map((isolate) async {
+      // Isolates may have the "None" pauseEvent kind at startup, so infer it
+      // from the runnable field.
+      final pauseEventKind = isolate.runnable ?? false
+          ? vm.EventKind.kIsolateRunnable
+          : vm.EventKind.kIsolateStart;
+      await _isolateManager.registerIsolate(isolate, pauseEventKind);
+
+      // If the Isolate already has a Pause event we can give it to the
+      // IsolateManager to handle (if it's PausePostStart it will re-configure
+      // the isolate before resuming), otherwise we can just resume it (if it's
+      // runnable - otherwise we'll handle this when it becomes runnable in an
+      // event later).
+      if (isolate.pauseEvent?.kind?.startsWith('Pause') ?? false) {
+        await _isolateManager.handleEvent(isolate.pauseEvent!);
+      } else if (isolate.runnable == true) {
+        await _isolateManager.resumeIsolate(isolate);
+      }
+    }));
+
+    _debuggerInitializedCompleter.complete();
+  }
+
+  /// Overridden by sub-classes to perform any additional setup after the VM
+  /// Service is connected.
+  FutureOr<void> debuggerConnected(vm.VM vmInfo);
+
+  /// Overridden by sub-classes to handle when the client sends a
+  /// `disconnectRequest` (a forceful request to shut down).
+  FutureOr<void> disconnectImpl();
+
+  /// disconnectRequest is called by the client when it wants to forcefully shut
+  /// us down quickly. This comes after the `terminateRequest` which is intended
+  /// to allow a graceful shutdown.
+  ///
+  /// It's not very obvious from the names, but `terminateRequest` is sent first
+  /// (a request for a graceful shutdown) and `disconnectRequest` second (a
+  /// request for a forced shutdown).
+  ///
+  /// https://microsoft.github.io/debug-adapter-protocol/overview#debug-session-end
+  @override
+  FutureOr<void> disconnectRequest(
+    Request request,
+    DisconnectArguments? args,
+    void Function(void) sendResponse,
+  ) async {
+    await disconnectImpl();
+    sendResponse(null);
+  }
+
+  /// initializeRequest is the first request send by the client during
+  /// initialization and allows exchanging capabilities and configuration
+  /// between client and server.
+  ///
+  /// The lifecycle is described in the DAP spec here:
+  /// https://microsoft.github.io/debug-adapter-protocol/overview#initialization
+  /// with a summary in this classes description.
+  @override
+  FutureOr<void> initializeRequest(
+    Request request,
+    InitializeRequestArguments? args,
+    void Function(Capabilities) sendResponse,
+  ) async {
+    // TODO(dantup): Capture/honor editor-specific settings like linesStartAt1
+    sendResponse(Capabilities(
+      exceptionBreakpointFilters: [
+        ExceptionBreakpointsFilter(
+          filter: 'All',
+          label: 'All Exceptions',
+          defaultValue: false,
+        ),
+        ExceptionBreakpointsFilter(
+          filter: 'Unhandled',
+          label: 'Uncaught Exceptions',
+          defaultValue: true,
+        ),
+      ],
+      supportsClipboardContext: true,
+      // TODO(dantup): All of these...
+      // supportsConditionalBreakpoints: true,
+      supportsConfigurationDoneRequest: true,
+      supportsDelayedStackTraceLoading: true,
+      supportsEvaluateForHovers: true,
+      // supportsLogPoints: true,
+      // supportsRestartFrame: true,
+      supportsTerminateRequest: true,
+    ));
+
+    // This must only be sent AFTER the response!
+    sendEvent(InitializedEventBody());
+  }
+
+  /// Overridden by sub-classes to handle when the client sends a
+  /// `launchRequest` (a request to start running/debugging an app).
+  ///
+  /// Sub-classes can use the [args] field to access the arguments provided
+  /// to this request.
+  FutureOr<void> launchImpl();
+
+  /// launchRequest is called by the client when it wants us to to start the app
+  /// to be run/debug. This will only be called once (and only one of this or
+  /// attachRequest will be called).
+  @override
+  FutureOr<void> launchRequest(
+    Request request,
+    T args,
+    void Function(void) sendResponse,
+  ) async {
+    this.args = args;
+    isAttach = false;
+
+    // Don't start launching until configurationDone.
+    if (!_configurationDoneCompleter.isCompleted) {
+      logger?.call('Waiting for configurationDone request...');
+      await _configurationDoneCompleter.future;
+    }
+
+    // Delegate to the sub-class to launch the process.
+    await launchImpl();
+
+    sendResponse(null);
+  }
+
+  /// Sends an OutputEvent (without a newline, since calls to this method
+  /// may be used by buffered data).
+  void sendOutput(String category, String message) {
+    sendEvent(OutputEventBody(category: category, output: message));
+  }
+
+  /// Sends an OutputEvent for [message], prefixed with [prefix] and with [message]
+  /// indented to after the prefix.
+  ///
+  /// Assumes the output is in full lines and will always include a terminating
+  /// newline.
+  void sendPrefixedOutput(String category, String prefix, String message) {
+    final indentString = ' ' * prefix.length;
+    final indentedMessage =
+        message.trimRight().split('\n').join('\n$indentString');
+    sendOutput(category, '$prefix$indentedMessage\n');
+  }
+
+  /// Overridden by sub-classes to handle when the client sends a
+  /// `terminateRequest` (a request for a graceful shut down).
+  FutureOr<void> terminateImpl();
+
+  /// terminateRequest is called by the client when it wants us to gracefully
+  /// shut down.
+  ///
+  /// It's not very obvious from the names, but `terminateRequest` is sent first
+  /// (a request for a graceful shutdown) and `disconnectRequest` second (a
+  /// request for a forced shutdown).
+  ///
+  /// https://microsoft.github.io/debug-adapter-protocol/overview#debug-session-end
+  @override
+  FutureOr<void> terminateRequest(
+    Request request,
+    TerminateArguments? args,
+    void Function(void) sendResponse,
+  ) async {
+    terminateImpl();
+    sendResponse(null);
+  }
+
+  void _handleDebugEvent(vm.Event event) {
+    _isolateManager.handleEvent(event);
+  }
+
+  void _handleIsolateEvent(vm.Event event) {
+    _isolateManager.handleEvent(event);
+  }
+
+  /// Handles a dart:developer log() event, sending output to the client.
+  Future<void> _handleLoggingEvent(vm.Event event) async {
+    final record = event.logRecord;
+    final thread = _isolateManager.threadForIsolate(event.isolate);
+    if (record == null || thread == null) {
+      return;
+    }
+
+    /// Helper to convert to InstanceRef to a String, taking into account
+    /// [vm.InstanceKind.kNull] which is the type for the unused fields of a
+    /// log event.
+    FutureOr<String?> asString(vm.InstanceRef? ref) {
+      if (ref == null || ref.kind == vm.InstanceKind.kNull) {
+        return null;
+      }
+      // TODO(dantup): This should handle truncation and complex types.
+      return ref.valueAsString;
+    }
+
+    var loggerName = await asString(record.loggerName);
+    if (loggerName?.isEmpty ?? true) {
+      loggerName = 'log';
+    }
+    final message = await asString(record.message);
+    final error = await asString(record.error);
+    final stack = await asString(record.stackTrace);
+
+    final prefix = '[$loggerName] ';
+
+    if (message != null) {
+      sendPrefixedOutput('stdout', prefix, '$message\n');
+    }
+    if (error != null) {
+      sendPrefixedOutput('stderr', prefix, '$error\n');
+    }
+    if (stack != null) {
+      sendPrefixedOutput('stderr', prefix, '$stack\n');
+    }
+  }
+
+  /// A wrapper around the same name function from package:vm_service that
+  /// allows logging all traffic over the VM Service.
+  Future<vm.VmService> _vmServiceConnectUri(
+    String wsUri, {
+    Logger? logger,
+  }) async {
+    final socket = await WebSocket.connect(wsUri);
+    final controller = StreamController();
+    final streamClosedCompleter = Completer();
+
+    socket.listen(
+      (data) {
+        logger?.call('<== [VM] $data');
+        controller.add(data);
+      },
+      onDone: () => streamClosedCompleter.complete(),
+    );
+
+    return vm.VmService(
+      controller.stream,
+      (String message) {
+        logger?.call('==> [VM] $message');
+        socket.add(message);
+      },
+      log: logger != null ? VmServiceLogger(logger) : null,
+      disposeHandler: () => socket.close(),
+      streamClosed: streamClosedCompleter.future,
+    );
+  }
+}
+
+/// An implementation of [LaunchRequestArguments] that includes all fields used
+/// by the base Dart debug adapter.
+///
+/// This class represents the data passed from the client editor to the debug
+/// adapter in launchRequest, which is a request to start debugging an
+/// application.
+///
+/// Specialised adapters (such as Flutter) will likely extend this class with
+/// their own additional fields.
+class DartLaunchRequestArguments extends LaunchRequestArguments {
+  final String program;
+  final List<String>? args;
+  final String? cwd;
+  final String? vmServiceInfoFile;
+  final int? vmServicePort;
+  final List<String>? vmAdditionalArgs;
+  final bool? enableAsserts;
+  final bool? debugSdkLibraries;
+  final bool? evaluateGettersInDebugViews;
+  final bool? evaluateToStringInDebugViews;
+
+  /// Whether to send debug logging to clients in a custom `dart.log` event. This
+  /// is used both by the out-of-process tests to ensure the logs contain enough
+  /// information to track down issues, but also by Dart-Code to capture VM
+  /// service traffic in a unified log file.
+  final bool? sendLogsToClient;
+
+  DartLaunchRequestArguments({
+    Object? restart,
+    bool? noDebug,
+    required this.program,
+    this.args,
+    this.cwd,
+    this.vmServiceInfoFile,
+    this.vmServicePort,
+    this.vmAdditionalArgs,
+    this.enableAsserts,
+    this.debugSdkLibraries,
+    this.evaluateGettersInDebugViews,
+    this.evaluateToStringInDebugViews,
+    this.sendLogsToClient,
+  }) : super(restart: restart, noDebug: noDebug);
+
+  DartLaunchRequestArguments.fromMap(Map<String, Object?> obj)
+      : program = obj['program'] as String,
+        args = (obj['args'] as List?)?.cast<String>(),
+        cwd = obj['cwd'] as String?,
+        vmServiceInfoFile = obj['vmServiceInfoFile'] as String?,
+        vmServicePort = obj['vmServicePort'] as int?,
+        vmAdditionalArgs = (obj['vmAdditionalArgs'] as List?)?.cast<String>(),
+        enableAsserts = obj['enableAsserts'] as bool?,
+        debugSdkLibraries = obj['debugSdkLibraries'] as bool?,
+        evaluateGettersInDebugViews =
+            obj['evaluateGettersInDebugViews'] as bool?,
+        evaluateToStringInDebugViews =
+            obj['evaluateToStringInDebugViews'] as bool?,
+        sendLogsToClient = obj['sendLogsToClient'] as bool?,
+        super.fromMap(obj);
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+        'program': program,
+        if (args != null) 'args': args,
+        if (cwd != null) 'cwd': cwd,
+        if (vmServiceInfoFile != null) 'vmServiceInfoFile': vmServiceInfoFile,
+        if (vmServicePort != null) 'vmServicePort': vmServicePort,
+        if (vmAdditionalArgs != null) 'vmAdditionalArgs': vmAdditionalArgs,
+        if (enableAsserts != null) 'enableAsserts': enableAsserts,
+        if (debugSdkLibraries != null) 'debugSdkLibraries': debugSdkLibraries,
+        if (evaluateGettersInDebugViews != null)
+          'evaluateGettersInDebugViews': evaluateGettersInDebugViews,
+        if (evaluateToStringInDebugViews != null)
+          'evaluateToStringInDebugViews': evaluateToStringInDebugViews,
+        if (sendLogsToClient != null) 'sendLogsToClient': sendLogsToClient,
+      };
+
+  static DartLaunchRequestArguments fromJson(Map<String, Object?> obj) =>
+      DartLaunchRequestArguments.fromMap(obj);
+}
diff --git a/dds/lib/src/dap/adapters/dart_cli.dart b/dds/lib/src/dap/adapters/dart_cli.dart
new file mode 100644
index 0000000..311955e
--- /dev/null
+++ b/dds/lib/src/dap/adapters/dart_cli.dart
@@ -0,0 +1,183 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+import 'package:pedantic/pedantic.dart';
+import 'package:vm_service/vm_service.dart' as vm;
+
+import '../logging.dart';
+import '../protocol_generated.dart';
+import '../protocol_stream.dart';
+import 'dart.dart';
+
+/// A DAP Debug Adapter for running and debugging Dart CLI scripts.
+class DartCliDebugAdapter extends DartDebugAdapter<DartLaunchRequestArguments> {
+  Process? _process;
+
+  /// The location of the vm-service-info file (if debugging).
+  ///
+  /// This may be provided by the user (eg. if attaching) or generated by the DA.
+  File? _vmServiceInfoFile;
+
+  /// A watcher for [_vmServiceInfoFile] to detect when the VM writes the service
+  /// info file.
+  ///
+  /// Should be cancelled once the file has been successfully read.
+  StreamSubscription<FileSystemEvent>? _vmServiceInfoFileWatcher;
+
+  /// Process IDs to terminate during shutdown.
+  ///
+  /// This may be populated with pids from the VM Service to ensure we clean up
+  /// properly where signals may not be passed through the shell to the
+  /// underlying VM process.
+  /// https://github.com/Dart-Code/Dart-Code/issues/907
+  final pidsToTerminate = <int>{};
+
+  @override
+  final parseLaunchArgs = DartLaunchRequestArguments.fromJson;
+
+  DartCliDebugAdapter(ByteStreamServerChannel channel, [Logger? logger])
+      : super(channel, logger);
+
+  FutureOr<void> debuggerConnected(vm.VM vmInfo) {
+    if (!isAttach) {
+      // Capture the PID from the VM Service so that we can terminate it when
+      // cleaning up. Terminating the process might not be enough as it could be
+      // just a shell script (eg. pub on Windows) and may not pass the
+      // signal on correctly.
+      // See: https://github.com/Dart-Code/Dart-Code/issues/907
+      final pid = vmInfo.pid;
+      if (pid != null) {
+        pidsToTerminate.add(pid);
+      }
+    }
+  }
+
+  /// Called by [disconnectRequest] to request that we forcefully shut down the
+  /// app being run (or in the case of an attach, disconnect).
+  FutureOr<void> disconnectImpl() {
+    // TODO(dantup): In Dart-Code DAP, we first try again with sigint and wait
+    // for a few seconds before sending sigkill.
+    pidsToTerminate.forEach(
+      (pid) => Process.killPid(pid, ProcessSignal.sigkill),
+    );
+  }
+
+  /// Called by [launchRequest] to request that we actually start the app to be
+  /// run/debugged.
+  ///
+  /// For debugging, this should start paused, connect to the VM Service, set
+  /// breakpoints, and resume.
+  Future<void> launchImpl() async {
+    final vmPath = Platform.resolvedExecutable;
+
+    final debug = !(args.noDebug ?? false);
+    if (debug) {
+      // Create a temp folder for the VM to write the service-info-file into.
+      // Using tmpDir.createTempory() is flakey on Windows+Linux (at least
+      // on GitHub Actions) complaining the file does not exist when creating a
+      // watcher. Creating/watching a folder and writing the file into it seems
+      // to be reliable.
+      final serviceInfoFilePath = path.join(
+        Directory.systemTemp.createTempSync('dart-vm-service').path,
+        'vm.json',
+      );
+      _vmServiceInfoFile = File(serviceInfoFilePath);
+      _vmServiceInfoFileWatcher = _vmServiceInfoFile?.parent
+          .watch(events: FileSystemEvent.all)
+          .where((event) => event.path == _vmServiceInfoFile?.path)
+          .listen(
+            _handleVmServiceInfoEvent,
+            onError: (e) => logger?.call('Ignoring exception from watcher: $e'),
+          );
+    }
+
+    // TODO(dantup): Currently this just spawns the new VM and completely
+    // ignores DDS. Figure out how this will ultimately work - will we just wrap
+    // the call to initDebugger() with something that starts DDS?
+    final vmServiceInfoFile = _vmServiceInfoFile;
+    final vmArgs = <String>[
+      '--no-serve-devtools',
+      if (debug) ...[
+        '--enable-vm-service=${args.vmServicePort ?? 0}',
+        '--pause_isolates_on_start=true',
+      ],
+      if (debug && vmServiceInfoFile != null) ...[
+        '-DSILENT_OBSERVATORY=true',
+        '--write-service-info=${Uri.file(vmServiceInfoFile.path)}'
+      ],
+      // Default to asserts on, this seems like the most useful behaviour for
+      // editor-spawned debug sessions.
+      if (args.enableAsserts ?? true) '--enable-asserts'
+    ];
+    final processArgs = [
+      ...vmArgs,
+      args.program,
+      ...?args.args,
+    ];
+
+    logger?.call('Spawning $vmPath with $processArgs in ${args.cwd}');
+    final process = await Process.start(
+      vmPath,
+      processArgs,
+      workingDirectory: args.cwd,
+    );
+    _process = process;
+    pidsToTerminate.add(process.pid);
+
+    process.stdout.listen(_handleStdout);
+    process.stderr.listen(_handleStderr);
+    unawaited(process.exitCode.then(_handleExitCode));
+  }
+
+  /// Called by [terminateRequest] to request that we gracefully shut down the
+  /// app being run (or in the case of an attach, disconnect).
+  FutureOr<void> terminateImpl() async {
+    pidsToTerminate.forEach(
+      (pid) => Process.killPid(pid, ProcessSignal.sigint),
+    );
+    await _process?.exitCode;
+  }
+
+  void _handleExitCode(int code) {
+    final codeSuffix = code == 0 ? '' : ' ($code)';
+    logger?.call('Process exited ($code)');
+    // Always add a leading newline since the last written text might not have
+    // had one.
+    sendOutput('console', '\nExited$codeSuffix.');
+    sendEvent(TerminatedEventBody());
+  }
+
+  void _handleStderr(List<int> data) {
+    sendOutput('stderr', utf8.decode(data));
+  }
+
+  void _handleStdout(List<int> data) {
+    sendOutput('stdout', utf8.decode(data));
+  }
+
+  /// Handles file watcher events for the vm-service-info file and connects the
+  /// debugger.
+  ///
+  /// The vm-service-info file is written by the VM when we start the app/script
+  /// to debug and contains the VM Service URI. This allows us to access the
+  /// auth token without needing to have the URI printed to/scraped from stdout.
+  void _handleVmServiceInfoEvent(FileSystemEvent event) {
+    try {
+      final content = _vmServiceInfoFile!.readAsStringSync();
+      final json = jsonDecode(content);
+      final uri = Uri.parse(json['uri']);
+      unawaited(connectDebugger(uri));
+      _vmServiceInfoFileWatcher?.cancel();
+    } catch (e) {
+      // It's possible we tried to read the file before it was completely
+      // written so ignore and try again on the next event.
+      logger?.call('Ignoring error parsing vm-service-info file: $e');
+    }
+  }
+}
diff --git a/dds/lib/src/dap/base_debug_adapter.dart b/dds/lib/src/dap/base_debug_adapter.dart
new file mode 100644
index 0000000..0c00630
--- /dev/null
+++ b/dds/lib/src/dap/base_debug_adapter.dart
@@ -0,0 +1,209 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'logging.dart';
+import 'protocol_common.dart';
+import 'protocol_generated.dart';
+import 'protocol_stream.dart';
+
+typedef _FromJsonHandler<T> = T Function(Map<String, Object?>);
+typedef _NullableFromJsonHandler<T> = T? Function(Map<String, Object?>?);
+
+/// A base class for debug adapters.
+///
+/// Communicates over a [ByteStreamServerChannel] and turns messages into
+/// appropriate method calls/events.
+///
+/// This class does not implement any DA functionality, only message handling.
+abstract class BaseDebugAdapter<TLaunchArgs extends LaunchRequestArguments> {
+  int _sequence = 1;
+  final ByteStreamServerChannel _channel;
+  final Logger? logger;
+
+  BaseDebugAdapter(this._channel, this.logger) {
+    _channel.listen(_handleIncomingMessage);
+  }
+
+  /// Parses arguments for [launchRequest] into a type of [TLaunchArgs].
+  ///
+  /// This method must be implemented by the implementing class using a class
+  /// that corresponds to the arguments it expects (these may differ between
+  /// Dart CLI, Dart tests, Flutter, Flutter tests).
+  TLaunchArgs Function(Map<String, Object?>) get parseLaunchArgs;
+
+  FutureOr<void> attachRequest(
+    Request request,
+    TLaunchArgs args,
+    void Function(void) sendResponse,
+  );
+
+  FutureOr<void> configurationDoneRequest(
+    Request request,
+    ConfigurationDoneArguments? args,
+    void Function(void) sendResponse,
+  );
+
+  FutureOr<void> disconnectRequest(
+    Request request,
+    DisconnectArguments? args,
+    void Function(void) sendResponse,
+  );
+
+  /// Calls [handler] for an incoming request, using [fromJson] to parse its
+  /// arguments from the request.
+  ///
+  /// [handler] will be provided a function [sendResponse] that it can use to
+  /// sends its response without needing to build a [Response] from fields on
+  /// the request.
+  ///
+  /// [handler] must _always_ call [sendResponse], even if the response does not
+  /// require a body.
+  ///
+  /// If [handler] throws, its exception will be sent as an error response.
+  FutureOr<void> handle<TArg, TResp>(
+    Request request,
+    FutureOr<void> Function(Request, TArg, void Function(TResp)) handler,
+    TArg Function(Map<String, Object?>) fromJson,
+  ) async {
+    final args = request.arguments != null
+        ? fromJson(request.arguments as Map<String, Object?>)
+        // arguments are only valid to be null then TArg is nullable.
+        : null as TArg;
+
+    // Because handlers may need to send responses before they have finished
+    // executing (for example, initializeRequest needs to send its response
+    // before sending InitializedEvent()), we pass in a function `sendResponse`
+    // rather than using a return value.
+    var sendResponseCalled = false;
+    void sendResponse(TResp responseBody) {
+      assert(!sendResponseCalled,
+          'sendResponse was called multiple times by ${request.command}');
+      sendResponseCalled = true;
+      final response = Response(
+        success: true,
+        requestSeq: request.seq,
+        seq: _sequence++,
+        command: request.command,
+        body: responseBody,
+      );
+      _channel.sendResponse(response);
+    }
+
+    try {
+      await handler(request, args, sendResponse);
+      assert(sendResponseCalled,
+          'sendResponse was not called in ${request.command}');
+    } catch (e, s) {
+      final response = Response(
+        success: false,
+        requestSeq: request.seq,
+        seq: _sequence++,
+        command: request.command,
+        message: '$e',
+        body: '$s',
+      );
+      _channel.sendResponse(response);
+    }
+  }
+
+  FutureOr<void> initializeRequest(
+    Request request,
+    InitializeRequestArguments args,
+    void Function(Capabilities) sendResponse,
+  );
+
+  FutureOr<void> launchRequest(
+      Request request, TLaunchArgs args, void Function(void) sendResponse);
+
+  /// Sends an event, lookup up the event type based on the runtimeType of
+  /// [body].
+  void sendEvent(EventBody body) {
+    final event = Event(
+      seq: _sequence++,
+      event: eventTypes[body.runtimeType]!,
+      body: body,
+    );
+    _channel.sendEvent(event);
+  }
+
+  /// Sends a request to the client, looking up the request type based on the
+  /// runtimeType of [arguments].
+  void sendRequest(RequestArguments arguments) {
+    final request = Request(
+      seq: _sequence++,
+      command: commandTypes[arguments.runtimeType]!,
+      arguments: arguments,
+    );
+    _channel.sendRequest(request);
+  }
+
+  FutureOr<void> terminateRequest(
+    Request request,
+    TerminateArguments? args,
+    void Function(void) sendResponse,
+  );
+
+  /// Wraps a fromJson handler for requests that allow null arguments.
+  _NullableFromJsonHandler<T> _allowNullArg<T extends RequestArguments>(
+    _FromJsonHandler<T> fromJson,
+  ) {
+    return (data) => data == null ? null : fromJson(data);
+  }
+
+  /// Handles incoming messages from the client editor.
+  void _handleIncomingMessage(ProtocolMessage message) {
+    if (message is Request) {
+      _handleIncomingRequest(message);
+    } else if (message is Response) {
+      _handleIncomingResponse(message);
+    } else {
+      throw Exception('Unknown Protocol message ${message.type}');
+    }
+  }
+
+  /// Handles an incoming request, calling the appropriate method to handle it.
+  void _handleIncomingRequest(Request request) {
+    if (request.command == 'initialize') {
+      handle(request, initializeRequest, InitializeRequestArguments.fromJson);
+    } else if (request.command == 'launch') {
+      handle(request, launchRequest, parseLaunchArgs);
+    } else if (request.command == 'attach') {
+      handle(request, attachRequest, parseLaunchArgs);
+    } else if (request.command == 'terminate') {
+      handle(
+        request,
+        terminateRequest,
+        _allowNullArg(TerminateArguments.fromJson),
+      );
+    } else if (request.command == 'disconnect') {
+      handle(
+        request,
+        disconnectRequest,
+        _allowNullArg(DisconnectArguments.fromJson),
+      );
+    } else if (request.command == 'configurationDone') {
+      handle(
+        request,
+        configurationDoneRequest,
+        _allowNullArg(ConfigurationDoneArguments.fromJson),
+      );
+    } else {
+      final response = Response(
+        success: false,
+        requestSeq: request.seq,
+        seq: _sequence++,
+        command: request.command,
+        message: 'Unknown command: ${request.command}',
+      );
+      _channel.sendResponse(response);
+    }
+  }
+
+  void _handleIncomingResponse(Response response) {
+    // TODO(dantup): Implement this when the server sends requests to the client
+    // (for example runInTerminalRequest).
+  }
+}
diff --git a/dds/lib/src/dap/isolate_manager.dart b/dds/lib/src/dap/isolate_manager.dart
new file mode 100644
index 0000000..3bbc226
--- /dev/null
+++ b/dds/lib/src/dap/isolate_manager.dart
@@ -0,0 +1,269 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:vm_service/vm_service.dart' as vm;
+
+import 'adapters/dart.dart';
+import 'protocol_generated.dart';
+
+/// Manages state of Isolates (called Threads by the DAP protocol).
+///
+/// Handles incoming Isolate and Debug events to track the lifetime of isolates
+/// and updating breakpoints for each isolate as necessary.
+class IsolateManager {
+  // TODO(dantup): This class has a lot of overlap with the same-named class
+  //  in DDS. Review what can be shared.
+  final DartDebugAdapter _adapter;
+  final Map<String, Completer<void>> _isolateRegistrations = {};
+  final Map<String, ThreadInfo> _threadsByIsolateId = {};
+  final Map<int, ThreadInfo> _threadsByThreadId = {};
+  int _nextThreadNumber = 1;
+
+  IsolateManager(this._adapter);
+
+  /// A list of all current active isolates.
+  ///
+  /// When isolates exit, they will no longer be returned in this list, although
+  /// due to the async nature, it's not guaranteed that threads in this list have
+  /// not exited between accessing this list and trying to use the results.
+  List<ThreadInfo> get threads => _threadsByIsolateId.values.toList();
+
+  Future<T> getObject<T extends vm.Response>(
+      vm.IsolateRef isolate, vm.ObjRef object) async {
+    final res = await _adapter.vmService?.getObject(isolate.id!, object.id!);
+    return res as T;
+  }
+
+  /// Handles Isolate and Debug events
+  FutureOr<void> handleEvent(vm.Event event) async {
+    final isolateId = event.isolate?.id;
+    if (isolateId == null) {
+      return;
+    }
+
+    // Delay processing any events until the debugger initialization has
+    // finished running, as events may arrive (for ex. IsolateRunnable) while
+    // it's doing is own initialization that this may interfere with.
+    await _adapter.debuggerInitialized;
+
+    final eventKind = event.kind;
+    if (eventKind == vm.EventKind.kIsolateStart ||
+        eventKind == vm.EventKind.kIsolateRunnable) {
+      await registerIsolate(event.isolate!, eventKind!);
+    }
+
+    // Additionally, ensure the thread registration has completed before trying
+    // to process any other events. This is to cover the case where we are
+    // processing the above registerIsolate call in the handler for one isolate
+    // event but another one arrives and gets us here before the registration
+    // above (in the other event handler) has finished.
+    await _isolateRegistrations[isolateId]?.future;
+
+    if (eventKind == vm.EventKind.kIsolateExit) {
+      await _handleExit(event);
+    } else if (eventKind?.startsWith('Pause') ?? false) {
+      await _handlePause(event);
+    } else if (eventKind == vm.EventKind.kResume) {
+      await _handleResumed(event);
+    }
+  }
+
+  /// Registers a new isolate that exists at startup, or has subsequently been
+  /// created.
+  ///
+  /// New isolates will be configured with the correct pause-exception behaviour,
+  /// libraries will be marked as debuggable if appropriate, and breakpoints
+  /// sent.
+  FutureOr<void> registerIsolate(
+    vm.IsolateRef isolate,
+    String eventKind,
+  ) async {
+    // Ensure the completer is set up before doing any async work, so future
+    // events can wait on it.
+    final registrationCompleter =
+        _isolateRegistrations.putIfAbsent(isolate.id!, () => Completer<void>());
+
+    final info = _threadsByIsolateId.putIfAbsent(
+      isolate.id!,
+      () {
+        // The first time we see an isolate, start tracking it.
+        final info = ThreadInfo(_nextThreadNumber++, isolate);
+        _threadsByThreadId[info.threadId] = info;
+        // And notify the client about it.
+        _adapter.sendEvent(
+          ThreadEventBody(reason: 'started', threadId: info.threadId),
+        );
+        return info;
+      },
+    );
+
+    // If it's just become runnable (IsolateRunnable), configure the isolate
+    // by sending breakpoints etc.
+    if (eventKind == vm.EventKind.kIsolateRunnable && !info.runnable) {
+      info.runnable = true;
+      await _configureIsolate(isolate);
+      registrationCompleter.complete();
+    }
+  }
+
+  FutureOr<void> resumeIsolate(vm.IsolateRef isolateRef,
+      [String? resumeType]) async {
+    final isolateId = isolateRef.id;
+    if (isolateId == null) {
+      return;
+    }
+
+    final thread = _threadsByIsolateId[isolateId];
+    if (thread == null) {
+      return;
+    }
+
+    await resumeThread(thread.threadId);
+  }
+
+  /// Resumes (or steps) an isolate using its client [threadId].
+  ///
+  /// If the isolate is not paused, or already has a pending resume request
+  /// in-flight, a request will not be sent.
+  ///
+  /// If the isolate is paused at an async suspension and the [resumeType] is
+  /// [vm.StepOption.kOver], a [StepOption.kOverAsyncSuspension] step will be
+  /// sent instead.
+  Future<void> resumeThread(int threadId, [String? resumeType]) async {
+    final thread = _threadsByThreadId[threadId];
+    if (thread == null) {
+      throw 'Thread $threadId was not found';
+    }
+
+    // Check this thread hasn't already been resumed by another handler in the
+    // meantime (for example if the user performs a hot restart or something
+    // while we processing some previous events).
+    if (!thread.paused || thread.hasPendingResume) {
+      return;
+    }
+
+    // We always assume that a step when at an async suspension is intended to
+    // be an async step.
+    if (resumeType == vm.StepOption.kOver && thread.atAsyncSuspension) {
+      resumeType = vm.StepOption.kOverAsyncSuspension;
+    }
+
+    thread.hasPendingResume = true;
+    try {
+      await _adapter.vmService?.resume(thread.isolate.id!, step: resumeType);
+    } finally {
+      thread.hasPendingResume = false;
+    }
+  }
+
+  ThreadInfo? threadForIsolate(vm.IsolateRef? isolate) =>
+      isolate?.id != null ? _threadsByIsolateId[isolate!.id!] : null;
+
+  /// Configures a new isolate, setting it's exception-pause mode, which
+  /// libraries are debuggable, and sending all breakpoints.
+  FutureOr<void> _configureIsolate(vm.IsolateRef isolate) async {
+    // TODO(dantup): set library debuggable, exception pause mode, breakpoints
+  }
+
+  FutureOr<void> _handleExit(vm.Event event) {
+    final isolate = event.isolate!;
+    final isolateId = isolate.id!;
+    final thread = _threadsByIsolateId[isolateId];
+    if (thread != null) {
+      // Notify the client.
+      _adapter.sendEvent(
+        ThreadEventBody(reason: 'exited', threadId: thread.threadId),
+      );
+      _threadsByIsolateId.remove(isolateId);
+      _threadsByThreadId.remove(thread.threadId);
+    }
+  }
+
+  /// Handles a pause event.
+  ///
+  /// For [vm.EventKind.kPausePostRequest] which occurs after a restart, the isolate
+  /// will be re-configured (pause-exception behaviour, debuggable libraries,
+  /// breakpoints) and then resumed.
+  ///
+  /// For [vm.EventKind.kPauseStart], the isolate will be resumed.
+  ///
+  /// For breakpoints with conditions that are not met and for logpoints, the
+  /// isolate will be automatically resumed.
+  ///
+  /// For all other pause types, the isolate will remain paused and a
+  /// corresponding "Stopped" event sent to the editor.
+  FutureOr<void> _handlePause(vm.Event event) async {
+    final eventKind = event.kind;
+    final isolate = event.isolate!;
+    final thread = _threadsByIsolateId[isolate.id!];
+
+    if (thread == null) {
+      return;
+    }
+
+    thread.atAsyncSuspension = event.atAsyncSuspension ?? false;
+    thread.paused = true;
+    thread.pauseEvent = event;
+
+    // For PausePostRequest we need to re-send all breakpoints; this happens
+    // after a hot restart.
+    if (eventKind == vm.EventKind.kPausePostRequest) {
+      _configureIsolate(isolate);
+      await resumeThread(thread.threadId);
+    } else if (eventKind == vm.EventKind.kPauseStart) {
+      await resumeThread(thread.threadId);
+    } else {
+      // PauseExit, PauseBreakpoint, PauseInterrupted, PauseException
+      var reason = 'pause';
+
+      if (eventKind == vm.EventKind.kPauseBreakpoint &&
+          (event.pauseBreakpoints?.isNotEmpty ?? false)) {
+        reason = 'breakpoint';
+      } else if (eventKind == vm.EventKind.kPauseBreakpoint) {
+        reason = 'step';
+      } else if (eventKind == vm.EventKind.kPauseException) {
+        reason = 'exception';
+      }
+
+      // TODO(dantup): Store exception.
+
+      // Notify the client.
+      _adapter.sendEvent(
+        StoppedEventBody(reason: reason, threadId: thread.threadId),
+      );
+    }
+  }
+
+  /// Handles a resume event from the VM, updating our local state.
+  FutureOr<void> _handleResumed(vm.Event event) {
+    final isolate = event.isolate!;
+    final thread = _threadsByIsolateId[isolate.id!];
+    if (thread != null) {
+      thread.paused = false;
+      thread.pauseEvent = null;
+      thread.exceptionReference = null;
+    }
+  }
+}
+
+/// Holds state for a single Isolate/Thread.
+class ThreadInfo {
+  final vm.IsolateRef isolate;
+  final int threadId;
+  var runnable = false;
+  var atAsyncSuspension = false;
+  int? exceptionReference;
+  var paused = false;
+
+  // The most recent pauseEvent for this isolate.
+  vm.Event? pauseEvent;
+
+  /// Whether this isolate has an in-flight resume request that has not yet
+  /// been responded to.
+  var hasPendingResume = false;
+
+  ThreadInfo(this.threadId, this.isolate);
+}
diff --git a/dds/lib/src/dap/logging.dart b/dds/lib/src/dap/logging.dart
new file mode 100644
index 0000000..a7a7ffd
--- /dev/null
+++ b/dds/lib/src/dap/logging.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:vm_service/vm_service.dart' as vm;
+
+typedef Logger = void Function(String);
+
+/// Wraps a [Logger] as a [vm/Log] to be passed to the VM Service library.
+class VmServiceLogger extends vm.Log {
+  final Logger _logger;
+
+  VmServiceLogger(this._logger);
+
+  @override
+  void severe(String message) => _logger.call('ERROR: $message');
+
+  @override
+  void warning(String message) => _logger.call('WARN: $message');
+}
diff --git a/dds/lib/src/dap/protocol_common.dart b/dds/lib/src/dap/protocol_common.dart
new file mode 100644
index 0000000..6295243
--- /dev/null
+++ b/dds/lib/src/dap/protocol_common.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A base class for (spec-generated) classes that represent the `body` of a an
+/// event.
+abstract class EventBody {
+  static bool canParse(Object? obj) => obj is Map<String, Object?>?;
+}
+
+/// A base class for (spec-generated) classes that represent the `arguments` of
+/// a request.
+abstract class RequestArguments {
+  static bool canParse(Object? obj) => obj is Map<String, Object?>?;
+}
diff --git a/dds/lib/src/dap/protocol_generated.dart b/dds/lib/src/dap/protocol_generated.dart
new file mode 100644
index 0000000..30620e4
--- /dev/null
+++ b/dds/lib/src/dap/protocol_generated.dart
@@ -0,0 +1,8934 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code was auto-generated by tool/dap/generate_all.dart - do not hand-edit!
+
+import 'package:dds/src/dap/protocol_common.dart';
+import 'package:dds/src/dap/protocol_special.dart';
+
+/// Arguments for 'attach' request. Additional attributes are implementation
+/// specific.
+class AttachRequestArguments extends RequestArguments {
+  /// Optional data from the previous, restarted session.
+  /// The data is sent as the 'restart' attribute of the 'terminated' event.
+  /// The client should leave the data intact.
+  final Object? restart;
+
+  static AttachRequestArguments fromJson(Map<String, Object?> obj) =>
+      AttachRequestArguments.fromMap(obj);
+
+  AttachRequestArguments({
+    this.restart,
+  });
+
+  AttachRequestArguments.fromMap(Map<String, Object?> obj)
+      : restart = obj['__restart'];
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (restart != null) '__restart': restart,
+      };
+}
+
+/// Response to 'attach' request. This is just an acknowledgement, so no body
+/// field is required.
+class AttachResponse extends Response {
+  static AttachResponse fromJson(Map<String, Object?> obj) =>
+      AttachResponse.fromMap(obj);
+
+  AttachResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  AttachResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Information about a Breakpoint created in setBreakpoints,
+/// setFunctionBreakpoints, setInstructionBreakpoints, or setDataBreakpoints.
+class Breakpoint {
+  /// An optional start column of the actual range covered by the breakpoint.
+  final int? column;
+
+  /// An optional end column of the actual range covered by the breakpoint.
+  /// If no end line is given, then the end column is assumed to be in the start
+  /// line.
+  final int? endColumn;
+
+  /// An optional end line of the actual range covered by the breakpoint.
+  final int? endLine;
+
+  /// An optional identifier for the breakpoint. It is needed if breakpoint
+  /// events are used to update or remove breakpoints.
+  final int? id;
+
+  /// An optional memory reference to where the breakpoint is set.
+  final String? instructionReference;
+
+  /// The start line of the actual range covered by the breakpoint.
+  final int? line;
+
+  /// An optional message about the state of the breakpoint.
+  /// This is shown to the user and can be used to explain why a breakpoint
+  /// could not be verified.
+  final String? message;
+
+  /// An optional offset from the instruction reference.
+  /// This can be negative.
+  final int? offset;
+
+  /// The source where the breakpoint is located.
+  final Source? source;
+
+  /// If true breakpoint could be set (but not necessarily at the desired
+  /// location).
+  final bool verified;
+
+  static Breakpoint fromJson(Map<String, Object?> obj) =>
+      Breakpoint.fromMap(obj);
+
+  Breakpoint({
+    this.column,
+    this.endColumn,
+    this.endLine,
+    this.id,
+    this.instructionReference,
+    this.line,
+    this.message,
+    this.offset,
+    this.source,
+    required this.verified,
+  });
+
+  Breakpoint.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        id = obj['id'] as int?,
+        instructionReference = obj['instructionReference'] as String?,
+        line = obj['line'] as int?,
+        message = obj['message'] as String?,
+        offset = obj['offset'] as int?,
+        source = obj['source'] == null
+            ? null
+            : Source.fromJson(obj['source'] as Map<String, Object?>),
+        verified = obj['verified'] as bool;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['id'] is! int?) {
+      return false;
+    }
+    if (obj['instructionReference'] is! String?) {
+      return false;
+    }
+    if (obj['line'] is! int?) {
+      return false;
+    }
+    if (obj['message'] is! String?) {
+      return false;
+    }
+    if (obj['offset'] is! int?) {
+      return false;
+    }
+    if (!Source?.canParse(obj['source'])) {
+      return false;
+    }
+    if (obj['verified'] is! bool) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        if (id != null) 'id': id,
+        if (instructionReference != null)
+          'instructionReference': instructionReference,
+        if (line != null) 'line': line,
+        if (message != null) 'message': message,
+        if (offset != null) 'offset': offset,
+        if (source != null) 'source': source,
+        'verified': verified,
+      };
+}
+
+/// Properties of a breakpoint location returned from the 'breakpointLocations'
+/// request.
+class BreakpointLocation {
+  /// Optional start column of breakpoint location.
+  final int? column;
+
+  /// Optional end column of breakpoint location if the location covers a range.
+  final int? endColumn;
+
+  /// Optional end line of breakpoint location if the location covers a range.
+  final int? endLine;
+
+  /// Start line of breakpoint location.
+  final int line;
+
+  static BreakpointLocation fromJson(Map<String, Object?> obj) =>
+      BreakpointLocation.fromMap(obj);
+
+  BreakpointLocation({
+    this.column,
+    this.endColumn,
+    this.endLine,
+    required this.line,
+  });
+
+  BreakpointLocation.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        line = obj['line'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['line'] is! int) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        'line': line,
+      };
+}
+
+/// Arguments for 'breakpointLocations' request.
+class BreakpointLocationsArguments extends RequestArguments {
+  /// Optional start column of range to search possible breakpoint locations in.
+  /// If no start column is given, the first column in the start line is
+  /// assumed.
+  final int? column;
+
+  /// Optional end column of range to search possible breakpoint locations in.
+  /// If no end column is given, then it is assumed to be in the last column of
+  /// the end line.
+  final int? endColumn;
+
+  /// Optional end line of range to search possible breakpoint locations in. If
+  /// no end line is given, then the end line is assumed to be the start line.
+  final int? endLine;
+
+  /// Start line of range to search possible breakpoint locations in. If only
+  /// the line is specified, the request returns all possible locations in that
+  /// line.
+  final int line;
+
+  /// The source location of the breakpoints; either 'source.path' or
+  /// 'source.reference' must be specified.
+  final Source source;
+
+  static BreakpointLocationsArguments fromJson(Map<String, Object?> obj) =>
+      BreakpointLocationsArguments.fromMap(obj);
+
+  BreakpointLocationsArguments({
+    this.column,
+    this.endColumn,
+    this.endLine,
+    required this.line,
+    required this.source,
+  });
+
+  BreakpointLocationsArguments.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        line = obj['line'] as int,
+        source = Source.fromJson(obj['source'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['line'] is! int) {
+      return false;
+    }
+    if (!Source.canParse(obj['source'])) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        'line': line,
+        'source': source,
+      };
+}
+
+/// Response to 'breakpointLocations' request.
+/// Contains possible locations for source breakpoints.
+class BreakpointLocationsResponse extends Response {
+  static BreakpointLocationsResponse fromJson(Map<String, Object?> obj) =>
+      BreakpointLocationsResponse.fromMap(obj);
+
+  BreakpointLocationsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  BreakpointLocationsResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'cancel' request.
+class CancelArguments extends RequestArguments {
+  /// The ID (attribute 'progressId') of the progress to cancel. If missing no
+  /// progress is cancelled.
+  /// Both a 'requestId' and a 'progressId' can be specified in one request.
+  final String? progressId;
+
+  /// The ID (attribute 'seq') of the request to cancel. If missing no request
+  /// is cancelled.
+  /// Both a 'requestId' and a 'progressId' can be specified in one request.
+  final int? requestId;
+
+  static CancelArguments fromJson(Map<String, Object?> obj) =>
+      CancelArguments.fromMap(obj);
+
+  CancelArguments({
+    this.progressId,
+    this.requestId,
+  });
+
+  CancelArguments.fromMap(Map<String, Object?> obj)
+      : progressId = obj['progressId'] as String?,
+        requestId = obj['requestId'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['progressId'] is! String?) {
+      return false;
+    }
+    if (obj['requestId'] is! int?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (progressId != null) 'progressId': progressId,
+        if (requestId != null) 'requestId': requestId,
+      };
+}
+
+/// Response to 'cancel' request. This is just an acknowledgement, so no body
+/// field is required.
+class CancelResponse extends Response {
+  static CancelResponse fromJson(Map<String, Object?> obj) =>
+      CancelResponse.fromMap(obj);
+
+  CancelResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  CancelResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Information about the capabilities of a debug adapter.
+class Capabilities {
+  /// The set of additional module information exposed by the debug adapter.
+  final List<ColumnDescriptor>? additionalModuleColumns;
+
+  /// The set of characters that should trigger completion in a REPL. If not
+  /// specified, the UI should assume the '.' character.
+  final List<String>? completionTriggerCharacters;
+
+  /// Available exception filter options for the 'setExceptionBreakpoints'
+  /// request.
+  final List<ExceptionBreakpointsFilter>? exceptionBreakpointFilters;
+
+  /// The debug adapter supports the 'suspendDebuggee' attribute on the
+  /// 'disconnect' request.
+  final bool? supportSuspendDebuggee;
+
+  /// The debug adapter supports the 'terminateDebuggee' attribute on the
+  /// 'disconnect' request.
+  final bool? supportTerminateDebuggee;
+
+  /// Checksum algorithms supported by the debug adapter.
+  final List<ChecksumAlgorithm>? supportedChecksumAlgorithms;
+
+  /// The debug adapter supports the 'breakpointLocations' request.
+  final bool? supportsBreakpointLocationsRequest;
+
+  /// The debug adapter supports the 'cancel' request.
+  final bool? supportsCancelRequest;
+
+  /// The debug adapter supports the 'clipboard' context value in the 'evaluate'
+  /// request.
+  final bool? supportsClipboardContext;
+
+  /// The debug adapter supports the 'completions' request.
+  final bool? supportsCompletionsRequest;
+
+  /// The debug adapter supports conditional breakpoints.
+  final bool? supportsConditionalBreakpoints;
+
+  /// The debug adapter supports the 'configurationDone' request.
+  final bool? supportsConfigurationDoneRequest;
+
+  /// The debug adapter supports data breakpoints.
+  final bool? supportsDataBreakpoints;
+
+  /// The debug adapter supports the delayed loading of parts of the stack,
+  /// which requires that both the 'startFrame' and 'levels' arguments and an
+  /// optional 'totalFrames' result of the 'StackTrace' request are supported.
+  final bool? supportsDelayedStackTraceLoading;
+
+  /// The debug adapter supports the 'disassemble' request.
+  final bool? supportsDisassembleRequest;
+
+  /// The debug adapter supports a (side effect free) evaluate request for data
+  /// hovers.
+  final bool? supportsEvaluateForHovers;
+
+  /// The debug adapter supports 'filterOptions' as an argument on the
+  /// 'setExceptionBreakpoints' request.
+  final bool? supportsExceptionFilterOptions;
+
+  /// The debug adapter supports the 'exceptionInfo' request.
+  final bool? supportsExceptionInfoRequest;
+
+  /// The debug adapter supports 'exceptionOptions' on the
+  /// setExceptionBreakpoints request.
+  final bool? supportsExceptionOptions;
+
+  /// The debug adapter supports function breakpoints.
+  final bool? supportsFunctionBreakpoints;
+
+  /// The debug adapter supports the 'gotoTargets' request.
+  final bool? supportsGotoTargetsRequest;
+
+  /// The debug adapter supports breakpoints that break execution after a
+  /// specified number of hits.
+  final bool? supportsHitConditionalBreakpoints;
+
+  /// The debug adapter supports adding breakpoints based on instruction
+  /// references.
+  final bool? supportsInstructionBreakpoints;
+
+  /// The debug adapter supports the 'loadedSources' request.
+  final bool? supportsLoadedSourcesRequest;
+
+  /// The debug adapter supports logpoints by interpreting the 'logMessage'
+  /// attribute of the SourceBreakpoint.
+  final bool? supportsLogPoints;
+
+  /// The debug adapter supports the 'modules' request.
+  final bool? supportsModulesRequest;
+
+  /// The debug adapter supports the 'readMemory' request.
+  final bool? supportsReadMemoryRequest;
+
+  /// The debug adapter supports restarting a frame.
+  final bool? supportsRestartFrame;
+
+  /// The debug adapter supports the 'restart' request. In this case a client
+  /// should not implement 'restart' by terminating and relaunching the adapter
+  /// but by calling the RestartRequest.
+  final bool? supportsRestartRequest;
+
+  /// The debug adapter supports the 'setExpression' request.
+  final bool? supportsSetExpression;
+
+  /// The debug adapter supports setting a variable to a value.
+  final bool? supportsSetVariable;
+
+  /// The debug adapter supports stepping back via the 'stepBack' and
+  /// 'reverseContinue' requests.
+  final bool? supportsStepBack;
+
+  /// The debug adapter supports the 'stepInTargets' request.
+  final bool? supportsStepInTargetsRequest;
+
+  /// The debug adapter supports stepping granularities (argument 'granularity')
+  /// for the stepping requests.
+  final bool? supportsSteppingGranularity;
+
+  /// The debug adapter supports the 'terminate' request.
+  final bool? supportsTerminateRequest;
+
+  /// The debug adapter supports the 'terminateThreads' request.
+  final bool? supportsTerminateThreadsRequest;
+
+  /// The debug adapter supports a 'format' attribute on the stackTraceRequest,
+  /// variablesRequest, and evaluateRequest.
+  final bool? supportsValueFormattingOptions;
+
+  static Capabilities fromJson(Map<String, Object?> obj) =>
+      Capabilities.fromMap(obj);
+
+  Capabilities({
+    this.additionalModuleColumns,
+    this.completionTriggerCharacters,
+    this.exceptionBreakpointFilters,
+    this.supportSuspendDebuggee,
+    this.supportTerminateDebuggee,
+    this.supportedChecksumAlgorithms,
+    this.supportsBreakpointLocationsRequest,
+    this.supportsCancelRequest,
+    this.supportsClipboardContext,
+    this.supportsCompletionsRequest,
+    this.supportsConditionalBreakpoints,
+    this.supportsConfigurationDoneRequest,
+    this.supportsDataBreakpoints,
+    this.supportsDelayedStackTraceLoading,
+    this.supportsDisassembleRequest,
+    this.supportsEvaluateForHovers,
+    this.supportsExceptionFilterOptions,
+    this.supportsExceptionInfoRequest,
+    this.supportsExceptionOptions,
+    this.supportsFunctionBreakpoints,
+    this.supportsGotoTargetsRequest,
+    this.supportsHitConditionalBreakpoints,
+    this.supportsInstructionBreakpoints,
+    this.supportsLoadedSourcesRequest,
+    this.supportsLogPoints,
+    this.supportsModulesRequest,
+    this.supportsReadMemoryRequest,
+    this.supportsRestartFrame,
+    this.supportsRestartRequest,
+    this.supportsSetExpression,
+    this.supportsSetVariable,
+    this.supportsStepBack,
+    this.supportsStepInTargetsRequest,
+    this.supportsSteppingGranularity,
+    this.supportsTerminateRequest,
+    this.supportsTerminateThreadsRequest,
+    this.supportsValueFormattingOptions,
+  });
+
+  Capabilities.fromMap(Map<String, Object?> obj)
+      : additionalModuleColumns = (obj['additionalModuleColumns'] as List?)
+            ?.map((item) =>
+                ColumnDescriptor.fromJson(item as Map<String, Object?>))
+            .toList(),
+        completionTriggerCharacters =
+            (obj['completionTriggerCharacters'] as List?)
+                ?.map((item) => item as String)
+                .toList(),
+        exceptionBreakpointFilters =
+            (obj['exceptionBreakpointFilters'] as List?)
+                ?.map((item) => ExceptionBreakpointsFilter.fromJson(
+                    item as Map<String, Object?>))
+                .toList(),
+        supportSuspendDebuggee = obj['supportSuspendDebuggee'] as bool?,
+        supportTerminateDebuggee = obj['supportTerminateDebuggee'] as bool?,
+        supportedChecksumAlgorithms =
+            (obj['supportedChecksumAlgorithms'] as List?)
+                ?.map((item) =>
+                    ChecksumAlgorithm.fromJson(item as Map<String, Object?>))
+                .toList(),
+        supportsBreakpointLocationsRequest =
+            obj['supportsBreakpointLocationsRequest'] as bool?,
+        supportsCancelRequest = obj['supportsCancelRequest'] as bool?,
+        supportsClipboardContext = obj['supportsClipboardContext'] as bool?,
+        supportsCompletionsRequest = obj['supportsCompletionsRequest'] as bool?,
+        supportsConditionalBreakpoints =
+            obj['supportsConditionalBreakpoints'] as bool?,
+        supportsConfigurationDoneRequest =
+            obj['supportsConfigurationDoneRequest'] as bool?,
+        supportsDataBreakpoints = obj['supportsDataBreakpoints'] as bool?,
+        supportsDelayedStackTraceLoading =
+            obj['supportsDelayedStackTraceLoading'] as bool?,
+        supportsDisassembleRequest = obj['supportsDisassembleRequest'] as bool?,
+        supportsEvaluateForHovers = obj['supportsEvaluateForHovers'] as bool?,
+        supportsExceptionFilterOptions =
+            obj['supportsExceptionFilterOptions'] as bool?,
+        supportsExceptionInfoRequest =
+            obj['supportsExceptionInfoRequest'] as bool?,
+        supportsExceptionOptions = obj['supportsExceptionOptions'] as bool?,
+        supportsFunctionBreakpoints =
+            obj['supportsFunctionBreakpoints'] as bool?,
+        supportsGotoTargetsRequest = obj['supportsGotoTargetsRequest'] as bool?,
+        supportsHitConditionalBreakpoints =
+            obj['supportsHitConditionalBreakpoints'] as bool?,
+        supportsInstructionBreakpoints =
+            obj['supportsInstructionBreakpoints'] as bool?,
+        supportsLoadedSourcesRequest =
+            obj['supportsLoadedSourcesRequest'] as bool?,
+        supportsLogPoints = obj['supportsLogPoints'] as bool?,
+        supportsModulesRequest = obj['supportsModulesRequest'] as bool?,
+        supportsReadMemoryRequest = obj['supportsReadMemoryRequest'] as bool?,
+        supportsRestartFrame = obj['supportsRestartFrame'] as bool?,
+        supportsRestartRequest = obj['supportsRestartRequest'] as bool?,
+        supportsSetExpression = obj['supportsSetExpression'] as bool?,
+        supportsSetVariable = obj['supportsSetVariable'] as bool?,
+        supportsStepBack = obj['supportsStepBack'] as bool?,
+        supportsStepInTargetsRequest =
+            obj['supportsStepInTargetsRequest'] as bool?,
+        supportsSteppingGranularity =
+            obj['supportsSteppingGranularity'] as bool?,
+        supportsTerminateRequest = obj['supportsTerminateRequest'] as bool?,
+        supportsTerminateThreadsRequest =
+            obj['supportsTerminateThreadsRequest'] as bool?,
+        supportsValueFormattingOptions =
+            obj['supportsValueFormattingOptions'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['additionalModuleColumns'] is! List ||
+        (obj['additionalModuleColumns']
+            .any((item) => !ColumnDescriptor.canParse(item))))) {
+      return false;
+    }
+    if ((obj['completionTriggerCharacters'] is! List ||
+        (obj['completionTriggerCharacters'].any((item) => item is! String)))) {
+      return false;
+    }
+    if ((obj['exceptionBreakpointFilters'] is! List ||
+        (obj['exceptionBreakpointFilters']
+            .any((item) => !ExceptionBreakpointsFilter.canParse(item))))) {
+      return false;
+    }
+    if (obj['supportSuspendDebuggee'] is! bool?) {
+      return false;
+    }
+    if (obj['supportTerminateDebuggee'] is! bool?) {
+      return false;
+    }
+    if ((obj['supportedChecksumAlgorithms'] is! List ||
+        (obj['supportedChecksumAlgorithms']
+            .any((item) => !ChecksumAlgorithm.canParse(item))))) {
+      return false;
+    }
+    if (obj['supportsBreakpointLocationsRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsCancelRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsClipboardContext'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsCompletionsRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsConditionalBreakpoints'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsConfigurationDoneRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsDataBreakpoints'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsDelayedStackTraceLoading'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsDisassembleRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsEvaluateForHovers'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsExceptionFilterOptions'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsExceptionInfoRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsExceptionOptions'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsFunctionBreakpoints'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsGotoTargetsRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsHitConditionalBreakpoints'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsInstructionBreakpoints'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsLoadedSourcesRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsLogPoints'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsModulesRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsReadMemoryRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsRestartFrame'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsRestartRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsSetExpression'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsSetVariable'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsStepBack'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsStepInTargetsRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsSteppingGranularity'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsTerminateRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsTerminateThreadsRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsValueFormattingOptions'] is! bool?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (additionalModuleColumns != null)
+          'additionalModuleColumns': additionalModuleColumns,
+        if (completionTriggerCharacters != null)
+          'completionTriggerCharacters': completionTriggerCharacters,
+        if (exceptionBreakpointFilters != null)
+          'exceptionBreakpointFilters': exceptionBreakpointFilters,
+        if (supportSuspendDebuggee != null)
+          'supportSuspendDebuggee': supportSuspendDebuggee,
+        if (supportTerminateDebuggee != null)
+          'supportTerminateDebuggee': supportTerminateDebuggee,
+        if (supportedChecksumAlgorithms != null)
+          'supportedChecksumAlgorithms': supportedChecksumAlgorithms,
+        if (supportsBreakpointLocationsRequest != null)
+          'supportsBreakpointLocationsRequest':
+              supportsBreakpointLocationsRequest,
+        if (supportsCancelRequest != null)
+          'supportsCancelRequest': supportsCancelRequest,
+        if (supportsClipboardContext != null)
+          'supportsClipboardContext': supportsClipboardContext,
+        if (supportsCompletionsRequest != null)
+          'supportsCompletionsRequest': supportsCompletionsRequest,
+        if (supportsConditionalBreakpoints != null)
+          'supportsConditionalBreakpoints': supportsConditionalBreakpoints,
+        if (supportsConfigurationDoneRequest != null)
+          'supportsConfigurationDoneRequest': supportsConfigurationDoneRequest,
+        if (supportsDataBreakpoints != null)
+          'supportsDataBreakpoints': supportsDataBreakpoints,
+        if (supportsDelayedStackTraceLoading != null)
+          'supportsDelayedStackTraceLoading': supportsDelayedStackTraceLoading,
+        if (supportsDisassembleRequest != null)
+          'supportsDisassembleRequest': supportsDisassembleRequest,
+        if (supportsEvaluateForHovers != null)
+          'supportsEvaluateForHovers': supportsEvaluateForHovers,
+        if (supportsExceptionFilterOptions != null)
+          'supportsExceptionFilterOptions': supportsExceptionFilterOptions,
+        if (supportsExceptionInfoRequest != null)
+          'supportsExceptionInfoRequest': supportsExceptionInfoRequest,
+        if (supportsExceptionOptions != null)
+          'supportsExceptionOptions': supportsExceptionOptions,
+        if (supportsFunctionBreakpoints != null)
+          'supportsFunctionBreakpoints': supportsFunctionBreakpoints,
+        if (supportsGotoTargetsRequest != null)
+          'supportsGotoTargetsRequest': supportsGotoTargetsRequest,
+        if (supportsHitConditionalBreakpoints != null)
+          'supportsHitConditionalBreakpoints':
+              supportsHitConditionalBreakpoints,
+        if (supportsInstructionBreakpoints != null)
+          'supportsInstructionBreakpoints': supportsInstructionBreakpoints,
+        if (supportsLoadedSourcesRequest != null)
+          'supportsLoadedSourcesRequest': supportsLoadedSourcesRequest,
+        if (supportsLogPoints != null) 'supportsLogPoints': supportsLogPoints,
+        if (supportsModulesRequest != null)
+          'supportsModulesRequest': supportsModulesRequest,
+        if (supportsReadMemoryRequest != null)
+          'supportsReadMemoryRequest': supportsReadMemoryRequest,
+        if (supportsRestartFrame != null)
+          'supportsRestartFrame': supportsRestartFrame,
+        if (supportsRestartRequest != null)
+          'supportsRestartRequest': supportsRestartRequest,
+        if (supportsSetExpression != null)
+          'supportsSetExpression': supportsSetExpression,
+        if (supportsSetVariable != null)
+          'supportsSetVariable': supportsSetVariable,
+        if (supportsStepBack != null) 'supportsStepBack': supportsStepBack,
+        if (supportsStepInTargetsRequest != null)
+          'supportsStepInTargetsRequest': supportsStepInTargetsRequest,
+        if (supportsSteppingGranularity != null)
+          'supportsSteppingGranularity': supportsSteppingGranularity,
+        if (supportsTerminateRequest != null)
+          'supportsTerminateRequest': supportsTerminateRequest,
+        if (supportsTerminateThreadsRequest != null)
+          'supportsTerminateThreadsRequest': supportsTerminateThreadsRequest,
+        if (supportsValueFormattingOptions != null)
+          'supportsValueFormattingOptions': supportsValueFormattingOptions,
+      };
+}
+
+/// The checksum of an item calculated by the specified algorithm.
+class Checksum {
+  /// The algorithm used to calculate this checksum.
+  final ChecksumAlgorithm algorithm;
+
+  /// Value of the checksum.
+  final String checksum;
+
+  static Checksum fromJson(Map<String, Object?> obj) => Checksum.fromMap(obj);
+
+  Checksum({
+    required this.algorithm,
+    required this.checksum,
+  });
+
+  Checksum.fromMap(Map<String, Object?> obj)
+      : algorithm = ChecksumAlgorithm.fromJson(
+            obj['algorithm'] as Map<String, Object?>),
+        checksum = obj['checksum'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!ChecksumAlgorithm.canParse(obj['algorithm'])) {
+      return false;
+    }
+    if (obj['checksum'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'algorithm': algorithm,
+        'checksum': checksum,
+      };
+}
+
+/// Names of checksum algorithms that may be supported by a debug adapter.
+class ChecksumAlgorithm {
+  static ChecksumAlgorithm fromJson(Map<String, Object?> obj) =>
+      ChecksumAlgorithm.fromMap(obj);
+
+  ChecksumAlgorithm();
+
+  ChecksumAlgorithm.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// A ColumnDescriptor specifies what module attribute to show in a column of
+/// the ModulesView, how to format it,
+/// and what the column's label should be.
+/// It is only used if the underlying UI actually supports this level of
+/// customization.
+class ColumnDescriptor {
+  /// Name of the attribute rendered in this column.
+  final String attributeName;
+
+  /// Format to use for the rendered values in this column. TBD how the format
+  /// strings looks like.
+  final String? format;
+
+  /// Header UI label of column.
+  final String label;
+
+  /// Datatype of values in this column.  Defaults to 'string' if not specified.
+  final String? type;
+
+  /// Width of this column in characters (hint only).
+  final int? width;
+
+  static ColumnDescriptor fromJson(Map<String, Object?> obj) =>
+      ColumnDescriptor.fromMap(obj);
+
+  ColumnDescriptor({
+    required this.attributeName,
+    this.format,
+    required this.label,
+    this.type,
+    this.width,
+  });
+
+  ColumnDescriptor.fromMap(Map<String, Object?> obj)
+      : attributeName = obj['attributeName'] as String,
+        format = obj['format'] as String?,
+        label = obj['label'] as String,
+        type = obj['type'] as String?,
+        width = obj['width'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['attributeName'] is! String) {
+      return false;
+    }
+    if (obj['format'] is! String?) {
+      return false;
+    }
+    if (obj['label'] is! String) {
+      return false;
+    }
+    if (obj['type'] is! String?) {
+      return false;
+    }
+    if (obj['width'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'attributeName': attributeName,
+        if (format != null) 'format': format,
+        'label': label,
+        if (type != null) 'type': type,
+        if (width != null) 'width': width,
+      };
+}
+
+/// CompletionItems are the suggestions returned from the CompletionsRequest.
+class CompletionItem {
+  /// The label of this completion item. By default this is also the text that
+  /// is inserted when selecting this completion.
+  final String label;
+
+  /// This value determines how many characters are overwritten by the
+  /// completion text.
+  /// If missing the value 0 is assumed which results in the completion text
+  /// being inserted.
+  final int? length;
+
+  /// Determines the length of the new selection after the text has been
+  /// inserted (or replaced).
+  /// The selection can not extend beyond the bounds of the completion text.
+  /// If omitted the length is assumed to be 0.
+  final int? selectionLength;
+
+  /// Determines the start of the new selection after the text has been inserted
+  /// (or replaced).
+  /// The start position must in the range 0 and length of the completion text.
+  /// If omitted the selection starts at the end of the completion text.
+  final int? selectionStart;
+
+  /// A string that should be used when comparing this item with other items.
+  /// When `falsy` the label is used.
+  final String? sortText;
+
+  /// This value determines the location (in the CompletionsRequest's 'text'
+  /// attribute) where the completion text is added.
+  /// If missing the text is added at the location specified by the
+  /// CompletionsRequest's 'column' attribute.
+  final int? start;
+
+  /// If text is not falsy then it is inserted instead of the label.
+  final String? text;
+
+  /// The item's type. Typically the client uses this information to render the
+  /// item in the UI with an icon.
+  final CompletionItemType? type;
+
+  static CompletionItem fromJson(Map<String, Object?> obj) =>
+      CompletionItem.fromMap(obj);
+
+  CompletionItem({
+    required this.label,
+    this.length,
+    this.selectionLength,
+    this.selectionStart,
+    this.sortText,
+    this.start,
+    this.text,
+    this.type,
+  });
+
+  CompletionItem.fromMap(Map<String, Object?> obj)
+      : label = obj['label'] as String,
+        length = obj['length'] as int?,
+        selectionLength = obj['selectionLength'] as int?,
+        selectionStart = obj['selectionStart'] as int?,
+        sortText = obj['sortText'] as String?,
+        start = obj['start'] as int?,
+        text = obj['text'] as String?,
+        type = obj['type'] == null
+            ? null
+            : CompletionItemType.fromJson(obj['type'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['label'] is! String) {
+      return false;
+    }
+    if (obj['length'] is! int?) {
+      return false;
+    }
+    if (obj['selectionLength'] is! int?) {
+      return false;
+    }
+    if (obj['selectionStart'] is! int?) {
+      return false;
+    }
+    if (obj['sortText'] is! String?) {
+      return false;
+    }
+    if (obj['start'] is! int?) {
+      return false;
+    }
+    if (obj['text'] is! String?) {
+      return false;
+    }
+    if (!CompletionItemType?.canParse(obj['type'])) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'label': label,
+        if (length != null) 'length': length,
+        if (selectionLength != null) 'selectionLength': selectionLength,
+        if (selectionStart != null) 'selectionStart': selectionStart,
+        if (sortText != null) 'sortText': sortText,
+        if (start != null) 'start': start,
+        if (text != null) 'text': text,
+        if (type != null) 'type': type,
+      };
+}
+
+/// Some predefined types for the CompletionItem. Please note that not all
+/// clients have specific icons for all of them.
+class CompletionItemType {
+  static CompletionItemType fromJson(Map<String, Object?> obj) =>
+      CompletionItemType.fromMap(obj);
+
+  CompletionItemType();
+
+  CompletionItemType.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Arguments for 'completions' request.
+class CompletionsArguments extends RequestArguments {
+  /// The character position for which to determine the completion proposals.
+  final int column;
+
+  /// Returns completions in the scope of this stack frame. If not specified,
+  /// the completions are returned for the global scope.
+  final int? frameId;
+
+  /// An optional line for which to determine the completion proposals. If
+  /// missing the first line of the text is assumed.
+  final int? line;
+
+  /// One or more source lines. Typically this is the text a user has typed into
+  /// the debug console before he asked for completion.
+  final String text;
+
+  static CompletionsArguments fromJson(Map<String, Object?> obj) =>
+      CompletionsArguments.fromMap(obj);
+
+  CompletionsArguments({
+    required this.column,
+    this.frameId,
+    this.line,
+    required this.text,
+  });
+
+  CompletionsArguments.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int,
+        frameId = obj['frameId'] as int?,
+        line = obj['line'] as int?,
+        text = obj['text'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int) {
+      return false;
+    }
+    if (obj['frameId'] is! int?) {
+      return false;
+    }
+    if (obj['line'] is! int?) {
+      return false;
+    }
+    if (obj['text'] is! String) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'column': column,
+        if (frameId != null) 'frameId': frameId,
+        if (line != null) 'line': line,
+        'text': text,
+      };
+}
+
+/// Response to 'completions' request.
+class CompletionsResponse extends Response {
+  static CompletionsResponse fromJson(Map<String, Object?> obj) =>
+      CompletionsResponse.fromMap(obj);
+
+  CompletionsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  CompletionsResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'configurationDone' request.
+class ConfigurationDoneArguments extends RequestArguments {
+  static ConfigurationDoneArguments fromJson(Map<String, Object?> obj) =>
+      ConfigurationDoneArguments.fromMap(obj);
+
+  ConfigurationDoneArguments();
+
+  ConfigurationDoneArguments.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Response to 'configurationDone' request. This is just an acknowledgement, so
+/// no body field is required.
+class ConfigurationDoneResponse extends Response {
+  static ConfigurationDoneResponse fromJson(Map<String, Object?> obj) =>
+      ConfigurationDoneResponse.fromMap(obj);
+
+  ConfigurationDoneResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ConfigurationDoneResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'continue' request.
+class ContinueArguments extends RequestArguments {
+  /// Continue execution for the specified thread (if possible).
+  /// If the backend cannot continue on a single thread but will continue on all
+  /// threads, it should set the 'allThreadsContinued' attribute in the response
+  /// to true.
+  final int threadId;
+
+  static ContinueArguments fromJson(Map<String, Object?> obj) =>
+      ContinueArguments.fromMap(obj);
+
+  ContinueArguments({
+    required this.threadId,
+  });
+
+  ContinueArguments.fromMap(Map<String, Object?> obj)
+      : threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'continue' request.
+class ContinueResponse extends Response {
+  static ContinueResponse fromJson(Map<String, Object?> obj) =>
+      ContinueResponse.fromMap(obj);
+
+  ContinueResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ContinueResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Properties of a data breakpoint passed to the setDataBreakpoints request.
+class DataBreakpoint {
+  /// The access type of the data.
+  final DataBreakpointAccessType? accessType;
+
+  /// An optional expression for conditional breakpoints.
+  final String? condition;
+
+  /// An id representing the data. This id is returned from the
+  /// dataBreakpointInfo request.
+  final String dataId;
+
+  /// An optional expression that controls how many hits of the breakpoint are
+  /// ignored.
+  /// The backend is expected to interpret the expression as needed.
+  final String? hitCondition;
+
+  static DataBreakpoint fromJson(Map<String, Object?> obj) =>
+      DataBreakpoint.fromMap(obj);
+
+  DataBreakpoint({
+    this.accessType,
+    this.condition,
+    required this.dataId,
+    this.hitCondition,
+  });
+
+  DataBreakpoint.fromMap(Map<String, Object?> obj)
+      : accessType = obj['accessType'] == null
+            ? null
+            : DataBreakpointAccessType.fromJson(
+                obj['accessType'] as Map<String, Object?>),
+        condition = obj['condition'] as String?,
+        dataId = obj['dataId'] as String,
+        hitCondition = obj['hitCondition'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!DataBreakpointAccessType?.canParse(obj['accessType'])) {
+      return false;
+    }
+    if (obj['condition'] is! String?) {
+      return false;
+    }
+    if (obj['dataId'] is! String) {
+      return false;
+    }
+    if (obj['hitCondition'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (accessType != null) 'accessType': accessType,
+        if (condition != null) 'condition': condition,
+        'dataId': dataId,
+        if (hitCondition != null) 'hitCondition': hitCondition,
+      };
+}
+
+/// This enumeration defines all possible access types for data breakpoints.
+class DataBreakpointAccessType {
+  static DataBreakpointAccessType fromJson(Map<String, Object?> obj) =>
+      DataBreakpointAccessType.fromMap(obj);
+
+  DataBreakpointAccessType();
+
+  DataBreakpointAccessType.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Arguments for 'dataBreakpointInfo' request.
+class DataBreakpointInfoArguments extends RequestArguments {
+  /// The name of the Variable's child to obtain data breakpoint information
+  /// for.
+  /// If variablesReference isn’t provided, this can be an expression.
+  final String name;
+
+  /// Reference to the Variable container if the data breakpoint is requested
+  /// for a child of the container.
+  final int? variablesReference;
+
+  static DataBreakpointInfoArguments fromJson(Map<String, Object?> obj) =>
+      DataBreakpointInfoArguments.fromMap(obj);
+
+  DataBreakpointInfoArguments({
+    required this.name,
+    this.variablesReference,
+  });
+
+  DataBreakpointInfoArguments.fromMap(Map<String, Object?> obj)
+      : name = obj['name'] as String,
+        variablesReference = obj['variablesReference'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'name': name,
+        if (variablesReference != null)
+          'variablesReference': variablesReference,
+      };
+}
+
+/// Response to 'dataBreakpointInfo' request.
+class DataBreakpointInfoResponse extends Response {
+  static DataBreakpointInfoResponse fromJson(Map<String, Object?> obj) =>
+      DataBreakpointInfoResponse.fromMap(obj);
+
+  DataBreakpointInfoResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  DataBreakpointInfoResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'disassemble' request.
+class DisassembleArguments extends RequestArguments {
+  /// Number of instructions to disassemble starting at the specified location
+  /// and offset.
+  /// An adapter must return exactly this number of instructions - any
+  /// unavailable instructions should be replaced with an implementation-defined
+  /// 'invalid instruction' value.
+  final int instructionCount;
+
+  /// Optional offset (in instructions) to be applied after the byte offset (if
+  /// any) before disassembling. Can be negative.
+  final int? instructionOffset;
+
+  /// Memory reference to the base location containing the instructions to
+  /// disassemble.
+  final String memoryReference;
+
+  /// Optional offset (in bytes) to be applied to the reference location before
+  /// disassembling. Can be negative.
+  final int? offset;
+
+  /// If true, the adapter should attempt to resolve memory addresses and other
+  /// values to symbolic names.
+  final bool? resolveSymbols;
+
+  static DisassembleArguments fromJson(Map<String, Object?> obj) =>
+      DisassembleArguments.fromMap(obj);
+
+  DisassembleArguments({
+    required this.instructionCount,
+    this.instructionOffset,
+    required this.memoryReference,
+    this.offset,
+    this.resolveSymbols,
+  });
+
+  DisassembleArguments.fromMap(Map<String, Object?> obj)
+      : instructionCount = obj['instructionCount'] as int,
+        instructionOffset = obj['instructionOffset'] as int?,
+        memoryReference = obj['memoryReference'] as String,
+        offset = obj['offset'] as int?,
+        resolveSymbols = obj['resolveSymbols'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['instructionCount'] is! int) {
+      return false;
+    }
+    if (obj['instructionOffset'] is! int?) {
+      return false;
+    }
+    if (obj['memoryReference'] is! String) {
+      return false;
+    }
+    if (obj['offset'] is! int?) {
+      return false;
+    }
+    if (obj['resolveSymbols'] is! bool?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'instructionCount': instructionCount,
+        if (instructionOffset != null) 'instructionOffset': instructionOffset,
+        'memoryReference': memoryReference,
+        if (offset != null) 'offset': offset,
+        if (resolveSymbols != null) 'resolveSymbols': resolveSymbols,
+      };
+}
+
+/// Response to 'disassemble' request.
+class DisassembleResponse extends Response {
+  static DisassembleResponse fromJson(Map<String, Object?> obj) =>
+      DisassembleResponse.fromMap(obj);
+
+  DisassembleResponse({
+    Map<String, Object?>? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  DisassembleResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>?) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Represents a single disassembled instruction.
+class DisassembledInstruction {
+  /// The address of the instruction. Treated as a hex value if prefixed with
+  /// '0x', or as a decimal value otherwise.
+  final String address;
+
+  /// The column within the line that corresponds to this instruction, if any.
+  final int? column;
+
+  /// The end column of the range that corresponds to this instruction, if any.
+  final int? endColumn;
+
+  /// The end line of the range that corresponds to this instruction, if any.
+  final int? endLine;
+
+  /// Text representing the instruction and its operands, in an
+  /// implementation-defined format.
+  final String instruction;
+
+  /// Optional raw bytes representing the instruction and its operands, in an
+  /// implementation-defined format.
+  final String? instructionBytes;
+
+  /// The line within the source location that corresponds to this instruction,
+  /// if any.
+  final int? line;
+
+  /// Source location that corresponds to this instruction, if any.
+  /// Should always be set (if available) on the first instruction returned,
+  /// but can be omitted afterwards if this instruction maps to the same source
+  /// file as the previous instruction.
+  final Source? location;
+
+  /// Name of the symbol that corresponds with the location of this instruction,
+  /// if any.
+  final String? symbol;
+
+  static DisassembledInstruction fromJson(Map<String, Object?> obj) =>
+      DisassembledInstruction.fromMap(obj);
+
+  DisassembledInstruction({
+    required this.address,
+    this.column,
+    this.endColumn,
+    this.endLine,
+    required this.instruction,
+    this.instructionBytes,
+    this.line,
+    this.location,
+    this.symbol,
+  });
+
+  DisassembledInstruction.fromMap(Map<String, Object?> obj)
+      : address = obj['address'] as String,
+        column = obj['column'] as int?,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        instruction = obj['instruction'] as String,
+        instructionBytes = obj['instructionBytes'] as String?,
+        line = obj['line'] as int?,
+        location = obj['location'] == null
+            ? null
+            : Source.fromJson(obj['location'] as Map<String, Object?>),
+        symbol = obj['symbol'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['address'] is! String) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['instruction'] is! String) {
+      return false;
+    }
+    if (obj['instructionBytes'] is! String?) {
+      return false;
+    }
+    if (obj['line'] is! int?) {
+      return false;
+    }
+    if (!Source?.canParse(obj['location'])) {
+      return false;
+    }
+    if (obj['symbol'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'address': address,
+        if (column != null) 'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        'instruction': instruction,
+        if (instructionBytes != null) 'instructionBytes': instructionBytes,
+        if (line != null) 'line': line,
+        if (location != null) 'location': location,
+        if (symbol != null) 'symbol': symbol,
+      };
+}
+
+/// Arguments for 'disconnect' request.
+class DisconnectArguments extends RequestArguments {
+  /// A value of true indicates that this 'disconnect' request is part of a
+  /// restart sequence.
+  final bool? restart;
+
+  /// Indicates whether the debuggee should stay suspended when the debugger is
+  /// disconnected.
+  /// If unspecified, the debuggee should resume execution.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportSuspendDebuggee' is true.
+  final bool? suspendDebuggee;
+
+  /// Indicates whether the debuggee should be terminated when the debugger is
+  /// disconnected.
+  /// If unspecified, the debug adapter is free to do whatever it thinks is
+  /// best.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportTerminateDebuggee' is true.
+  final bool? terminateDebuggee;
+
+  static DisconnectArguments fromJson(Map<String, Object?> obj) =>
+      DisconnectArguments.fromMap(obj);
+
+  DisconnectArguments({
+    this.restart,
+    this.suspendDebuggee,
+    this.terminateDebuggee,
+  });
+
+  DisconnectArguments.fromMap(Map<String, Object?> obj)
+      : restart = obj['restart'] as bool?,
+        suspendDebuggee = obj['suspendDebuggee'] as bool?,
+        terminateDebuggee = obj['terminateDebuggee'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['restart'] is! bool?) {
+      return false;
+    }
+    if (obj['suspendDebuggee'] is! bool?) {
+      return false;
+    }
+    if (obj['terminateDebuggee'] is! bool?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (restart != null) 'restart': restart,
+        if (suspendDebuggee != null) 'suspendDebuggee': suspendDebuggee,
+        if (terminateDebuggee != null) 'terminateDebuggee': terminateDebuggee,
+      };
+}
+
+/// Response to 'disconnect' request. This is just an acknowledgement, so no
+/// body field is required.
+class DisconnectResponse extends Response {
+  static DisconnectResponse fromJson(Map<String, Object?> obj) =>
+      DisconnectResponse.fromMap(obj);
+
+  DisconnectResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  DisconnectResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// On error (whenever 'success' is false), the body can provide more details.
+class ErrorResponse extends Response {
+  static ErrorResponse fromJson(Map<String, Object?> obj) =>
+      ErrorResponse.fromMap(obj);
+
+  ErrorResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ErrorResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'evaluate' request.
+class EvaluateArguments extends RequestArguments {
+  /// The context in which the evaluate request is run.
+  final String? context;
+
+  /// The expression to evaluate.
+  final String expression;
+
+  /// Specifies details on how to format the Evaluate result.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsValueFormattingOptions' is true.
+  final ValueFormat? format;
+
+  /// Evaluate the expression in the scope of this stack frame. If not
+  /// specified, the expression is evaluated in the global scope.
+  final int? frameId;
+
+  static EvaluateArguments fromJson(Map<String, Object?> obj) =>
+      EvaluateArguments.fromMap(obj);
+
+  EvaluateArguments({
+    this.context,
+    required this.expression,
+    this.format,
+    this.frameId,
+  });
+
+  EvaluateArguments.fromMap(Map<String, Object?> obj)
+      : context = obj['context'] as String?,
+        expression = obj['expression'] as String,
+        format = obj['format'] == null
+            ? null
+            : ValueFormat.fromJson(obj['format'] as Map<String, Object?>),
+        frameId = obj['frameId'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['context'] is! String?) {
+      return false;
+    }
+    if (obj['expression'] is! String) {
+      return false;
+    }
+    if (!ValueFormat?.canParse(obj['format'])) {
+      return false;
+    }
+    if (obj['frameId'] is! int?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (context != null) 'context': context,
+        'expression': expression,
+        if (format != null) 'format': format,
+        if (frameId != null) 'frameId': frameId,
+      };
+}
+
+/// Response to 'evaluate' request.
+class EvaluateResponse extends Response {
+  static EvaluateResponse fromJson(Map<String, Object?> obj) =>
+      EvaluateResponse.fromMap(obj);
+
+  EvaluateResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  EvaluateResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A debug adapter initiated event.
+class Event extends ProtocolMessage {
+  /// Event-specific information.
+  final Object? body;
+
+  /// Type of event.
+  final String event;
+
+  static Event fromJson(Map<String, Object?> obj) => Event.fromMap(obj);
+
+  Event({
+    this.body,
+    required this.event,
+    required int seq,
+  }) : super(
+          seq: seq,
+          type: 'event',
+        );
+
+  Event.fromMap(Map<String, Object?> obj)
+      : body = obj['body'],
+        event = obj['event'] as String,
+        super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['event'] is! String) {
+      return false;
+    }
+    if (obj['type'] is! String) {
+      return false;
+    }
+    return ProtocolMessage.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+        if (body != null) 'body': body,
+        'event': event,
+      };
+}
+
+/// This enumeration defines all possible conditions when a thrown exception
+/// should result in a break.
+/// never: never breaks,
+/// always: always breaks,
+/// unhandled: breaks when exception unhandled,
+/// userUnhandled: breaks if the exception is not handled by user code.
+class ExceptionBreakMode {
+  static ExceptionBreakMode fromJson(Map<String, Object?> obj) =>
+      ExceptionBreakMode.fromMap(obj);
+
+  ExceptionBreakMode();
+
+  ExceptionBreakMode.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// An ExceptionBreakpointsFilter is shown in the UI as an filter option for
+/// configuring how exceptions are dealt with.
+class ExceptionBreakpointsFilter {
+  /// An optional help text providing information about the condition. This
+  /// string is shown as the placeholder text for a text box and must be
+  /// translated.
+  final String? conditionDescription;
+
+  /// Initial value of the filter option. If not specified a value 'false' is
+  /// assumed.
+  final bool? defaultValue;
+
+  /// An optional help text providing additional information about the exception
+  /// filter. This string is typically shown as a hover and must be translated.
+  final String? description;
+
+  /// The internal ID of the filter option. This value is passed to the
+  /// 'setExceptionBreakpoints' request.
+  final String filter;
+
+  /// The name of the filter option. This will be shown in the UI.
+  final String label;
+
+  /// Controls whether a condition can be specified for this filter option. If
+  /// false or missing, a condition can not be set.
+  final bool? supportsCondition;
+
+  static ExceptionBreakpointsFilter fromJson(Map<String, Object?> obj) =>
+      ExceptionBreakpointsFilter.fromMap(obj);
+
+  ExceptionBreakpointsFilter({
+    this.conditionDescription,
+    this.defaultValue,
+    this.description,
+    required this.filter,
+    required this.label,
+    this.supportsCondition,
+  });
+
+  ExceptionBreakpointsFilter.fromMap(Map<String, Object?> obj)
+      : conditionDescription = obj['conditionDescription'] as String?,
+        defaultValue = obj['default'] as bool?,
+        description = obj['description'] as String?,
+        filter = obj['filter'] as String,
+        label = obj['label'] as String,
+        supportsCondition = obj['supportsCondition'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['conditionDescription'] is! String?) {
+      return false;
+    }
+    if (obj['default'] is! bool?) {
+      return false;
+    }
+    if (obj['description'] is! String?) {
+      return false;
+    }
+    if (obj['filter'] is! String) {
+      return false;
+    }
+    if (obj['label'] is! String) {
+      return false;
+    }
+    if (obj['supportsCondition'] is! bool?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (conditionDescription != null)
+          'conditionDescription': conditionDescription,
+        if (defaultValue != null) 'default': defaultValue,
+        if (description != null) 'description': description,
+        'filter': filter,
+        'label': label,
+        if (supportsCondition != null) 'supportsCondition': supportsCondition,
+      };
+}
+
+/// Detailed information about an exception that has occurred.
+class ExceptionDetails {
+  /// Optional expression that can be evaluated in the current scope to obtain
+  /// the exception object.
+  final String? evaluateName;
+
+  /// Fully-qualified type name of the exception object.
+  final String? fullTypeName;
+
+  /// Details of the exception contained by this exception, if any.
+  final List<ExceptionDetails>? innerException;
+
+  /// Message contained in the exception.
+  final String? message;
+
+  /// Stack trace at the time the exception was thrown.
+  final String? stackTrace;
+
+  /// Short type name of the exception object.
+  final String? typeName;
+
+  static ExceptionDetails fromJson(Map<String, Object?> obj) =>
+      ExceptionDetails.fromMap(obj);
+
+  ExceptionDetails({
+    this.evaluateName,
+    this.fullTypeName,
+    this.innerException,
+    this.message,
+    this.stackTrace,
+    this.typeName,
+  });
+
+  ExceptionDetails.fromMap(Map<String, Object?> obj)
+      : evaluateName = obj['evaluateName'] as String?,
+        fullTypeName = obj['fullTypeName'] as String?,
+        innerException = (obj['innerException'] as List?)
+            ?.map((item) =>
+                ExceptionDetails.fromJson(item as Map<String, Object?>))
+            .toList(),
+        message = obj['message'] as String?,
+        stackTrace = obj['stackTrace'] as String?,
+        typeName = obj['typeName'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['evaluateName'] is! String?) {
+      return false;
+    }
+    if (obj['fullTypeName'] is! String?) {
+      return false;
+    }
+    if ((obj['innerException'] is! List ||
+        (obj['innerException']
+            .any((item) => !ExceptionDetails.canParse(item))))) {
+      return false;
+    }
+    if (obj['message'] is! String?) {
+      return false;
+    }
+    if (obj['stackTrace'] is! String?) {
+      return false;
+    }
+    if (obj['typeName'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (evaluateName != null) 'evaluateName': evaluateName,
+        if (fullTypeName != null) 'fullTypeName': fullTypeName,
+        if (innerException != null) 'innerException': innerException,
+        if (message != null) 'message': message,
+        if (stackTrace != null) 'stackTrace': stackTrace,
+        if (typeName != null) 'typeName': typeName,
+      };
+}
+
+/// An ExceptionFilterOptions is used to specify an exception filter together
+/// with a condition for the setExceptionsFilter request.
+class ExceptionFilterOptions {
+  /// An optional expression for conditional exceptions.
+  /// The exception will break into the debugger if the result of the condition
+  /// is true.
+  final String? condition;
+
+  /// ID of an exception filter returned by the 'exceptionBreakpointFilters'
+  /// capability.
+  final String filterId;
+
+  static ExceptionFilterOptions fromJson(Map<String, Object?> obj) =>
+      ExceptionFilterOptions.fromMap(obj);
+
+  ExceptionFilterOptions({
+    this.condition,
+    required this.filterId,
+  });
+
+  ExceptionFilterOptions.fromMap(Map<String, Object?> obj)
+      : condition = obj['condition'] as String?,
+        filterId = obj['filterId'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['condition'] is! String?) {
+      return false;
+    }
+    if (obj['filterId'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (condition != null) 'condition': condition,
+        'filterId': filterId,
+      };
+}
+
+/// Arguments for 'exceptionInfo' request.
+class ExceptionInfoArguments extends RequestArguments {
+  /// Thread for which exception information should be retrieved.
+  final int threadId;
+
+  static ExceptionInfoArguments fromJson(Map<String, Object?> obj) =>
+      ExceptionInfoArguments.fromMap(obj);
+
+  ExceptionInfoArguments({
+    required this.threadId,
+  });
+
+  ExceptionInfoArguments.fromMap(Map<String, Object?> obj)
+      : threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'exceptionInfo' request.
+class ExceptionInfoResponse extends Response {
+  static ExceptionInfoResponse fromJson(Map<String, Object?> obj) =>
+      ExceptionInfoResponse.fromMap(obj);
+
+  ExceptionInfoResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ExceptionInfoResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// An ExceptionOptions assigns configuration options to a set of exceptions.
+class ExceptionOptions {
+  /// Condition when a thrown exception should result in a break.
+  final ExceptionBreakMode breakMode;
+
+  /// A path that selects a single or multiple exceptions in a tree. If 'path'
+  /// is missing, the whole tree is selected.
+  /// By convention the first segment of the path is a category that is used to
+  /// group exceptions in the UI.
+  final List<ExceptionPathSegment>? path;
+
+  static ExceptionOptions fromJson(Map<String, Object?> obj) =>
+      ExceptionOptions.fromMap(obj);
+
+  ExceptionOptions({
+    required this.breakMode,
+    this.path,
+  });
+
+  ExceptionOptions.fromMap(Map<String, Object?> obj)
+      : breakMode = ExceptionBreakMode.fromJson(
+            obj['breakMode'] as Map<String, Object?>),
+        path = (obj['path'] as List?)
+            ?.map((item) =>
+                ExceptionPathSegment.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!ExceptionBreakMode.canParse(obj['breakMode'])) {
+      return false;
+    }
+    if ((obj['path'] is! List ||
+        (obj['path'].any((item) => !ExceptionPathSegment.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakMode': breakMode,
+        if (path != null) 'path': path,
+      };
+}
+
+/// An ExceptionPathSegment represents a segment in a path that is used to match
+/// leafs or nodes in a tree of exceptions.
+/// If a segment consists of more than one name, it matches the names provided
+/// if 'negate' is false or missing or
+/// it matches anything except the names provided if 'negate' is true.
+class ExceptionPathSegment {
+  /// Depending on the value of 'negate' the names that should match or not
+  /// match.
+  final List<String> names;
+
+  /// If false or missing this segment matches the names provided, otherwise it
+  /// matches anything except the names provided.
+  final bool? negate;
+
+  static ExceptionPathSegment fromJson(Map<String, Object?> obj) =>
+      ExceptionPathSegment.fromMap(obj);
+
+  ExceptionPathSegment({
+    required this.names,
+    this.negate,
+  });
+
+  ExceptionPathSegment.fromMap(Map<String, Object?> obj)
+      : names = (obj['names'] as List).map((item) => item as String).toList(),
+        negate = obj['negate'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['names'] is! List ||
+        (obj['names'].any((item) => item is! String)))) {
+      return false;
+    }
+    if (obj['negate'] is! bool?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'names': names,
+        if (negate != null) 'negate': negate,
+      };
+}
+
+/// Properties of a breakpoint passed to the setFunctionBreakpoints request.
+class FunctionBreakpoint {
+  /// An optional expression for conditional breakpoints.
+  /// It is only honored by a debug adapter if the capability
+  /// 'supportsConditionalBreakpoints' is true.
+  final String? condition;
+
+  /// An optional expression that controls how many hits of the breakpoint are
+  /// ignored.
+  /// The backend is expected to interpret the expression as needed.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsHitConditionalBreakpoints' is true.
+  final String? hitCondition;
+
+  /// The name of the function.
+  final String name;
+
+  static FunctionBreakpoint fromJson(Map<String, Object?> obj) =>
+      FunctionBreakpoint.fromMap(obj);
+
+  FunctionBreakpoint({
+    this.condition,
+    this.hitCondition,
+    required this.name,
+  });
+
+  FunctionBreakpoint.fromMap(Map<String, Object?> obj)
+      : condition = obj['condition'] as String?,
+        hitCondition = obj['hitCondition'] as String?,
+        name = obj['name'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['condition'] is! String?) {
+      return false;
+    }
+    if (obj['hitCondition'] is! String?) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (condition != null) 'condition': condition,
+        if (hitCondition != null) 'hitCondition': hitCondition,
+        'name': name,
+      };
+}
+
+/// Arguments for 'goto' request.
+class GotoArguments extends RequestArguments {
+  /// The location where the debuggee will continue to run.
+  final int targetId;
+
+  /// Set the goto target for this thread.
+  final int threadId;
+
+  static GotoArguments fromJson(Map<String, Object?> obj) =>
+      GotoArguments.fromMap(obj);
+
+  GotoArguments({
+    required this.targetId,
+    required this.threadId,
+  });
+
+  GotoArguments.fromMap(Map<String, Object?> obj)
+      : targetId = obj['targetId'] as int,
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['targetId'] is! int) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'targetId': targetId,
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'goto' request. This is just an acknowledgement, so no body
+/// field is required.
+class GotoResponse extends Response {
+  static GotoResponse fromJson(Map<String, Object?> obj) =>
+      GotoResponse.fromMap(obj);
+
+  GotoResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  GotoResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A GotoTarget describes a code location that can be used as a target in the
+/// 'goto' request.
+/// The possible goto targets can be determined via the 'gotoTargets' request.
+class GotoTarget {
+  /// An optional column of the goto target.
+  final int? column;
+
+  /// An optional end column of the range covered by the goto target.
+  final int? endColumn;
+
+  /// An optional end line of the range covered by the goto target.
+  final int? endLine;
+
+  /// Unique identifier for a goto target. This is used in the goto request.
+  final int id;
+
+  /// Optional memory reference for the instruction pointer value represented by
+  /// this target.
+  final String? instructionPointerReference;
+
+  /// The name of the goto target (shown in the UI).
+  final String label;
+
+  /// The line of the goto target.
+  final int line;
+
+  static GotoTarget fromJson(Map<String, Object?> obj) =>
+      GotoTarget.fromMap(obj);
+
+  GotoTarget({
+    this.column,
+    this.endColumn,
+    this.endLine,
+    required this.id,
+    this.instructionPointerReference,
+    required this.label,
+    required this.line,
+  });
+
+  GotoTarget.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        id = obj['id'] as int,
+        instructionPointerReference =
+            obj['instructionPointerReference'] as String?,
+        label = obj['label'] as String,
+        line = obj['line'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['id'] is! int) {
+      return false;
+    }
+    if (obj['instructionPointerReference'] is! String?) {
+      return false;
+    }
+    if (obj['label'] is! String) {
+      return false;
+    }
+    if (obj['line'] is! int) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        'id': id,
+        if (instructionPointerReference != null)
+          'instructionPointerReference': instructionPointerReference,
+        'label': label,
+        'line': line,
+      };
+}
+
+/// Arguments for 'gotoTargets' request.
+class GotoTargetsArguments extends RequestArguments {
+  /// An optional column location for which the goto targets are determined.
+  final int? column;
+
+  /// The line location for which the goto targets are determined.
+  final int line;
+
+  /// The source location for which the goto targets are determined.
+  final Source source;
+
+  static GotoTargetsArguments fromJson(Map<String, Object?> obj) =>
+      GotoTargetsArguments.fromMap(obj);
+
+  GotoTargetsArguments({
+    this.column,
+    required this.line,
+    required this.source,
+  });
+
+  GotoTargetsArguments.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        line = obj['line'] as int,
+        source = Source.fromJson(obj['source'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['line'] is! int) {
+      return false;
+    }
+    if (!Source.canParse(obj['source'])) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        'line': line,
+        'source': source,
+      };
+}
+
+/// Response to 'gotoTargets' request.
+class GotoTargetsResponse extends Response {
+  static GotoTargetsResponse fromJson(Map<String, Object?> obj) =>
+      GotoTargetsResponse.fromMap(obj);
+
+  GotoTargetsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  GotoTargetsResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'initialize' request.
+class InitializeRequestArguments extends RequestArguments {
+  /// The ID of the debug adapter.
+  final String adapterID;
+
+  /// The ID of the (frontend) client using this adapter.
+  final String? clientID;
+
+  /// The human readable name of the (frontend) client using this adapter.
+  final String? clientName;
+
+  /// If true all column numbers are 1-based (default).
+  final bool? columnsStartAt1;
+
+  /// If true all line numbers are 1-based (default).
+  final bool? linesStartAt1;
+
+  /// The ISO-639 locale of the (frontend) client using this adapter, e.g. en-US
+  /// or de-CH.
+  final String? locale;
+
+  /// Determines in what format paths are specified. The default is 'path',
+  /// which is the native format.
+  final String? pathFormat;
+
+  /// Client supports the invalidated event.
+  final bool? supportsInvalidatedEvent;
+
+  /// Client supports memory references.
+  final bool? supportsMemoryReferences;
+
+  /// Client supports progress reporting.
+  final bool? supportsProgressReporting;
+
+  /// Client supports the runInTerminal request.
+  final bool? supportsRunInTerminalRequest;
+
+  /// Client supports the paging of variables.
+  final bool? supportsVariablePaging;
+
+  /// Client supports the optional type attribute for variables.
+  final bool? supportsVariableType;
+
+  static InitializeRequestArguments fromJson(Map<String, Object?> obj) =>
+      InitializeRequestArguments.fromMap(obj);
+
+  InitializeRequestArguments({
+    required this.adapterID,
+    this.clientID,
+    this.clientName,
+    this.columnsStartAt1,
+    this.linesStartAt1,
+    this.locale,
+    this.pathFormat,
+    this.supportsInvalidatedEvent,
+    this.supportsMemoryReferences,
+    this.supportsProgressReporting,
+    this.supportsRunInTerminalRequest,
+    this.supportsVariablePaging,
+    this.supportsVariableType,
+  });
+
+  InitializeRequestArguments.fromMap(Map<String, Object?> obj)
+      : adapterID = obj['adapterID'] as String,
+        clientID = obj['clientID'] as String?,
+        clientName = obj['clientName'] as String?,
+        columnsStartAt1 = obj['columnsStartAt1'] as bool?,
+        linesStartAt1 = obj['linesStartAt1'] as bool?,
+        locale = obj['locale'] as String?,
+        pathFormat = obj['pathFormat'] as String?,
+        supportsInvalidatedEvent = obj['supportsInvalidatedEvent'] as bool?,
+        supportsMemoryReferences = obj['supportsMemoryReferences'] as bool?,
+        supportsProgressReporting = obj['supportsProgressReporting'] as bool?,
+        supportsRunInTerminalRequest =
+            obj['supportsRunInTerminalRequest'] as bool?,
+        supportsVariablePaging = obj['supportsVariablePaging'] as bool?,
+        supportsVariableType = obj['supportsVariableType'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['adapterID'] is! String) {
+      return false;
+    }
+    if (obj['clientID'] is! String?) {
+      return false;
+    }
+    if (obj['clientName'] is! String?) {
+      return false;
+    }
+    if (obj['columnsStartAt1'] is! bool?) {
+      return false;
+    }
+    if (obj['linesStartAt1'] is! bool?) {
+      return false;
+    }
+    if (obj['locale'] is! String?) {
+      return false;
+    }
+    if (obj['pathFormat'] is! String?) {
+      return false;
+    }
+    if (obj['supportsInvalidatedEvent'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsMemoryReferences'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsProgressReporting'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsRunInTerminalRequest'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsVariablePaging'] is! bool?) {
+      return false;
+    }
+    if (obj['supportsVariableType'] is! bool?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'adapterID': adapterID,
+        if (clientID != null) 'clientID': clientID,
+        if (clientName != null) 'clientName': clientName,
+        if (columnsStartAt1 != null) 'columnsStartAt1': columnsStartAt1,
+        if (linesStartAt1 != null) 'linesStartAt1': linesStartAt1,
+        if (locale != null) 'locale': locale,
+        if (pathFormat != null) 'pathFormat': pathFormat,
+        if (supportsInvalidatedEvent != null)
+          'supportsInvalidatedEvent': supportsInvalidatedEvent,
+        if (supportsMemoryReferences != null)
+          'supportsMemoryReferences': supportsMemoryReferences,
+        if (supportsProgressReporting != null)
+          'supportsProgressReporting': supportsProgressReporting,
+        if (supportsRunInTerminalRequest != null)
+          'supportsRunInTerminalRequest': supportsRunInTerminalRequest,
+        if (supportsVariablePaging != null)
+          'supportsVariablePaging': supportsVariablePaging,
+        if (supportsVariableType != null)
+          'supportsVariableType': supportsVariableType,
+      };
+}
+
+/// Response to 'initialize' request.
+class InitializeResponse extends Response {
+  static InitializeResponse fromJson(Map<String, Object?> obj) =>
+      InitializeResponse.fromMap(obj);
+
+  InitializeResponse({
+    Capabilities? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  InitializeResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!Capabilities?.canParse(obj['body'])) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Properties of a breakpoint passed to the setInstructionBreakpoints request
+class InstructionBreakpoint {
+  /// An optional expression for conditional breakpoints.
+  /// It is only honored by a debug adapter if the capability
+  /// 'supportsConditionalBreakpoints' is true.
+  final String? condition;
+
+  /// An optional expression that controls how many hits of the breakpoint are
+  /// ignored.
+  /// The backend is expected to interpret the expression as needed.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsHitConditionalBreakpoints' is true.
+  final String? hitCondition;
+
+  /// The instruction reference of the breakpoint.
+  /// This should be a memory or instruction pointer reference from an
+  /// EvaluateResponse, Variable, StackFrame, GotoTarget, or Breakpoint.
+  final String instructionReference;
+
+  /// An optional offset from the instruction reference.
+  /// This can be negative.
+  final int? offset;
+
+  static InstructionBreakpoint fromJson(Map<String, Object?> obj) =>
+      InstructionBreakpoint.fromMap(obj);
+
+  InstructionBreakpoint({
+    this.condition,
+    this.hitCondition,
+    required this.instructionReference,
+    this.offset,
+  });
+
+  InstructionBreakpoint.fromMap(Map<String, Object?> obj)
+      : condition = obj['condition'] as String?,
+        hitCondition = obj['hitCondition'] as String?,
+        instructionReference = obj['instructionReference'] as String,
+        offset = obj['offset'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['condition'] is! String?) {
+      return false;
+    }
+    if (obj['hitCondition'] is! String?) {
+      return false;
+    }
+    if (obj['instructionReference'] is! String) {
+      return false;
+    }
+    if (obj['offset'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (condition != null) 'condition': condition,
+        if (hitCondition != null) 'hitCondition': hitCondition,
+        'instructionReference': instructionReference,
+        if (offset != null) 'offset': offset,
+      };
+}
+
+/// Logical areas that can be invalidated by the 'invalidated' event.
+class InvalidatedAreas {
+  static InvalidatedAreas fromJson(Map<String, Object?> obj) =>
+      InvalidatedAreas.fromMap(obj);
+
+  InvalidatedAreas();
+
+  InvalidatedAreas.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Arguments for 'launch' request. Additional attributes are implementation
+/// specific.
+class LaunchRequestArguments extends RequestArguments {
+  /// Optional data from the previous, restarted session.
+  /// The data is sent as the 'restart' attribute of the 'terminated' event.
+  /// The client should leave the data intact.
+  final Object? restart;
+
+  /// If noDebug is true the launch request should launch the program without
+  /// enabling debugging.
+  final bool? noDebug;
+
+  static LaunchRequestArguments fromJson(Map<String, Object?> obj) =>
+      LaunchRequestArguments.fromMap(obj);
+
+  LaunchRequestArguments({
+    this.restart,
+    this.noDebug,
+  });
+
+  LaunchRequestArguments.fromMap(Map<String, Object?> obj)
+      : restart = obj['__restart'],
+        noDebug = obj['noDebug'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['noDebug'] is! bool?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (restart != null) '__restart': restart,
+        if (noDebug != null) 'noDebug': noDebug,
+      };
+}
+
+/// Response to 'launch' request. This is just an acknowledgement, so no body
+/// field is required.
+class LaunchResponse extends Response {
+  static LaunchResponse fromJson(Map<String, Object?> obj) =>
+      LaunchResponse.fromMap(obj);
+
+  LaunchResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  LaunchResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'loadedSources' request.
+class LoadedSourcesArguments extends RequestArguments {
+  static LoadedSourcesArguments fromJson(Map<String, Object?> obj) =>
+      LoadedSourcesArguments.fromMap(obj);
+
+  LoadedSourcesArguments();
+
+  LoadedSourcesArguments.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Response to 'loadedSources' request.
+class LoadedSourcesResponse extends Response {
+  static LoadedSourcesResponse fromJson(Map<String, Object?> obj) =>
+      LoadedSourcesResponse.fromMap(obj);
+
+  LoadedSourcesResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  LoadedSourcesResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A structured message object. Used to return errors from requests.
+class Message {
+  /// A format string for the message. Embedded variables have the form
+  /// '{name}'.
+  /// If variable name starts with an underscore character, the variable does
+  /// not contain user data (PII) and can be safely used for telemetry purposes.
+  final String format;
+
+  /// Unique identifier for the message.
+  final int id;
+
+  /// If true send to telemetry.
+  final bool? sendTelemetry;
+
+  /// If true show user.
+  final bool? showUser;
+
+  /// An optional url where additional information about this message can be
+  /// found.
+  final String? url;
+
+  /// An optional label that is presented to the user as the UI for opening the
+  /// url.
+  final String? urlLabel;
+
+  /// An object used as a dictionary for looking up the variables in the format
+  /// string.
+  final Map<String, Object?>? variables;
+
+  static Message fromJson(Map<String, Object?> obj) => Message.fromMap(obj);
+
+  Message({
+    required this.format,
+    required this.id,
+    this.sendTelemetry,
+    this.showUser,
+    this.url,
+    this.urlLabel,
+    this.variables,
+  });
+
+  Message.fromMap(Map<String, Object?> obj)
+      : format = obj['format'] as String,
+        id = obj['id'] as int,
+        sendTelemetry = obj['sendTelemetry'] as bool?,
+        showUser = obj['showUser'] as bool?,
+        url = obj['url'] as String?,
+        urlLabel = obj['urlLabel'] as String?,
+        variables = obj['variables'] as Map<String, Object?>?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['format'] is! String) {
+      return false;
+    }
+    if (obj['id'] is! int) {
+      return false;
+    }
+    if (obj['sendTelemetry'] is! bool?) {
+      return false;
+    }
+    if (obj['showUser'] is! bool?) {
+      return false;
+    }
+    if (obj['url'] is! String?) {
+      return false;
+    }
+    if (obj['urlLabel'] is! String?) {
+      return false;
+    }
+    if (obj['variables'] is! Map<String, Object?>?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'format': format,
+        'id': id,
+        if (sendTelemetry != null) 'sendTelemetry': sendTelemetry,
+        if (showUser != null) 'showUser': showUser,
+        if (url != null) 'url': url,
+        if (urlLabel != null) 'urlLabel': urlLabel,
+        if (variables != null) 'variables': variables,
+      };
+}
+
+/// A Module object represents a row in the modules view.
+/// Two attributes are mandatory: an id identifies a module in the modules view
+/// and is used in a ModuleEvent for identifying a module for adding, updating
+/// or deleting.
+/// The name is used to minimally render the module in the UI.
+///
+/// Additional attributes can be added to the module. They will show up in the
+/// module View if they have a corresponding ColumnDescriptor.
+///
+/// To avoid an unnecessary proliferation of additional attributes with similar
+/// semantics but different names
+/// we recommend to re-use attributes from the 'recommended' list below first,
+/// and only introduce new attributes if nothing appropriate could be found.
+class Module {
+  /// Address range covered by this module.
+  final String? addressRange;
+
+  /// Module created or modified.
+  final String? dateTimeStamp;
+
+  /// Unique identifier for the module.
+  final Either2<int, String> id;
+
+  /// True if the module is optimized.
+  final bool? isOptimized;
+
+  /// True if the module is considered 'user code' by a debugger that supports
+  /// 'Just My Code'.
+  final bool? isUserCode;
+
+  /// A name of the module.
+  final String name;
+
+  /// optional but recommended attributes.
+  /// always try to use these first before introducing additional attributes.
+  ///
+  /// Logical full path to the module. The exact definition is implementation
+  /// defined, but usually this would be a full path to the on-disk file for the
+  /// module.
+  final String? path;
+
+  /// Logical full path to the symbol file. The exact definition is
+  /// implementation defined.
+  final String? symbolFilePath;
+
+  /// User understandable description of if symbols were found for the module
+  /// (ex: 'Symbols Loaded', 'Symbols not found', etc.
+  final String? symbolStatus;
+
+  /// Version of Module.
+  final String? version;
+
+  static Module fromJson(Map<String, Object?> obj) => Module.fromMap(obj);
+
+  Module({
+    this.addressRange,
+    this.dateTimeStamp,
+    required this.id,
+    this.isOptimized,
+    this.isUserCode,
+    required this.name,
+    this.path,
+    this.symbolFilePath,
+    this.symbolStatus,
+    this.version,
+  });
+
+  Module.fromMap(Map<String, Object?> obj)
+      : addressRange = obj['addressRange'] as String?,
+        dateTimeStamp = obj['dateTimeStamp'] as String?,
+        id = obj['id'] is int
+            ? Either2<int, String>.t1(obj['id'] as int)
+            : Either2<int, String>.t2(obj['id'] as String),
+        isOptimized = obj['isOptimized'] as bool?,
+        isUserCode = obj['isUserCode'] as bool?,
+        name = obj['name'] as String,
+        path = obj['path'] as String?,
+        symbolFilePath = obj['symbolFilePath'] as String?,
+        symbolStatus = obj['symbolStatus'] as String?,
+        version = obj['version'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['addressRange'] is! String?) {
+      return false;
+    }
+    if (obj['dateTimeStamp'] is! String?) {
+      return false;
+    }
+    if ((obj['id'] is! int && obj['id'] is! String)) {
+      return false;
+    }
+    if (obj['isOptimized'] is! bool?) {
+      return false;
+    }
+    if (obj['isUserCode'] is! bool?) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['path'] is! String?) {
+      return false;
+    }
+    if (obj['symbolFilePath'] is! String?) {
+      return false;
+    }
+    if (obj['symbolStatus'] is! String?) {
+      return false;
+    }
+    if (obj['version'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (addressRange != null) 'addressRange': addressRange,
+        if (dateTimeStamp != null) 'dateTimeStamp': dateTimeStamp,
+        'id': id,
+        if (isOptimized != null) 'isOptimized': isOptimized,
+        if (isUserCode != null) 'isUserCode': isUserCode,
+        'name': name,
+        if (path != null) 'path': path,
+        if (symbolFilePath != null) 'symbolFilePath': symbolFilePath,
+        if (symbolStatus != null) 'symbolStatus': symbolStatus,
+        if (version != null) 'version': version,
+      };
+}
+
+/// Arguments for 'modules' request.
+class ModulesArguments extends RequestArguments {
+  /// The number of modules to return. If moduleCount is not specified or 0, all
+  /// modules are returned.
+  final int? moduleCount;
+
+  /// The index of the first module to return; if omitted modules start at 0.
+  final int? startModule;
+
+  static ModulesArguments fromJson(Map<String, Object?> obj) =>
+      ModulesArguments.fromMap(obj);
+
+  ModulesArguments({
+    this.moduleCount,
+    this.startModule,
+  });
+
+  ModulesArguments.fromMap(Map<String, Object?> obj)
+      : moduleCount = obj['moduleCount'] as int?,
+        startModule = obj['startModule'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['moduleCount'] is! int?) {
+      return false;
+    }
+    if (obj['startModule'] is! int?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (moduleCount != null) 'moduleCount': moduleCount,
+        if (startModule != null) 'startModule': startModule,
+      };
+}
+
+/// Response to 'modules' request.
+class ModulesResponse extends Response {
+  static ModulesResponse fromJson(Map<String, Object?> obj) =>
+      ModulesResponse.fromMap(obj);
+
+  ModulesResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ModulesResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// The ModulesViewDescriptor is the container for all declarative configuration
+/// options of a ModuleView.
+/// For now it only specifies the columns to be shown in the modules view.
+class ModulesViewDescriptor {
+  final List<ColumnDescriptor> columns;
+
+  static ModulesViewDescriptor fromJson(Map<String, Object?> obj) =>
+      ModulesViewDescriptor.fromMap(obj);
+
+  ModulesViewDescriptor({
+    required this.columns,
+  });
+
+  ModulesViewDescriptor.fromMap(Map<String, Object?> obj)
+      : columns = (obj['columns'] as List)
+            .map((item) =>
+                ColumnDescriptor.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['columns'] is! List ||
+        (obj['columns'].any((item) => !ColumnDescriptor.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'columns': columns,
+      };
+}
+
+/// Arguments for 'next' request.
+class NextArguments extends RequestArguments {
+  /// Optional granularity to step. If no granularity is specified, a
+  /// granularity of 'statement' is assumed.
+  final SteppingGranularity? granularity;
+
+  /// Execute 'next' for this thread.
+  final int threadId;
+
+  static NextArguments fromJson(Map<String, Object?> obj) =>
+      NextArguments.fromMap(obj);
+
+  NextArguments({
+    this.granularity,
+    required this.threadId,
+  });
+
+  NextArguments.fromMap(Map<String, Object?> obj)
+      : granularity = obj['granularity'] == null
+            ? null
+            : SteppingGranularity.fromJson(
+                obj['granularity'] as Map<String, Object?>),
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!SteppingGranularity?.canParse(obj['granularity'])) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (granularity != null) 'granularity': granularity,
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'next' request. This is just an acknowledgement, so no body
+/// field is required.
+class NextResponse extends Response {
+  static NextResponse fromJson(Map<String, Object?> obj) =>
+      NextResponse.fromMap(obj);
+
+  NextResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  NextResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'pause' request.
+class PauseArguments extends RequestArguments {
+  /// Pause execution for this thread.
+  final int threadId;
+
+  static PauseArguments fromJson(Map<String, Object?> obj) =>
+      PauseArguments.fromMap(obj);
+
+  PauseArguments({
+    required this.threadId,
+  });
+
+  PauseArguments.fromMap(Map<String, Object?> obj)
+      : threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'pause' request. This is just an acknowledgement, so no body
+/// field is required.
+class PauseResponse extends Response {
+  static PauseResponse fromJson(Map<String, Object?> obj) =>
+      PauseResponse.fromMap(obj);
+
+  PauseResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  PauseResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Base class of requests, responses, and events.
+class ProtocolMessage {
+  /// Sequence number (also known as message ID). For protocol messages of type
+  /// 'request' this ID can be used to cancel the request.
+  final int seq;
+
+  /// Message type.
+  final String type;
+
+  static ProtocolMessage fromJson(Map<String, Object?> obj) =>
+      ProtocolMessage.fromMap(obj);
+
+  ProtocolMessage({
+    required this.seq,
+    required this.type,
+  });
+
+  ProtocolMessage.fromMap(Map<String, Object?> obj)
+      : seq = obj['seq'] as int,
+        type = obj['type'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['seq'] is! int) {
+      return false;
+    }
+    if (obj['type'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'seq': seq,
+        'type': type,
+      };
+}
+
+/// Arguments for 'readMemory' request.
+class ReadMemoryArguments extends RequestArguments {
+  /// Number of bytes to read at the specified location and offset.
+  final int count;
+
+  /// Memory reference to the base location from which data should be read.
+  final String memoryReference;
+
+  /// Optional offset (in bytes) to be applied to the reference location before
+  /// reading data. Can be negative.
+  final int? offset;
+
+  static ReadMemoryArguments fromJson(Map<String, Object?> obj) =>
+      ReadMemoryArguments.fromMap(obj);
+
+  ReadMemoryArguments({
+    required this.count,
+    required this.memoryReference,
+    this.offset,
+  });
+
+  ReadMemoryArguments.fromMap(Map<String, Object?> obj)
+      : count = obj['count'] as int,
+        memoryReference = obj['memoryReference'] as String,
+        offset = obj['offset'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['count'] is! int) {
+      return false;
+    }
+    if (obj['memoryReference'] is! String) {
+      return false;
+    }
+    if (obj['offset'] is! int?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'count': count,
+        'memoryReference': memoryReference,
+        if (offset != null) 'offset': offset,
+      };
+}
+
+/// Response to 'readMemory' request.
+class ReadMemoryResponse extends Response {
+  static ReadMemoryResponse fromJson(Map<String, Object?> obj) =>
+      ReadMemoryResponse.fromMap(obj);
+
+  ReadMemoryResponse({
+    Map<String, Object?>? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ReadMemoryResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>?) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A client or debug adapter initiated request.
+class Request extends ProtocolMessage {
+  /// Object containing arguments for the command.
+  final Object? arguments;
+
+  /// The command to execute.
+  final String command;
+
+  static Request fromJson(Map<String, Object?> obj) => Request.fromMap(obj);
+
+  Request({
+    this.arguments,
+    required this.command,
+    required int seq,
+  }) : super(
+          seq: seq,
+          type: 'request',
+        );
+
+  Request.fromMap(Map<String, Object?> obj)
+      : arguments = obj['arguments'],
+        command = obj['command'] as String,
+        super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['command'] is! String) {
+      return false;
+    }
+    if (obj['type'] is! String) {
+      return false;
+    }
+    return ProtocolMessage.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+        if (arguments != null) 'arguments': arguments,
+        'command': command,
+      };
+}
+
+/// Response for a request.
+class Response extends ProtocolMessage {
+  /// Contains request result if success is true and optional error details if
+  /// success is false.
+  final Object? body;
+
+  /// The command requested.
+  final String command;
+
+  /// Contains the raw error in short form if 'success' is false.
+  /// This raw error might be interpreted by the frontend and is not shown in
+  /// the UI.
+  /// Some predefined values exist.
+  final String? message;
+
+  /// Sequence number of the corresponding request.
+  final int requestSeq;
+
+  /// Outcome of the request.
+  /// If true, the request was successful and the 'body' attribute may contain
+  /// the result of the request.
+  /// If the value is false, the attribute 'message' contains the error in short
+  /// form and the 'body' may contain additional information (see
+  /// 'ErrorResponse.body.error').
+  final bool success;
+
+  static Response fromJson(Map<String, Object?> obj) => Response.fromMap(obj);
+
+  Response({
+    this.body,
+    required this.command,
+    this.message,
+    required this.requestSeq,
+    required this.success,
+    required int seq,
+  }) : super(
+          seq: seq,
+          type: 'response',
+        );
+
+  Response.fromMap(Map<String, Object?> obj)
+      : body = obj['body'],
+        command = obj['command'] as String,
+        message = obj['message'] as String?,
+        requestSeq = obj['request_seq'] as int,
+        success = obj['success'] as bool,
+        super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['command'] is! String) {
+      return false;
+    }
+    if (obj['message'] is! String?) {
+      return false;
+    }
+    if (obj['request_seq'] is! int) {
+      return false;
+    }
+    if (obj['success'] is! bool) {
+      return false;
+    }
+    if (obj['type'] is! String) {
+      return false;
+    }
+    return ProtocolMessage.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+        if (body != null) 'body': body,
+        'command': command,
+        if (message != null) 'message': message,
+        'request_seq': requestSeq,
+        'success': success,
+      };
+}
+
+/// Arguments for 'restart' request.
+class RestartArguments extends RequestArguments {
+  /// The latest version of the 'launch' or 'attach' configuration.
+  final Either2<LaunchRequestArguments, AttachRequestArguments>? arguments;
+
+  static RestartArguments fromJson(Map<String, Object?> obj) =>
+      RestartArguments.fromMap(obj);
+
+  RestartArguments({
+    this.arguments,
+  });
+
+  RestartArguments.fromMap(Map<String, Object?> obj)
+      : arguments = LaunchRequestArguments.canParse(obj['arguments'])
+            ? Either2<LaunchRequestArguments, AttachRequestArguments>.t1(
+                LaunchRequestArguments.fromJson(
+                    obj['arguments'] as Map<String, Object?>))
+            : AttachRequestArguments.canParse(obj['arguments'])
+                ? Either2<LaunchRequestArguments, AttachRequestArguments>.t2(
+                    AttachRequestArguments.fromJson(
+                        obj['arguments'] as Map<String, Object?>))
+                : null;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((!LaunchRequestArguments.canParse(obj['arguments']) &&
+        !AttachRequestArguments.canParse(obj['arguments']) &&
+        obj['arguments'] != null)) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (arguments != null) 'arguments': arguments,
+      };
+}
+
+/// Arguments for 'restartFrame' request.
+class RestartFrameArguments extends RequestArguments {
+  /// Restart this stackframe.
+  final int frameId;
+
+  static RestartFrameArguments fromJson(Map<String, Object?> obj) =>
+      RestartFrameArguments.fromMap(obj);
+
+  RestartFrameArguments({
+    required this.frameId,
+  });
+
+  RestartFrameArguments.fromMap(Map<String, Object?> obj)
+      : frameId = obj['frameId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['frameId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'frameId': frameId,
+      };
+}
+
+/// Response to 'restartFrame' request. This is just an acknowledgement, so no
+/// body field is required.
+class RestartFrameResponse extends Response {
+  static RestartFrameResponse fromJson(Map<String, Object?> obj) =>
+      RestartFrameResponse.fromMap(obj);
+
+  RestartFrameResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  RestartFrameResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Response to 'restart' request. This is just an acknowledgement, so no body
+/// field is required.
+class RestartResponse extends Response {
+  static RestartResponse fromJson(Map<String, Object?> obj) =>
+      RestartResponse.fromMap(obj);
+
+  RestartResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  RestartResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'reverseContinue' request.
+class ReverseContinueArguments extends RequestArguments {
+  /// Execute 'reverseContinue' for this thread.
+  final int threadId;
+
+  static ReverseContinueArguments fromJson(Map<String, Object?> obj) =>
+      ReverseContinueArguments.fromMap(obj);
+
+  ReverseContinueArguments({
+    required this.threadId,
+  });
+
+  ReverseContinueArguments.fromMap(Map<String, Object?> obj)
+      : threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'reverseContinue' request. This is just an acknowledgement, so
+/// no body field is required.
+class ReverseContinueResponse extends Response {
+  static ReverseContinueResponse fromJson(Map<String, Object?> obj) =>
+      ReverseContinueResponse.fromMap(obj);
+
+  ReverseContinueResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ReverseContinueResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'runInTerminal' request.
+class RunInTerminalRequestArguments extends RequestArguments {
+  /// List of arguments. The first argument is the command to run.
+  final List<String> args;
+
+  /// Working directory for the command. For non-empty, valid paths this
+  /// typically results in execution of a change directory command.
+  final String cwd;
+
+  /// Environment key-value pairs that are added to or removed from the default
+  /// environment.
+  final Map<String, Object?>? env;
+
+  /// What kind of terminal to launch.
+  final String? kind;
+
+  /// Optional title of the terminal.
+  final String? title;
+
+  static RunInTerminalRequestArguments fromJson(Map<String, Object?> obj) =>
+      RunInTerminalRequestArguments.fromMap(obj);
+
+  RunInTerminalRequestArguments({
+    required this.args,
+    required this.cwd,
+    this.env,
+    this.kind,
+    this.title,
+  });
+
+  RunInTerminalRequestArguments.fromMap(Map<String, Object?> obj)
+      : args = (obj['args'] as List).map((item) => item as String).toList(),
+        cwd = obj['cwd'] as String,
+        env = obj['env'] as Map<String, Object?>?,
+        kind = obj['kind'] as String?,
+        title = obj['title'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['args'] is! List ||
+        (obj['args'].any((item) => item is! String)))) {
+      return false;
+    }
+    if (obj['cwd'] is! String) {
+      return false;
+    }
+    if (obj['env'] is! Map<String, Object?>?) {
+      return false;
+    }
+    if (obj['kind'] is! String?) {
+      return false;
+    }
+    if (obj['title'] is! String?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'args': args,
+        'cwd': cwd,
+        if (env != null) 'env': env,
+        if (kind != null) 'kind': kind,
+        if (title != null) 'title': title,
+      };
+}
+
+/// Response to 'runInTerminal' request.
+class RunInTerminalResponse extends Response {
+  static RunInTerminalResponse fromJson(Map<String, Object?> obj) =>
+      RunInTerminalResponse.fromMap(obj);
+
+  RunInTerminalResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  RunInTerminalResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A Scope is a named container for variables. Optionally a scope can map to a
+/// source or a range within a source.
+class Scope {
+  /// Optional start column of the range covered by this scope.
+  final int? column;
+
+  /// Optional end column of the range covered by this scope.
+  final int? endColumn;
+
+  /// Optional end line of the range covered by this scope.
+  final int? endLine;
+
+  /// If true, the number of variables in this scope is large or expensive to
+  /// retrieve.
+  final bool expensive;
+
+  /// The number of indexed variables in this scope.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  final int? indexedVariables;
+
+  /// Optional start line of the range covered by this scope.
+  final int? line;
+
+  /// Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This
+  /// string is shown in the UI as is and can be translated.
+  final String name;
+
+  /// The number of named variables in this scope.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  final int? namedVariables;
+
+  /// An optional hint for how to present this scope in the UI. If this
+  /// attribute is missing, the scope is shown with a generic UI.
+  final String? presentationHint;
+
+  /// Optional source for this scope.
+  final Source? source;
+
+  /// The variables of this scope can be retrieved by passing the value of
+  /// variablesReference to the VariablesRequest.
+  final int variablesReference;
+
+  static Scope fromJson(Map<String, Object?> obj) => Scope.fromMap(obj);
+
+  Scope({
+    this.column,
+    this.endColumn,
+    this.endLine,
+    required this.expensive,
+    this.indexedVariables,
+    this.line,
+    required this.name,
+    this.namedVariables,
+    this.presentationHint,
+    this.source,
+    required this.variablesReference,
+  });
+
+  Scope.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        expensive = obj['expensive'] as bool,
+        indexedVariables = obj['indexedVariables'] as int?,
+        line = obj['line'] as int?,
+        name = obj['name'] as String,
+        namedVariables = obj['namedVariables'] as int?,
+        presentationHint = obj['presentationHint'] as String?,
+        source = obj['source'] == null
+            ? null
+            : Source.fromJson(obj['source'] as Map<String, Object?>),
+        variablesReference = obj['variablesReference'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['expensive'] is! bool) {
+      return false;
+    }
+    if (obj['indexedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['line'] is! int?) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['namedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['presentationHint'] is! String?) {
+      return false;
+    }
+    if (!Source?.canParse(obj['source'])) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        'expensive': expensive,
+        if (indexedVariables != null) 'indexedVariables': indexedVariables,
+        if (line != null) 'line': line,
+        'name': name,
+        if (namedVariables != null) 'namedVariables': namedVariables,
+        if (presentationHint != null) 'presentationHint': presentationHint,
+        if (source != null) 'source': source,
+        'variablesReference': variablesReference,
+      };
+}
+
+/// Arguments for 'scopes' request.
+class ScopesArguments extends RequestArguments {
+  /// Retrieve the scopes for this stackframe.
+  final int frameId;
+
+  static ScopesArguments fromJson(Map<String, Object?> obj) =>
+      ScopesArguments.fromMap(obj);
+
+  ScopesArguments({
+    required this.frameId,
+  });
+
+  ScopesArguments.fromMap(Map<String, Object?> obj)
+      : frameId = obj['frameId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['frameId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'frameId': frameId,
+      };
+}
+
+/// Response to 'scopes' request.
+class ScopesResponse extends Response {
+  static ScopesResponse fromJson(Map<String, Object?> obj) =>
+      ScopesResponse.fromMap(obj);
+
+  ScopesResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ScopesResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setBreakpoints' request.
+class SetBreakpointsArguments extends RequestArguments {
+  /// The code locations of the breakpoints.
+  final List<SourceBreakpoint>? breakpoints;
+
+  /// Deprecated: The code locations of the breakpoints.
+  final List<int>? lines;
+
+  /// The source location of the breakpoints; either 'source.path' or
+  /// 'source.reference' must be specified.
+  final Source source;
+
+  /// A value of true indicates that the underlying source has been modified
+  /// which results in new breakpoint locations.
+  final bool? sourceModified;
+
+  static SetBreakpointsArguments fromJson(Map<String, Object?> obj) =>
+      SetBreakpointsArguments.fromMap(obj);
+
+  SetBreakpointsArguments({
+    this.breakpoints,
+    this.lines,
+    required this.source,
+    this.sourceModified,
+  });
+
+  SetBreakpointsArguments.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List?)
+            ?.map((item) =>
+                SourceBreakpoint.fromJson(item as Map<String, Object?>))
+            .toList(),
+        lines = (obj['lines'] as List?)?.map((item) => item as int).toList(),
+        source = Source.fromJson(obj['source'] as Map<String, Object?>),
+        sourceModified = obj['sourceModified'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !SourceBreakpoint.canParse(item))))) {
+      return false;
+    }
+    if ((obj['lines'] is! List || (obj['lines'].any((item) => item is! int)))) {
+      return false;
+    }
+    if (!Source.canParse(obj['source'])) {
+      return false;
+    }
+    if (obj['sourceModified'] is! bool?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (breakpoints != null) 'breakpoints': breakpoints,
+        if (lines != null) 'lines': lines,
+        'source': source,
+        if (sourceModified != null) 'sourceModified': sourceModified,
+      };
+}
+
+/// Response to 'setBreakpoints' request.
+/// Returned is information about each breakpoint created by this request.
+/// This includes the actual code location and whether the breakpoint could be
+/// verified.
+/// The breakpoints returned are in the same order as the elements of the
+/// 'breakpoints'
+/// (or the deprecated 'lines') array in the arguments.
+class SetBreakpointsResponse extends Response {
+  static SetBreakpointsResponse fromJson(Map<String, Object?> obj) =>
+      SetBreakpointsResponse.fromMap(obj);
+
+  SetBreakpointsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetBreakpointsResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setDataBreakpoints' request.
+class SetDataBreakpointsArguments extends RequestArguments {
+  /// The contents of this array replaces all existing data breakpoints. An
+  /// empty array clears all data breakpoints.
+  final List<DataBreakpoint> breakpoints;
+
+  static SetDataBreakpointsArguments fromJson(Map<String, Object?> obj) =>
+      SetDataBreakpointsArguments.fromMap(obj);
+
+  SetDataBreakpointsArguments({
+    required this.breakpoints,
+  });
+
+  SetDataBreakpointsArguments.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map(
+                (item) => DataBreakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !DataBreakpoint.canParse(item))))) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+/// Response to 'setDataBreakpoints' request.
+/// Returned is information about each breakpoint created by this request.
+class SetDataBreakpointsResponse extends Response {
+  static SetDataBreakpointsResponse fromJson(Map<String, Object?> obj) =>
+      SetDataBreakpointsResponse.fromMap(obj);
+
+  SetDataBreakpointsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetDataBreakpointsResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setExceptionBreakpoints' request.
+class SetExceptionBreakpointsArguments extends RequestArguments {
+  /// Configuration options for selected exceptions.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsExceptionOptions' is true.
+  final List<ExceptionOptions>? exceptionOptions;
+
+  /// Set of exception filters and their options. The set of all possible
+  /// exception filters is defined by the 'exceptionBreakpointFilters'
+  /// capability. This attribute is only honored by a debug adapter if the
+  /// capability 'supportsExceptionFilterOptions' is true. The 'filter' and
+  /// 'filterOptions' sets are additive.
+  final List<ExceptionFilterOptions>? filterOptions;
+
+  /// Set of exception filters specified by their ID. The set of all possible
+  /// exception filters is defined by the 'exceptionBreakpointFilters'
+  /// capability. The 'filter' and 'filterOptions' sets are additive.
+  final List<String> filters;
+
+  static SetExceptionBreakpointsArguments fromJson(Map<String, Object?> obj) =>
+      SetExceptionBreakpointsArguments.fromMap(obj);
+
+  SetExceptionBreakpointsArguments({
+    this.exceptionOptions,
+    this.filterOptions,
+    required this.filters,
+  });
+
+  SetExceptionBreakpointsArguments.fromMap(Map<String, Object?> obj)
+      : exceptionOptions = (obj['exceptionOptions'] as List?)
+            ?.map((item) =>
+                ExceptionOptions.fromJson(item as Map<String, Object?>))
+            .toList(),
+        filterOptions = (obj['filterOptions'] as List?)
+            ?.map((item) =>
+                ExceptionFilterOptions.fromJson(item as Map<String, Object?>))
+            .toList(),
+        filters =
+            (obj['filters'] as List).map((item) => item as String).toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['exceptionOptions'] is! List ||
+        (obj['exceptionOptions']
+            .any((item) => !ExceptionOptions.canParse(item))))) {
+      return false;
+    }
+    if ((obj['filterOptions'] is! List ||
+        (obj['filterOptions']
+            .any((item) => !ExceptionFilterOptions.canParse(item))))) {
+      return false;
+    }
+    if ((obj['filters'] is! List ||
+        (obj['filters'].any((item) => item is! String)))) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (exceptionOptions != null) 'exceptionOptions': exceptionOptions,
+        if (filterOptions != null) 'filterOptions': filterOptions,
+        'filters': filters,
+      };
+}
+
+/// Response to 'setExceptionBreakpoints' request.
+/// The response contains an array of Breakpoint objects with information about
+/// each exception breakpoint or filter. The Breakpoint objects are in the same
+/// order as the elements of the 'filters', 'filterOptions', 'exceptionOptions'
+/// arrays given as arguments. If both 'filters' and 'filterOptions' are given,
+/// the returned array must start with 'filters' information first, followed by
+/// 'filterOptions' information.
+/// The mandatory 'verified' property of a Breakpoint object signals whether the
+/// exception breakpoint or filter could be successfully created and whether the
+/// optional condition or hit count expressions are valid. In case of an error
+/// the 'message' property explains the problem. An optional 'id' property can
+/// be used to introduce a unique ID for the exception breakpoint or filter so
+/// that it can be updated subsequently by sending breakpoint events.
+/// For backward compatibility both the 'breakpoints' array and the enclosing
+/// 'body' are optional. If these elements are missing a client will not be able
+/// to show problems for individual exception breakpoints or filters.
+class SetExceptionBreakpointsResponse extends Response {
+  static SetExceptionBreakpointsResponse fromJson(Map<String, Object?> obj) =>
+      SetExceptionBreakpointsResponse.fromMap(obj);
+
+  SetExceptionBreakpointsResponse({
+    Map<String, Object?>? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetExceptionBreakpointsResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>?) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setExpression' request.
+class SetExpressionArguments extends RequestArguments {
+  /// The l-value expression to assign to.
+  final String expression;
+
+  /// Specifies how the resulting value should be formatted.
+  final ValueFormat? format;
+
+  /// Evaluate the expressions in the scope of this stack frame. If not
+  /// specified, the expressions are evaluated in the global scope.
+  final int? frameId;
+
+  /// The value expression to assign to the l-value expression.
+  final String value;
+
+  static SetExpressionArguments fromJson(Map<String, Object?> obj) =>
+      SetExpressionArguments.fromMap(obj);
+
+  SetExpressionArguments({
+    required this.expression,
+    this.format,
+    this.frameId,
+    required this.value,
+  });
+
+  SetExpressionArguments.fromMap(Map<String, Object?> obj)
+      : expression = obj['expression'] as String,
+        format = obj['format'] == null
+            ? null
+            : ValueFormat.fromJson(obj['format'] as Map<String, Object?>),
+        frameId = obj['frameId'] as int?,
+        value = obj['value'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['expression'] is! String) {
+      return false;
+    }
+    if (!ValueFormat?.canParse(obj['format'])) {
+      return false;
+    }
+    if (obj['frameId'] is! int?) {
+      return false;
+    }
+    if (obj['value'] is! String) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'expression': expression,
+        if (format != null) 'format': format,
+        if (frameId != null) 'frameId': frameId,
+        'value': value,
+      };
+}
+
+/// Response to 'setExpression' request.
+class SetExpressionResponse extends Response {
+  static SetExpressionResponse fromJson(Map<String, Object?> obj) =>
+      SetExpressionResponse.fromMap(obj);
+
+  SetExpressionResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetExpressionResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setFunctionBreakpoints' request.
+class SetFunctionBreakpointsArguments extends RequestArguments {
+  /// The function names of the breakpoints.
+  final List<FunctionBreakpoint> breakpoints;
+
+  static SetFunctionBreakpointsArguments fromJson(Map<String, Object?> obj) =>
+      SetFunctionBreakpointsArguments.fromMap(obj);
+
+  SetFunctionBreakpointsArguments({
+    required this.breakpoints,
+  });
+
+  SetFunctionBreakpointsArguments.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) =>
+                FunctionBreakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints']
+            .any((item) => !FunctionBreakpoint.canParse(item))))) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+/// Response to 'setFunctionBreakpoints' request.
+/// Returned is information about each breakpoint created by this request.
+class SetFunctionBreakpointsResponse extends Response {
+  static SetFunctionBreakpointsResponse fromJson(Map<String, Object?> obj) =>
+      SetFunctionBreakpointsResponse.fromMap(obj);
+
+  SetFunctionBreakpointsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetFunctionBreakpointsResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setInstructionBreakpoints' request
+class SetInstructionBreakpointsArguments extends RequestArguments {
+  /// The instruction references of the breakpoints
+  final List<InstructionBreakpoint> breakpoints;
+
+  static SetInstructionBreakpointsArguments fromJson(
+          Map<String, Object?> obj) =>
+      SetInstructionBreakpointsArguments.fromMap(obj);
+
+  SetInstructionBreakpointsArguments({
+    required this.breakpoints,
+  });
+
+  SetInstructionBreakpointsArguments.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) =>
+                InstructionBreakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints']
+            .any((item) => !InstructionBreakpoint.canParse(item))))) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+/// Response to 'setInstructionBreakpoints' request
+class SetInstructionBreakpointsResponse extends Response {
+  static SetInstructionBreakpointsResponse fromJson(Map<String, Object?> obj) =>
+      SetInstructionBreakpointsResponse.fromMap(obj);
+
+  SetInstructionBreakpointsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetInstructionBreakpointsResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'setVariable' request.
+class SetVariableArguments extends RequestArguments {
+  /// Specifies details on how to format the response value.
+  final ValueFormat? format;
+
+  /// The name of the variable in the container.
+  final String name;
+
+  /// The value of the variable.
+  final String value;
+
+  /// The reference of the variable container.
+  final int variablesReference;
+
+  static SetVariableArguments fromJson(Map<String, Object?> obj) =>
+      SetVariableArguments.fromMap(obj);
+
+  SetVariableArguments({
+    this.format,
+    required this.name,
+    required this.value,
+    required this.variablesReference,
+  });
+
+  SetVariableArguments.fromMap(Map<String, Object?> obj)
+      : format = obj['format'] == null
+            ? null
+            : ValueFormat.fromJson(obj['format'] as Map<String, Object?>),
+        name = obj['name'] as String,
+        value = obj['value'] as String,
+        variablesReference = obj['variablesReference'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!ValueFormat?.canParse(obj['format'])) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['value'] is! String) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (format != null) 'format': format,
+        'name': name,
+        'value': value,
+        'variablesReference': variablesReference,
+      };
+}
+
+/// Response to 'setVariable' request.
+class SetVariableResponse extends Response {
+  static SetVariableResponse fromJson(Map<String, Object?> obj) =>
+      SetVariableResponse.fromMap(obj);
+
+  SetVariableResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SetVariableResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A Source is a descriptor for source code.
+/// It is returned from the debug adapter as part of a StackFrame and it is used
+/// by clients when specifying breakpoints.
+class Source {
+  /// Optional data that a debug adapter might want to loop through the client.
+  /// The client should leave the data intact and persist it across sessions.
+  /// The client should not interpret the data.
+  final Object? adapterData;
+
+  /// The checksums associated with this file.
+  final List<Checksum>? checksums;
+
+  /// The short name of the source. Every source returned from the debug adapter
+  /// has a name.
+  /// When sending a source to the debug adapter this name is optional.
+  final String? name;
+
+  /// The (optional) origin of this source: possible values 'internal module',
+  /// 'inlined content from source map', etc.
+  final String? origin;
+
+  /// The path of the source to be shown in the UI.
+  /// It is only used to locate and load the content of the source if no
+  /// sourceReference is specified (or its value is 0).
+  final String? path;
+
+  /// An optional hint for how to present the source in the UI.
+  /// A value of 'deemphasize' can be used to indicate that the source is not
+  /// available or that it is skipped on stepping.
+  final String? presentationHint;
+
+  /// If sourceReference > 0 the contents of the source must be retrieved
+  /// through the SourceRequest (even if a path is specified).
+  /// A sourceReference is only valid for a session, so it must not be used to
+  /// persist a source.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? sourceReference;
+
+  /// An optional list of sources that are related to this source. These may be
+  /// the source that generated this source.
+  final List<Source>? sources;
+
+  static Source fromJson(Map<String, Object?> obj) => Source.fromMap(obj);
+
+  Source({
+    this.adapterData,
+    this.checksums,
+    this.name,
+    this.origin,
+    this.path,
+    this.presentationHint,
+    this.sourceReference,
+    this.sources,
+  });
+
+  Source.fromMap(Map<String, Object?> obj)
+      : adapterData = obj['adapterData'],
+        checksums = (obj['checksums'] as List?)
+            ?.map((item) => Checksum.fromJson(item as Map<String, Object?>))
+            .toList(),
+        name = obj['name'] as String?,
+        origin = obj['origin'] as String?,
+        path = obj['path'] as String?,
+        presentationHint = obj['presentationHint'] as String?,
+        sourceReference = obj['sourceReference'] as int?,
+        sources = (obj['sources'] as List?)
+            ?.map((item) => Source.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['checksums'] is! List ||
+        (obj['checksums'].any((item) => !Checksum.canParse(item))))) {
+      return false;
+    }
+    if (obj['name'] is! String?) {
+      return false;
+    }
+    if (obj['origin'] is! String?) {
+      return false;
+    }
+    if (obj['path'] is! String?) {
+      return false;
+    }
+    if (obj['presentationHint'] is! String?) {
+      return false;
+    }
+    if (obj['sourceReference'] is! int?) {
+      return false;
+    }
+    if ((obj['sources'] is! List ||
+        (obj['sources'].any((item) => !Source.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (adapterData != null) 'adapterData': adapterData,
+        if (checksums != null) 'checksums': checksums,
+        if (name != null) 'name': name,
+        if (origin != null) 'origin': origin,
+        if (path != null) 'path': path,
+        if (presentationHint != null) 'presentationHint': presentationHint,
+        if (sourceReference != null) 'sourceReference': sourceReference,
+        if (sources != null) 'sources': sources,
+      };
+}
+
+/// Arguments for 'source' request.
+class SourceArguments extends RequestArguments {
+  /// Specifies the source content to load. Either source.path or
+  /// source.sourceReference must be specified.
+  final Source? source;
+
+  /// The reference to the source. This is the same as source.sourceReference.
+  /// This is provided for backward compatibility since old backends do not
+  /// understand the 'source' attribute.
+  final int sourceReference;
+
+  static SourceArguments fromJson(Map<String, Object?> obj) =>
+      SourceArguments.fromMap(obj);
+
+  SourceArguments({
+    this.source,
+    required this.sourceReference,
+  });
+
+  SourceArguments.fromMap(Map<String, Object?> obj)
+      : source = obj['source'] == null
+            ? null
+            : Source.fromJson(obj['source'] as Map<String, Object?>),
+        sourceReference = obj['sourceReference'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!Source?.canParse(obj['source'])) {
+      return false;
+    }
+    if (obj['sourceReference'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (source != null) 'source': source,
+        'sourceReference': sourceReference,
+      };
+}
+
+/// Properties of a breakpoint or logpoint passed to the setBreakpoints request.
+class SourceBreakpoint {
+  /// An optional source column of the breakpoint.
+  final int? column;
+
+  /// An optional expression for conditional breakpoints.
+  /// It is only honored by a debug adapter if the capability
+  /// 'supportsConditionalBreakpoints' is true.
+  final String? condition;
+
+  /// An optional expression that controls how many hits of the breakpoint are
+  /// ignored.
+  /// The backend is expected to interpret the expression as needed.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsHitConditionalBreakpoints' is true.
+  final String? hitCondition;
+
+  /// The source line of the breakpoint or logpoint.
+  final int line;
+
+  /// If this attribute exists and is non-empty, the backend must not 'break'
+  /// (stop)
+  /// but log the message instead. Expressions within {} are interpolated.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsLogPoints' is true.
+  final String? logMessage;
+
+  static SourceBreakpoint fromJson(Map<String, Object?> obj) =>
+      SourceBreakpoint.fromMap(obj);
+
+  SourceBreakpoint({
+    this.column,
+    this.condition,
+    this.hitCondition,
+    required this.line,
+    this.logMessage,
+  });
+
+  SourceBreakpoint.fromMap(Map<String, Object?> obj)
+      : column = obj['column'] as int?,
+        condition = obj['condition'] as String?,
+        hitCondition = obj['hitCondition'] as String?,
+        line = obj['line'] as int,
+        logMessage = obj['logMessage'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['condition'] is! String?) {
+      return false;
+    }
+    if (obj['hitCondition'] is! String?) {
+      return false;
+    }
+    if (obj['line'] is! int) {
+      return false;
+    }
+    if (obj['logMessage'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (column != null) 'column': column,
+        if (condition != null) 'condition': condition,
+        if (hitCondition != null) 'hitCondition': hitCondition,
+        'line': line,
+        if (logMessage != null) 'logMessage': logMessage,
+      };
+}
+
+/// Response to 'source' request.
+class SourceResponse extends Response {
+  static SourceResponse fromJson(Map<String, Object?> obj) =>
+      SourceResponse.fromMap(obj);
+
+  SourceResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  SourceResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A Stackframe contains the source location.
+class StackFrame {
+  /// Indicates whether this frame can be restarted with the 'restart' request.
+  /// Clients should only use this if the debug adapter supports the 'restart'
+  /// request (capability 'supportsRestartRequest' is true).
+  final bool? canRestart;
+
+  /// The column within the line. If source is null or doesn't exist, column is
+  /// 0 and must be ignored.
+  final int column;
+
+  /// An optional end column of the range covered by the stack frame.
+  final int? endColumn;
+
+  /// An optional end line of the range covered by the stack frame.
+  final int? endLine;
+
+  /// An identifier for the stack frame. It must be unique across all threads.
+  /// This id can be used to retrieve the scopes of the frame with the
+  /// 'scopesRequest' or to restart the execution of a stackframe.
+  final int id;
+
+  /// Optional memory reference for the current instruction pointer in this
+  /// frame.
+  final String? instructionPointerReference;
+
+  /// The line within the file of the frame. If source is null or doesn't exist,
+  /// line is 0 and must be ignored.
+  final int line;
+
+  /// The module associated with this frame, if any.
+  final Either2<int, String>? moduleId;
+
+  /// The name of the stack frame, typically a method name.
+  final String name;
+
+  /// An optional hint for how to present this frame in the UI.
+  /// A value of 'label' can be used to indicate that the frame is an artificial
+  /// frame that is used as a visual label or separator. A value of 'subtle' can
+  /// be used to change the appearance of a frame in a 'subtle' way.
+  final String? presentationHint;
+
+  /// The optional source of the frame.
+  final Source? source;
+
+  static StackFrame fromJson(Map<String, Object?> obj) =>
+      StackFrame.fromMap(obj);
+
+  StackFrame({
+    this.canRestart,
+    required this.column,
+    this.endColumn,
+    this.endLine,
+    required this.id,
+    this.instructionPointerReference,
+    required this.line,
+    this.moduleId,
+    required this.name,
+    this.presentationHint,
+    this.source,
+  });
+
+  StackFrame.fromMap(Map<String, Object?> obj)
+      : canRestart = obj['canRestart'] as bool?,
+        column = obj['column'] as int,
+        endColumn = obj['endColumn'] as int?,
+        endLine = obj['endLine'] as int?,
+        id = obj['id'] as int,
+        instructionPointerReference =
+            obj['instructionPointerReference'] as String?,
+        line = obj['line'] as int,
+        moduleId = obj['moduleId'] is int
+            ? Either2<int, String>.t1(obj['moduleId'] as int)
+            : obj['moduleId'] is String
+                ? Either2<int, String>.t2(obj['moduleId'] as String)
+                : null,
+        name = obj['name'] as String,
+        presentationHint = obj['presentationHint'] as String?,
+        source = obj['source'] == null
+            ? null
+            : Source.fromJson(obj['source'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['canRestart'] is! bool?) {
+      return false;
+    }
+    if (obj['column'] is! int) {
+      return false;
+    }
+    if (obj['endColumn'] is! int?) {
+      return false;
+    }
+    if (obj['endLine'] is! int?) {
+      return false;
+    }
+    if (obj['id'] is! int) {
+      return false;
+    }
+    if (obj['instructionPointerReference'] is! String?) {
+      return false;
+    }
+    if (obj['line'] is! int) {
+      return false;
+    }
+    if ((obj['moduleId'] is! int &&
+        obj['moduleId'] is! String &&
+        obj['moduleId'] != null)) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['presentationHint'] is! String?) {
+      return false;
+    }
+    if (!Source?.canParse(obj['source'])) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (canRestart != null) 'canRestart': canRestart,
+        'column': column,
+        if (endColumn != null) 'endColumn': endColumn,
+        if (endLine != null) 'endLine': endLine,
+        'id': id,
+        if (instructionPointerReference != null)
+          'instructionPointerReference': instructionPointerReference,
+        'line': line,
+        if (moduleId != null) 'moduleId': moduleId,
+        'name': name,
+        if (presentationHint != null) 'presentationHint': presentationHint,
+        if (source != null) 'source': source,
+      };
+}
+
+/// Provides formatting information for a stack frame.
+class StackFrameFormat extends ValueFormat {
+  /// Includes all stack frames, including those the debug adapter might
+  /// otherwise hide.
+  final bool? includeAll;
+
+  /// Displays the line number of the stack frame.
+  final bool? line;
+
+  /// Displays the module of the stack frame.
+  final bool? module;
+
+  /// Displays the names of parameters for the stack frame.
+  final bool? parameterNames;
+
+  /// Displays the types of parameters for the stack frame.
+  final bool? parameterTypes;
+
+  /// Displays the values of parameters for the stack frame.
+  final bool? parameterValues;
+
+  /// Displays parameters for the stack frame.
+  final bool? parameters;
+
+  static StackFrameFormat fromJson(Map<String, Object?> obj) =>
+      StackFrameFormat.fromMap(obj);
+
+  StackFrameFormat({
+    this.includeAll,
+    this.line,
+    this.module,
+    this.parameterNames,
+    this.parameterTypes,
+    this.parameterValues,
+    this.parameters,
+    bool? hex,
+  }) : super(
+          hex: hex,
+        );
+
+  StackFrameFormat.fromMap(Map<String, Object?> obj)
+      : includeAll = obj['includeAll'] as bool?,
+        line = obj['line'] as bool?,
+        module = obj['module'] as bool?,
+        parameterNames = obj['parameterNames'] as bool?,
+        parameterTypes = obj['parameterTypes'] as bool?,
+        parameterValues = obj['parameterValues'] as bool?,
+        parameters = obj['parameters'] as bool?,
+        super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['includeAll'] is! bool?) {
+      return false;
+    }
+    if (obj['line'] is! bool?) {
+      return false;
+    }
+    if (obj['module'] is! bool?) {
+      return false;
+    }
+    if (obj['parameterNames'] is! bool?) {
+      return false;
+    }
+    if (obj['parameterTypes'] is! bool?) {
+      return false;
+    }
+    if (obj['parameterValues'] is! bool?) {
+      return false;
+    }
+    if (obj['parameters'] is! bool?) {
+      return false;
+    }
+    return ValueFormat.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+        if (includeAll != null) 'includeAll': includeAll,
+        if (line != null) 'line': line,
+        if (module != null) 'module': module,
+        if (parameterNames != null) 'parameterNames': parameterNames,
+        if (parameterTypes != null) 'parameterTypes': parameterTypes,
+        if (parameterValues != null) 'parameterValues': parameterValues,
+        if (parameters != null) 'parameters': parameters,
+      };
+}
+
+/// Arguments for 'stackTrace' request.
+class StackTraceArguments extends RequestArguments {
+  /// Specifies details on how to format the stack frames.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsValueFormattingOptions' is true.
+  final StackFrameFormat? format;
+
+  /// The maximum number of frames to return. If levels is not specified or 0,
+  /// all frames are returned.
+  final int? levels;
+
+  /// The index of the first frame to return; if omitted frames start at 0.
+  final int? startFrame;
+
+  /// Retrieve the stacktrace for this thread.
+  final int threadId;
+
+  static StackTraceArguments fromJson(Map<String, Object?> obj) =>
+      StackTraceArguments.fromMap(obj);
+
+  StackTraceArguments({
+    this.format,
+    this.levels,
+    this.startFrame,
+    required this.threadId,
+  });
+
+  StackTraceArguments.fromMap(Map<String, Object?> obj)
+      : format = obj['format'] == null
+            ? null
+            : StackFrameFormat.fromJson(obj['format'] as Map<String, Object?>),
+        levels = obj['levels'] as int?,
+        startFrame = obj['startFrame'] as int?,
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!StackFrameFormat?.canParse(obj['format'])) {
+      return false;
+    }
+    if (obj['levels'] is! int?) {
+      return false;
+    }
+    if (obj['startFrame'] is! int?) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (format != null) 'format': format,
+        if (levels != null) 'levels': levels,
+        if (startFrame != null) 'startFrame': startFrame,
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'stackTrace' request.
+class StackTraceResponse extends Response {
+  static StackTraceResponse fromJson(Map<String, Object?> obj) =>
+      StackTraceResponse.fromMap(obj);
+
+  StackTraceResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  StackTraceResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'stepBack' request.
+class StepBackArguments extends RequestArguments {
+  /// Optional granularity to step. If no granularity is specified, a
+  /// granularity of 'statement' is assumed.
+  final SteppingGranularity? granularity;
+
+  /// Execute 'stepBack' for this thread.
+  final int threadId;
+
+  static StepBackArguments fromJson(Map<String, Object?> obj) =>
+      StepBackArguments.fromMap(obj);
+
+  StepBackArguments({
+    this.granularity,
+    required this.threadId,
+  });
+
+  StepBackArguments.fromMap(Map<String, Object?> obj)
+      : granularity = obj['granularity'] == null
+            ? null
+            : SteppingGranularity.fromJson(
+                obj['granularity'] as Map<String, Object?>),
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!SteppingGranularity?.canParse(obj['granularity'])) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (granularity != null) 'granularity': granularity,
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'stepBack' request. This is just an acknowledgement, so no body
+/// field is required.
+class StepBackResponse extends Response {
+  static StepBackResponse fromJson(Map<String, Object?> obj) =>
+      StepBackResponse.fromMap(obj);
+
+  StepBackResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  StepBackResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'stepIn' request.
+class StepInArguments extends RequestArguments {
+  /// Optional granularity to step. If no granularity is specified, a
+  /// granularity of 'statement' is assumed.
+  final SteppingGranularity? granularity;
+
+  /// Optional id of the target to step into.
+  final int? targetId;
+
+  /// Execute 'stepIn' for this thread.
+  final int threadId;
+
+  static StepInArguments fromJson(Map<String, Object?> obj) =>
+      StepInArguments.fromMap(obj);
+
+  StepInArguments({
+    this.granularity,
+    this.targetId,
+    required this.threadId,
+  });
+
+  StepInArguments.fromMap(Map<String, Object?> obj)
+      : granularity = obj['granularity'] == null
+            ? null
+            : SteppingGranularity.fromJson(
+                obj['granularity'] as Map<String, Object?>),
+        targetId = obj['targetId'] as int?,
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!SteppingGranularity?.canParse(obj['granularity'])) {
+      return false;
+    }
+    if (obj['targetId'] is! int?) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (granularity != null) 'granularity': granularity,
+        if (targetId != null) 'targetId': targetId,
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'stepIn' request. This is just an acknowledgement, so no body
+/// field is required.
+class StepInResponse extends Response {
+  static StepInResponse fromJson(Map<String, Object?> obj) =>
+      StepInResponse.fromMap(obj);
+
+  StepInResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  StepInResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A StepInTarget can be used in the 'stepIn' request and determines into which
+/// single target the stepIn request should step.
+class StepInTarget {
+  /// Unique identifier for a stepIn target.
+  final int id;
+
+  /// The name of the stepIn target (shown in the UI).
+  final String label;
+
+  static StepInTarget fromJson(Map<String, Object?> obj) =>
+      StepInTarget.fromMap(obj);
+
+  StepInTarget({
+    required this.id,
+    required this.label,
+  });
+
+  StepInTarget.fromMap(Map<String, Object?> obj)
+      : id = obj['id'] as int,
+        label = obj['label'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['id'] is! int) {
+      return false;
+    }
+    if (obj['label'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'id': id,
+        'label': label,
+      };
+}
+
+/// Arguments for 'stepInTargets' request.
+class StepInTargetsArguments extends RequestArguments {
+  /// The stack frame for which to retrieve the possible stepIn targets.
+  final int frameId;
+
+  static StepInTargetsArguments fromJson(Map<String, Object?> obj) =>
+      StepInTargetsArguments.fromMap(obj);
+
+  StepInTargetsArguments({
+    required this.frameId,
+  });
+
+  StepInTargetsArguments.fromMap(Map<String, Object?> obj)
+      : frameId = obj['frameId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['frameId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'frameId': frameId,
+      };
+}
+
+/// Response to 'stepInTargets' request.
+class StepInTargetsResponse extends Response {
+  static StepInTargetsResponse fromJson(Map<String, Object?> obj) =>
+      StepInTargetsResponse.fromMap(obj);
+
+  StepInTargetsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  StepInTargetsResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'stepOut' request.
+class StepOutArguments extends RequestArguments {
+  /// Optional granularity to step. If no granularity is specified, a
+  /// granularity of 'statement' is assumed.
+  final SteppingGranularity? granularity;
+
+  /// Execute 'stepOut' for this thread.
+  final int threadId;
+
+  static StepOutArguments fromJson(Map<String, Object?> obj) =>
+      StepOutArguments.fromMap(obj);
+
+  StepOutArguments({
+    this.granularity,
+    required this.threadId,
+  });
+
+  StepOutArguments.fromMap(Map<String, Object?> obj)
+      : granularity = obj['granularity'] == null
+            ? null
+            : SteppingGranularity.fromJson(
+                obj['granularity'] as Map<String, Object?>),
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!SteppingGranularity?.canParse(obj['granularity'])) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (granularity != null) 'granularity': granularity,
+        'threadId': threadId,
+      };
+}
+
+/// Response to 'stepOut' request. This is just an acknowledgement, so no body
+/// field is required.
+class StepOutResponse extends Response {
+  static StepOutResponse fromJson(Map<String, Object?> obj) =>
+      StepOutResponse.fromMap(obj);
+
+  StepOutResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  StepOutResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// The granularity of one 'step' in the stepping requests 'next', 'stepIn',
+/// 'stepOut', and 'stepBack'.
+class SteppingGranularity {
+  static SteppingGranularity fromJson(Map<String, Object?> obj) =>
+      SteppingGranularity.fromMap(obj);
+
+  SteppingGranularity();
+
+  SteppingGranularity.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Arguments for 'terminate' request.
+class TerminateArguments extends RequestArguments {
+  /// A value of true indicates that this 'terminate' request is part of a
+  /// restart sequence.
+  final bool? restart;
+
+  static TerminateArguments fromJson(Map<String, Object?> obj) =>
+      TerminateArguments.fromMap(obj);
+
+  TerminateArguments({
+    this.restart,
+  });
+
+  TerminateArguments.fromMap(Map<String, Object?> obj)
+      : restart = obj['restart'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['restart'] is! bool?) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (restart != null) 'restart': restart,
+      };
+}
+
+/// Response to 'terminate' request. This is just an acknowledgement, so no body
+/// field is required.
+class TerminateResponse extends Response {
+  static TerminateResponse fromJson(Map<String, Object?> obj) =>
+      TerminateResponse.fromMap(obj);
+
+  TerminateResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  TerminateResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Arguments for 'terminateThreads' request.
+class TerminateThreadsArguments extends RequestArguments {
+  /// Ids of threads to be terminated.
+  final List<int>? threadIds;
+
+  static TerminateThreadsArguments fromJson(Map<String, Object?> obj) =>
+      TerminateThreadsArguments.fromMap(obj);
+
+  TerminateThreadsArguments({
+    this.threadIds,
+  });
+
+  TerminateThreadsArguments.fromMap(Map<String, Object?> obj)
+      : threadIds =
+            (obj['threadIds'] as List?)?.map((item) => item as int).toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['threadIds'] is! List ||
+        (obj['threadIds'].any((item) => item is! int)))) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (threadIds != null) 'threadIds': threadIds,
+      };
+}
+
+/// Response to 'terminateThreads' request. This is just an acknowledgement, so
+/// no body field is required.
+class TerminateThreadsResponse extends Response {
+  static TerminateThreadsResponse fromJson(Map<String, Object?> obj) =>
+      TerminateThreadsResponse.fromMap(obj);
+
+  TerminateThreadsResponse({
+    Object? body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  TerminateThreadsResponse.fromMap(Map<String, Object?> obj)
+      : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// A Thread
+class Thread {
+  /// Unique identifier for the thread.
+  final int id;
+
+  /// A name of the thread.
+  final String name;
+
+  static Thread fromJson(Map<String, Object?> obj) => Thread.fromMap(obj);
+
+  Thread({
+    required this.id,
+    required this.name,
+  });
+
+  Thread.fromMap(Map<String, Object?> obj)
+      : id = obj['id'] as int,
+        name = obj['name'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['id'] is! int) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'id': id,
+        'name': name,
+      };
+}
+
+/// Response to 'threads' request.
+class ThreadsResponse extends Response {
+  static ThreadsResponse fromJson(Map<String, Object?> obj) =>
+      ThreadsResponse.fromMap(obj);
+
+  ThreadsResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  ThreadsResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Provides formatting information for a value.
+class ValueFormat {
+  /// Display the value in hex.
+  final bool? hex;
+
+  static ValueFormat fromJson(Map<String, Object?> obj) =>
+      ValueFormat.fromMap(obj);
+
+  ValueFormat({
+    this.hex,
+  });
+
+  ValueFormat.fromMap(Map<String, Object?> obj) : hex = obj['hex'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['hex'] is! bool?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (hex != null) 'hex': hex,
+      };
+}
+
+/// A Variable is a name/value pair.
+/// Optionally a variable can have a 'type' that is shown if space permits or
+/// when hovering over the variable's name.
+/// An optional 'kind' is used to render additional properties of the variable,
+/// e.g. different icons can be used to indicate that a variable is public or
+/// private.
+/// If the value is structured (has children), a handle is provided to retrieve
+/// the children with the VariablesRequest.
+/// If the number of named or indexed children is large, the numbers should be
+/// returned via the optional 'namedVariables' and 'indexedVariables'
+/// attributes.
+/// The client can use this optional information to present the children in a
+/// paged UI and fetch them in chunks.
+class Variable {
+  /// Optional evaluatable name of this variable which can be passed to the
+  /// 'EvaluateRequest' to fetch the variable's value.
+  final String? evaluateName;
+
+  /// The number of indexed child variables.
+  /// The client can use this optional information to present the children in a
+  /// paged UI and fetch them in chunks.
+  final int? indexedVariables;
+
+  /// Optional memory reference for the variable if the variable represents
+  /// executable code, such as a function pointer.
+  /// This attribute is only required if the client has passed the value true
+  /// for the 'supportsMemoryReferences' capability of the 'initialize' request.
+  final String? memoryReference;
+
+  /// The variable's name.
+  final String name;
+
+  /// The number of named child variables.
+  /// The client can use this optional information to present the children in a
+  /// paged UI and fetch them in chunks.
+  final int? namedVariables;
+
+  /// Properties of a variable that can be used to determine how to render the
+  /// variable in the UI.
+  final VariablePresentationHint? presentationHint;
+
+  /// The type of the variable's value. Typically shown in the UI when hovering
+  /// over the value.
+  /// This attribute should only be returned by a debug adapter if the client
+  /// has passed the value true for the 'supportsVariableType' capability of the
+  /// 'initialize' request.
+  final String? type;
+
+  /// The variable's value. This can be a multi-line text, e.g. for a function
+  /// the body of a function.
+  final String value;
+
+  /// If variablesReference is > 0, the variable is structured and its children
+  /// can be retrieved by passing variablesReference to the VariablesRequest.
+  final int variablesReference;
+
+  static Variable fromJson(Map<String, Object?> obj) => Variable.fromMap(obj);
+
+  Variable({
+    this.evaluateName,
+    this.indexedVariables,
+    this.memoryReference,
+    required this.name,
+    this.namedVariables,
+    this.presentationHint,
+    this.type,
+    required this.value,
+    required this.variablesReference,
+  });
+
+  Variable.fromMap(Map<String, Object?> obj)
+      : evaluateName = obj['evaluateName'] as String?,
+        indexedVariables = obj['indexedVariables'] as int?,
+        memoryReference = obj['memoryReference'] as String?,
+        name = obj['name'] as String,
+        namedVariables = obj['namedVariables'] as int?,
+        presentationHint = obj['presentationHint'] == null
+            ? null
+            : VariablePresentationHint.fromJson(
+                obj['presentationHint'] as Map<String, Object?>),
+        type = obj['type'] as String?,
+        value = obj['value'] as String,
+        variablesReference = obj['variablesReference'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['evaluateName'] is! String?) {
+      return false;
+    }
+    if (obj['indexedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['memoryReference'] is! String?) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['namedVariables'] is! int?) {
+      return false;
+    }
+    if (!VariablePresentationHint?.canParse(obj['presentationHint'])) {
+      return false;
+    }
+    if (obj['type'] is! String?) {
+      return false;
+    }
+    if (obj['value'] is! String) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (evaluateName != null) 'evaluateName': evaluateName,
+        if (indexedVariables != null) 'indexedVariables': indexedVariables,
+        if (memoryReference != null) 'memoryReference': memoryReference,
+        'name': name,
+        if (namedVariables != null) 'namedVariables': namedVariables,
+        if (presentationHint != null) 'presentationHint': presentationHint,
+        if (type != null) 'type': type,
+        'value': value,
+        'variablesReference': variablesReference,
+      };
+}
+
+/// Optional properties of a variable that can be used to determine how to
+/// render the variable in the UI.
+class VariablePresentationHint {
+  /// Set of attributes represented as an array of strings. Before introducing
+  /// additional values, try to use the listed values.
+  final List<String>? attributes;
+
+  /// The kind of variable. Before introducing additional values, try to use the
+  /// listed values.
+  final String? kind;
+
+  /// Visibility of variable. Before introducing additional values, try to use
+  /// the listed values.
+  final String? visibility;
+
+  static VariablePresentationHint fromJson(Map<String, Object?> obj) =>
+      VariablePresentationHint.fromMap(obj);
+
+  VariablePresentationHint({
+    this.attributes,
+    this.kind,
+    this.visibility,
+  });
+
+  VariablePresentationHint.fromMap(Map<String, Object?> obj)
+      : attributes = (obj['attributes'] as List?)
+            ?.map((item) => item as String)
+            .toList(),
+        kind = obj['kind'] as String?,
+        visibility = obj['visibility'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['attributes'] is! List ||
+        (obj['attributes'].any((item) => item is! String)))) {
+      return false;
+    }
+    if (obj['kind'] is! String?) {
+      return false;
+    }
+    if (obj['visibility'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (attributes != null) 'attributes': attributes,
+        if (kind != null) 'kind': kind,
+        if (visibility != null) 'visibility': visibility,
+      };
+}
+
+/// Arguments for 'variables' request.
+class VariablesArguments extends RequestArguments {
+  /// The number of variables to return. If count is missing or 0, all variables
+  /// are returned.
+  final int? count;
+
+  /// Optional filter to limit the child variables to either named or indexed.
+  /// If omitted, both types are fetched.
+  final String? filter;
+
+  /// Specifies details on how to format the Variable values.
+  /// The attribute is only honored by a debug adapter if the capability
+  /// 'supportsValueFormattingOptions' is true.
+  final ValueFormat? format;
+
+  /// The index of the first variable to return; if omitted children start at 0.
+  final int? start;
+
+  /// The Variable reference.
+  final int variablesReference;
+
+  static VariablesArguments fromJson(Map<String, Object?> obj) =>
+      VariablesArguments.fromMap(obj);
+
+  VariablesArguments({
+    this.count,
+    this.filter,
+    this.format,
+    this.start,
+    required this.variablesReference,
+  });
+
+  VariablesArguments.fromMap(Map<String, Object?> obj)
+      : count = obj['count'] as int?,
+        filter = obj['filter'] as String?,
+        format = obj['format'] == null
+            ? null
+            : ValueFormat.fromJson(obj['format'] as Map<String, Object?>),
+        start = obj['start'] as int?,
+        variablesReference = obj['variablesReference'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['count'] is! int?) {
+      return false;
+    }
+    if (obj['filter'] is! String?) {
+      return false;
+    }
+    if (!ValueFormat?.canParse(obj['format'])) {
+      return false;
+    }
+    if (obj['start'] is! int?) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int) {
+      return false;
+    }
+    return RequestArguments.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (count != null) 'count': count,
+        if (filter != null) 'filter': filter,
+        if (format != null) 'format': format,
+        if (start != null) 'start': start,
+        'variablesReference': variablesReference,
+      };
+}
+
+/// Response to 'variables' request.
+class VariablesResponse extends Response {
+  static VariablesResponse fromJson(Map<String, Object?> obj) =>
+      VariablesResponse.fromMap(obj);
+
+  VariablesResponse({
+    required Map<String, Object?> body,
+    required String command,
+    String? message,
+    required int requestSeq,
+    required int seq,
+    required bool success,
+  }) : super(
+          seq: seq,
+          requestSeq: requestSeq,
+          success: success,
+          command: command,
+          message: message,
+          body: body,
+        );
+
+  VariablesResponse.fromMap(Map<String, Object?> obj) : super.fromMap(obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['body'] is! Map<String, Object?>) {
+      return false;
+    }
+    return Response.canParse(obj);
+  }
+
+  @override
+  Map<String, Object?> toJson() => {
+        ...super.toJson(),
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class AttachResponseBody {
+  static AttachResponseBody fromJson(Map<String, Object?> obj) =>
+      AttachResponseBody.fromMap(obj);
+
+  AttachResponseBody();
+
+  AttachResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class BreakpointEventBody extends EventBody {
+  /// The 'id' attribute is used to find the target breakpoint and the other
+  /// attributes are used as the new values.
+  final Breakpoint breakpoint;
+
+  /// The reason for the event.
+  final String reason;
+
+  static BreakpointEventBody fromJson(Map<String, Object?> obj) =>
+      BreakpointEventBody.fromMap(obj);
+
+  BreakpointEventBody({
+    required this.breakpoint,
+    required this.reason,
+  });
+
+  BreakpointEventBody.fromMap(Map<String, Object?> obj)
+      : breakpoint =
+            Breakpoint.fromJson(obj['breakpoint'] as Map<String, Object?>),
+        reason = obj['reason'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!Breakpoint.canParse(obj['breakpoint'])) {
+      return false;
+    }
+    if (obj['reason'] is! String) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoint': breakpoint,
+        'reason': reason,
+      };
+}
+
+class BreakpointLocationsResponseBody {
+  /// Sorted set of possible breakpoint locations.
+  final List<BreakpointLocation> breakpoints;
+
+  static BreakpointLocationsResponseBody fromJson(Map<String, Object?> obj) =>
+      BreakpointLocationsResponseBody.fromMap(obj);
+
+  BreakpointLocationsResponseBody({
+    required this.breakpoints,
+  });
+
+  BreakpointLocationsResponseBody.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) =>
+                BreakpointLocation.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints']
+            .any((item) => !BreakpointLocation.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class CancelResponseBody {
+  static CancelResponseBody fromJson(Map<String, Object?> obj) =>
+      CancelResponseBody.fromMap(obj);
+
+  CancelResponseBody();
+
+  CancelResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class CapabilitiesEventBody extends EventBody {
+  /// The set of updated capabilities.
+  final Capabilities capabilities;
+
+  static CapabilitiesEventBody fromJson(Map<String, Object?> obj) =>
+      CapabilitiesEventBody.fromMap(obj);
+
+  CapabilitiesEventBody({
+    required this.capabilities,
+  });
+
+  CapabilitiesEventBody.fromMap(Map<String, Object?> obj)
+      : capabilities =
+            Capabilities.fromJson(obj['capabilities'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!Capabilities.canParse(obj['capabilities'])) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'capabilities': capabilities,
+      };
+}
+
+class CompletionsResponseBody {
+  /// The possible completions for .
+  final List<CompletionItem> targets;
+
+  static CompletionsResponseBody fromJson(Map<String, Object?> obj) =>
+      CompletionsResponseBody.fromMap(obj);
+
+  CompletionsResponseBody({
+    required this.targets,
+  });
+
+  CompletionsResponseBody.fromMap(Map<String, Object?> obj)
+      : targets = (obj['targets'] as List)
+            .map(
+                (item) => CompletionItem.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['targets'] is! List ||
+        (obj['targets'].any((item) => !CompletionItem.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'targets': targets,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class ConfigurationDoneResponseBody {
+  static ConfigurationDoneResponseBody fromJson(Map<String, Object?> obj) =>
+      ConfigurationDoneResponseBody.fromMap(obj);
+
+  ConfigurationDoneResponseBody();
+
+  ConfigurationDoneResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class ContinueResponseBody {
+  /// If true, the 'continue' request has ignored the specified thread and
+  /// continued all threads instead.
+  /// If this attribute is missing a value of 'true' is assumed for backward
+  /// compatibility.
+  final bool? allThreadsContinued;
+
+  static ContinueResponseBody fromJson(Map<String, Object?> obj) =>
+      ContinueResponseBody.fromMap(obj);
+
+  ContinueResponseBody({
+    this.allThreadsContinued,
+  });
+
+  ContinueResponseBody.fromMap(Map<String, Object?> obj)
+      : allThreadsContinued = obj['allThreadsContinued'] as bool?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['allThreadsContinued'] is! bool?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (allThreadsContinued != null)
+          'allThreadsContinued': allThreadsContinued,
+      };
+}
+
+class ContinuedEventBody extends EventBody {
+  /// If 'allThreadsContinued' is true, a debug adapter can announce that all
+  /// threads have continued.
+  final bool? allThreadsContinued;
+
+  /// The thread which was continued.
+  final int threadId;
+
+  static ContinuedEventBody fromJson(Map<String, Object?> obj) =>
+      ContinuedEventBody.fromMap(obj);
+
+  ContinuedEventBody({
+    this.allThreadsContinued,
+    required this.threadId,
+  });
+
+  ContinuedEventBody.fromMap(Map<String, Object?> obj)
+      : allThreadsContinued = obj['allThreadsContinued'] as bool?,
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['allThreadsContinued'] is! bool?) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (allThreadsContinued != null)
+          'allThreadsContinued': allThreadsContinued,
+        'threadId': threadId,
+      };
+}
+
+class DataBreakpointInfoResponseBody {
+  /// Optional attribute listing the available access types for a potential data
+  /// breakpoint. A UI frontend could surface this information.
+  final List<DataBreakpointAccessType>? accessTypes;
+
+  /// Optional attribute indicating that a potential data breakpoint could be
+  /// persisted across sessions.
+  final bool? canPersist;
+
+  /// An identifier for the data on which a data breakpoint can be registered
+  /// with the setDataBreakpoints request or null if no data breakpoint is
+  /// available.
+  final Either2<String, Null> dataId;
+
+  /// UI string that describes on what data the breakpoint is set on or why a
+  /// data breakpoint is not available.
+  final String description;
+
+  static DataBreakpointInfoResponseBody fromJson(Map<String, Object?> obj) =>
+      DataBreakpointInfoResponseBody.fromMap(obj);
+
+  DataBreakpointInfoResponseBody({
+    this.accessTypes,
+    this.canPersist,
+    required this.dataId,
+    required this.description,
+  });
+
+  DataBreakpointInfoResponseBody.fromMap(Map<String, Object?> obj)
+      : accessTypes = (obj['accessTypes'] as List?)
+            ?.map((item) =>
+                DataBreakpointAccessType.fromJson(item as Map<String, Object?>))
+            .toList(),
+        canPersist = obj['canPersist'] as bool?,
+        dataId = obj['dataId'] is String
+            ? Either2<String, Null>.t1(obj['dataId'] as String)
+            : Either2<String, Null>.t2(obj['dataId'] as Null),
+        description = obj['description'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['accessTypes'] is! List ||
+        (obj['accessTypes']
+            .any((item) => !DataBreakpointAccessType.canParse(item))))) {
+      return false;
+    }
+    if (obj['canPersist'] is! bool?) {
+      return false;
+    }
+    if ((obj['dataId'] is! String && obj['dataId'] != null)) {
+      return false;
+    }
+    if (obj['description'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (accessTypes != null) 'accessTypes': accessTypes,
+        if (canPersist != null) 'canPersist': canPersist,
+        'dataId': dataId,
+        'description': description,
+      };
+}
+
+class DisassembleResponseBody {
+  /// The list of disassembled instructions.
+  final List<DisassembledInstruction> instructions;
+
+  static DisassembleResponseBody fromJson(Map<String, Object?> obj) =>
+      DisassembleResponseBody.fromMap(obj);
+
+  DisassembleResponseBody({
+    required this.instructions,
+  });
+
+  DisassembleResponseBody.fromMap(Map<String, Object?> obj)
+      : instructions = (obj['instructions'] as List)
+            .map((item) =>
+                DisassembledInstruction.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['instructions'] is! List ||
+        (obj['instructions']
+            .any((item) => !DisassembledInstruction.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'instructions': instructions,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class DisconnectResponseBody {
+  static DisconnectResponseBody fromJson(Map<String, Object?> obj) =>
+      DisconnectResponseBody.fromMap(obj);
+
+  DisconnectResponseBody();
+
+  DisconnectResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class ErrorResponseBody {
+  /// An optional, structured error message.
+  final Message? error;
+
+  static ErrorResponseBody fromJson(Map<String, Object?> obj) =>
+      ErrorResponseBody.fromMap(obj);
+
+  ErrorResponseBody({
+    this.error,
+  });
+
+  ErrorResponseBody.fromMap(Map<String, Object?> obj)
+      : error = obj['error'] == null
+            ? null
+            : Message.fromJson(obj['error'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!Message?.canParse(obj['error'])) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (error != null) 'error': error,
+      };
+}
+
+class EvaluateResponseBody {
+  /// The number of indexed child variables.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? indexedVariables;
+
+  /// Optional memory reference to a location appropriate for this result.
+  /// For pointer type eval results, this is generally a reference to the memory
+  /// address contained in the pointer.
+  /// This attribute should be returned by a debug adapter if the client has
+  /// passed the value true for the 'supportsMemoryReferences' capability of the
+  /// 'initialize' request.
+  final String? memoryReference;
+
+  /// The number of named child variables.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? namedVariables;
+
+  /// Properties of a evaluate result that can be used to determine how to
+  /// render the result in the UI.
+  final VariablePresentationHint? presentationHint;
+
+  /// The result of the evaluate request.
+  final String result;
+
+  /// The optional type of the evaluate result.
+  /// This attribute should only be returned by a debug adapter if the client
+  /// has passed the value true for the 'supportsVariableType' capability of the
+  /// 'initialize' request.
+  final String? type;
+
+  /// If variablesReference is > 0, the evaluate result is structured and its
+  /// children can be retrieved by passing variablesReference to the
+  /// VariablesRequest.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int variablesReference;
+
+  static EvaluateResponseBody fromJson(Map<String, Object?> obj) =>
+      EvaluateResponseBody.fromMap(obj);
+
+  EvaluateResponseBody({
+    this.indexedVariables,
+    this.memoryReference,
+    this.namedVariables,
+    this.presentationHint,
+    required this.result,
+    this.type,
+    required this.variablesReference,
+  });
+
+  EvaluateResponseBody.fromMap(Map<String, Object?> obj)
+      : indexedVariables = obj['indexedVariables'] as int?,
+        memoryReference = obj['memoryReference'] as String?,
+        namedVariables = obj['namedVariables'] as int?,
+        presentationHint = obj['presentationHint'] == null
+            ? null
+            : VariablePresentationHint.fromJson(
+                obj['presentationHint'] as Map<String, Object?>),
+        result = obj['result'] as String,
+        type = obj['type'] as String?,
+        variablesReference = obj['variablesReference'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['indexedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['memoryReference'] is! String?) {
+      return false;
+    }
+    if (obj['namedVariables'] is! int?) {
+      return false;
+    }
+    if (!VariablePresentationHint?.canParse(obj['presentationHint'])) {
+      return false;
+    }
+    if (obj['result'] is! String) {
+      return false;
+    }
+    if (obj['type'] is! String?) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (indexedVariables != null) 'indexedVariables': indexedVariables,
+        if (memoryReference != null) 'memoryReference': memoryReference,
+        if (namedVariables != null) 'namedVariables': namedVariables,
+        if (presentationHint != null) 'presentationHint': presentationHint,
+        'result': result,
+        if (type != null) 'type': type,
+        'variablesReference': variablesReference,
+      };
+}
+
+class ExceptionInfoResponseBody {
+  /// Mode that caused the exception notification to be raised.
+  final ExceptionBreakMode breakMode;
+
+  /// Descriptive text for the exception provided by the debug adapter.
+  final String? description;
+
+  /// Detailed information about the exception.
+  final ExceptionDetails? details;
+
+  /// ID of the exception that was thrown.
+  final String exceptionId;
+
+  static ExceptionInfoResponseBody fromJson(Map<String, Object?> obj) =>
+      ExceptionInfoResponseBody.fromMap(obj);
+
+  ExceptionInfoResponseBody({
+    required this.breakMode,
+    this.description,
+    this.details,
+    required this.exceptionId,
+  });
+
+  ExceptionInfoResponseBody.fromMap(Map<String, Object?> obj)
+      : breakMode = ExceptionBreakMode.fromJson(
+            obj['breakMode'] as Map<String, Object?>),
+        description = obj['description'] as String?,
+        details = obj['details'] == null
+            ? null
+            : ExceptionDetails.fromJson(obj['details'] as Map<String, Object?>),
+        exceptionId = obj['exceptionId'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!ExceptionBreakMode.canParse(obj['breakMode'])) {
+      return false;
+    }
+    if (obj['description'] is! String?) {
+      return false;
+    }
+    if (!ExceptionDetails?.canParse(obj['details'])) {
+      return false;
+    }
+    if (obj['exceptionId'] is! String) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakMode': breakMode,
+        if (description != null) 'description': description,
+        if (details != null) 'details': details,
+        'exceptionId': exceptionId,
+      };
+}
+
+class ExitedEventBody extends EventBody {
+  /// The exit code returned from the debuggee.
+  final int exitCode;
+
+  static ExitedEventBody fromJson(Map<String, Object?> obj) =>
+      ExitedEventBody.fromMap(obj);
+
+  ExitedEventBody({
+    required this.exitCode,
+  });
+
+  ExitedEventBody.fromMap(Map<String, Object?> obj)
+      : exitCode = obj['exitCode'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['exitCode'] is! int) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'exitCode': exitCode,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class GotoResponseBody {
+  static GotoResponseBody fromJson(Map<String, Object?> obj) =>
+      GotoResponseBody.fromMap(obj);
+
+  GotoResponseBody();
+
+  GotoResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class GotoTargetsResponseBody {
+  /// The possible goto targets of the specified location.
+  final List<GotoTarget> targets;
+
+  static GotoTargetsResponseBody fromJson(Map<String, Object?> obj) =>
+      GotoTargetsResponseBody.fromMap(obj);
+
+  GotoTargetsResponseBody({
+    required this.targets,
+  });
+
+  GotoTargetsResponseBody.fromMap(Map<String, Object?> obj)
+      : targets = (obj['targets'] as List)
+            .map((item) => GotoTarget.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['targets'] is! List ||
+        (obj['targets'].any((item) => !GotoTarget.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'targets': targets,
+      };
+}
+
+/// The capabilities of this debug adapter.
+class InitializeResponseBody {
+  static InitializeResponseBody fromJson(Map<String, Object?> obj) =>
+      InitializeResponseBody.fromMap(obj);
+
+  InitializeResponseBody();
+
+  InitializeResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Event-specific information.
+class InitializedEventBody extends EventBody {
+  static InitializedEventBody fromJson(Map<String, Object?> obj) =>
+      InitializedEventBody.fromMap(obj);
+
+  InitializedEventBody();
+
+  InitializedEventBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class InvalidatedEventBody extends EventBody {
+  /// Optional set of logical areas that got invalidated. This property has a
+  /// hint characteristic: a client can only be expected to make a 'best effort'
+  /// in honouring the areas but there are no guarantees. If this property is
+  /// missing, empty, or if values are not understand the client should assume a
+  /// single value 'all'.
+  final List<InvalidatedAreas>? areas;
+
+  /// If specified, the client only needs to refetch data related to this stack
+  /// frame (and the 'threadId' is ignored).
+  final int? stackFrameId;
+
+  /// If specified, the client only needs to refetch data related to this
+  /// thread.
+  final int? threadId;
+
+  static InvalidatedEventBody fromJson(Map<String, Object?> obj) =>
+      InvalidatedEventBody.fromMap(obj);
+
+  InvalidatedEventBody({
+    this.areas,
+    this.stackFrameId,
+    this.threadId,
+  });
+
+  InvalidatedEventBody.fromMap(Map<String, Object?> obj)
+      : areas = (obj['areas'] as List?)
+            ?.map((item) =>
+                InvalidatedAreas.fromJson(item as Map<String, Object?>))
+            .toList(),
+        stackFrameId = obj['stackFrameId'] as int?,
+        threadId = obj['threadId'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['areas'] is! List ||
+        (obj['areas'].any((item) => !InvalidatedAreas.canParse(item))))) {
+      return false;
+    }
+    if (obj['stackFrameId'] is! int?) {
+      return false;
+    }
+    if (obj['threadId'] is! int?) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (areas != null) 'areas': areas,
+        if (stackFrameId != null) 'stackFrameId': stackFrameId,
+        if (threadId != null) 'threadId': threadId,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class LaunchResponseBody {
+  static LaunchResponseBody fromJson(Map<String, Object?> obj) =>
+      LaunchResponseBody.fromMap(obj);
+
+  LaunchResponseBody();
+
+  LaunchResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class LoadedSourceEventBody extends EventBody {
+  /// The reason for the event.
+  final String reason;
+
+  /// The new, changed, or removed source.
+  final Source source;
+
+  static LoadedSourceEventBody fromJson(Map<String, Object?> obj) =>
+      LoadedSourceEventBody.fromMap(obj);
+
+  LoadedSourceEventBody({
+    required this.reason,
+    required this.source,
+  });
+
+  LoadedSourceEventBody.fromMap(Map<String, Object?> obj)
+      : reason = obj['reason'] as String,
+        source = Source.fromJson(obj['source'] as Map<String, Object?>);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['reason'] is! String) {
+      return false;
+    }
+    if (!Source.canParse(obj['source'])) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'reason': reason,
+        'source': source,
+      };
+}
+
+class LoadedSourcesResponseBody {
+  /// Set of loaded sources.
+  final List<Source> sources;
+
+  static LoadedSourcesResponseBody fromJson(Map<String, Object?> obj) =>
+      LoadedSourcesResponseBody.fromMap(obj);
+
+  LoadedSourcesResponseBody({
+    required this.sources,
+  });
+
+  LoadedSourcesResponseBody.fromMap(Map<String, Object?> obj)
+      : sources = (obj['sources'] as List)
+            .map((item) => Source.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['sources'] is! List ||
+        (obj['sources'].any((item) => !Source.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'sources': sources,
+      };
+}
+
+class ModuleEventBody extends EventBody {
+  /// The new, changed, or removed module. In case of 'removed' only the module
+  /// id is used.
+  final Module module;
+
+  /// The reason for the event.
+  final String reason;
+
+  static ModuleEventBody fromJson(Map<String, Object?> obj) =>
+      ModuleEventBody.fromMap(obj);
+
+  ModuleEventBody({
+    required this.module,
+    required this.reason,
+  });
+
+  ModuleEventBody.fromMap(Map<String, Object?> obj)
+      : module = Module.fromJson(obj['module'] as Map<String, Object?>),
+        reason = obj['reason'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (!Module.canParse(obj['module'])) {
+      return false;
+    }
+    if (obj['reason'] is! String) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'module': module,
+        'reason': reason,
+      };
+}
+
+class ModulesResponseBody {
+  /// All modules or range of modules.
+  final List<Module> modules;
+
+  /// The total number of modules available.
+  final int? totalModules;
+
+  static ModulesResponseBody fromJson(Map<String, Object?> obj) =>
+      ModulesResponseBody.fromMap(obj);
+
+  ModulesResponseBody({
+    required this.modules,
+    this.totalModules,
+  });
+
+  ModulesResponseBody.fromMap(Map<String, Object?> obj)
+      : modules = (obj['modules'] as List)
+            .map((item) => Module.fromJson(item as Map<String, Object?>))
+            .toList(),
+        totalModules = obj['totalModules'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['modules'] is! List ||
+        (obj['modules'].any((item) => !Module.canParse(item))))) {
+      return false;
+    }
+    if (obj['totalModules'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'modules': modules,
+        if (totalModules != null) 'totalModules': totalModules,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class NextResponseBody {
+  static NextResponseBody fromJson(Map<String, Object?> obj) =>
+      NextResponseBody.fromMap(obj);
+
+  NextResponseBody();
+
+  NextResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class OutputEventBody extends EventBody {
+  /// The output category. If not specified, 'console' is assumed.
+  final String? category;
+
+  /// An optional source location column where the output was produced.
+  final int? column;
+
+  /// Optional data to report. For the 'telemetry' category the data will be
+  /// sent to telemetry, for the other categories the data is shown in JSON
+  /// format.
+  final Object? data;
+
+  /// Support for keeping an output log organized by grouping related messages.
+  final String? group;
+
+  /// An optional source location line where the output was produced.
+  final int? line;
+
+  /// The output to report.
+  final String output;
+
+  /// An optional source location where the output was produced.
+  final Source? source;
+
+  /// If an attribute 'variablesReference' exists and its value is > 0, the
+  /// output contains objects which can be retrieved by passing
+  /// 'variablesReference' to the 'variables' request. The value should be less
+  /// than or equal to 2147483647 (2^31-1).
+  final int? variablesReference;
+
+  static OutputEventBody fromJson(Map<String, Object?> obj) =>
+      OutputEventBody.fromMap(obj);
+
+  OutputEventBody({
+    this.category,
+    this.column,
+    this.data,
+    this.group,
+    this.line,
+    required this.output,
+    this.source,
+    this.variablesReference,
+  });
+
+  OutputEventBody.fromMap(Map<String, Object?> obj)
+      : category = obj['category'] as String?,
+        column = obj['column'] as int?,
+        data = obj['data'],
+        group = obj['group'] as String?,
+        line = obj['line'] as int?,
+        output = obj['output'] as String,
+        source = obj['source'] == null
+            ? null
+            : Source.fromJson(obj['source'] as Map<String, Object?>),
+        variablesReference = obj['variablesReference'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['category'] is! String?) {
+      return false;
+    }
+    if (obj['column'] is! int?) {
+      return false;
+    }
+    if (obj['group'] is! String?) {
+      return false;
+    }
+    if (obj['line'] is! int?) {
+      return false;
+    }
+    if (obj['output'] is! String) {
+      return false;
+    }
+    if (!Source?.canParse(obj['source'])) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int?) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (category != null) 'category': category,
+        if (column != null) 'column': column,
+        if (data != null) 'data': data,
+        if (group != null) 'group': group,
+        if (line != null) 'line': line,
+        'output': output,
+        if (source != null) 'source': source,
+        if (variablesReference != null)
+          'variablesReference': variablesReference,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class PauseResponseBody {
+  static PauseResponseBody fromJson(Map<String, Object?> obj) =>
+      PauseResponseBody.fromMap(obj);
+
+  PauseResponseBody();
+
+  PauseResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class ProcessEventBody extends EventBody {
+  /// If true, the process is running on the same computer as the debug adapter.
+  final bool? isLocalProcess;
+
+  /// The logical name of the process. This is usually the full path to
+  /// process's executable file. Example: /home/example/myproj/program.js.
+  final String name;
+
+  /// The size of a pointer or address for this process, in bits. This value may
+  /// be used by clients when formatting addresses for display.
+  final int? pointerSize;
+
+  /// Describes how the debug engine started debugging this process.
+  final String? startMethod;
+
+  /// The system process id of the debugged process. This property will be
+  /// missing for non-system processes.
+  final int? systemProcessId;
+
+  static ProcessEventBody fromJson(Map<String, Object?> obj) =>
+      ProcessEventBody.fromMap(obj);
+
+  ProcessEventBody({
+    this.isLocalProcess,
+    required this.name,
+    this.pointerSize,
+    this.startMethod,
+    this.systemProcessId,
+  });
+
+  ProcessEventBody.fromMap(Map<String, Object?> obj)
+      : isLocalProcess = obj['isLocalProcess'] as bool?,
+        name = obj['name'] as String,
+        pointerSize = obj['pointerSize'] as int?,
+        startMethod = obj['startMethod'] as String?,
+        systemProcessId = obj['systemProcessId'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['isLocalProcess'] is! bool?) {
+      return false;
+    }
+    if (obj['name'] is! String) {
+      return false;
+    }
+    if (obj['pointerSize'] is! int?) {
+      return false;
+    }
+    if (obj['startMethod'] is! String?) {
+      return false;
+    }
+    if (obj['systemProcessId'] is! int?) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (isLocalProcess != null) 'isLocalProcess': isLocalProcess,
+        'name': name,
+        if (pointerSize != null) 'pointerSize': pointerSize,
+        if (startMethod != null) 'startMethod': startMethod,
+        if (systemProcessId != null) 'systemProcessId': systemProcessId,
+      };
+}
+
+class ProgressEndEventBody extends EventBody {
+  /// Optional, more detailed progress message. If omitted, the previous message
+  /// (if any) is used.
+  final String? message;
+
+  /// The ID that was introduced in the initial 'ProgressStartEvent'.
+  final String progressId;
+
+  static ProgressEndEventBody fromJson(Map<String, Object?> obj) =>
+      ProgressEndEventBody.fromMap(obj);
+
+  ProgressEndEventBody({
+    this.message,
+    required this.progressId,
+  });
+
+  ProgressEndEventBody.fromMap(Map<String, Object?> obj)
+      : message = obj['message'] as String?,
+        progressId = obj['progressId'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['message'] is! String?) {
+      return false;
+    }
+    if (obj['progressId'] is! String) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (message != null) 'message': message,
+        'progressId': progressId,
+      };
+}
+
+class ProgressStartEventBody extends EventBody {
+  /// If true, the request that reports progress may be canceled with a 'cancel'
+  /// request.
+  /// So this property basically controls whether the client should use UX that
+  /// supports cancellation.
+  /// Clients that don't support cancellation are allowed to ignore the setting.
+  final bool? cancellable;
+
+  /// Optional, more detailed progress message.
+  final String? message;
+
+  /// Optional progress percentage to display (value range: 0 to 100). If
+  /// omitted no percentage will be shown.
+  final num? percentage;
+
+  /// An ID that must be used in subsequent 'progressUpdate' and 'progressEnd'
+  /// events to make them refer to the same progress reporting.
+  /// IDs must be unique within a debug session.
+  final String progressId;
+
+  /// The request ID that this progress report is related to. If specified a
+  /// debug adapter is expected to emit
+  /// progress events for the long running request until the request has been
+  /// either completed or cancelled.
+  /// If the request ID is omitted, the progress report is assumed to be related
+  /// to some general activity of the debug adapter.
+  final int? requestId;
+
+  /// Mandatory (short) title of the progress reporting. Shown in the UI to
+  /// describe the long running operation.
+  final String title;
+
+  static ProgressStartEventBody fromJson(Map<String, Object?> obj) =>
+      ProgressStartEventBody.fromMap(obj);
+
+  ProgressStartEventBody({
+    this.cancellable,
+    this.message,
+    this.percentage,
+    required this.progressId,
+    this.requestId,
+    required this.title,
+  });
+
+  ProgressStartEventBody.fromMap(Map<String, Object?> obj)
+      : cancellable = obj['cancellable'] as bool?,
+        message = obj['message'] as String?,
+        percentage = obj['percentage'] as num?,
+        progressId = obj['progressId'] as String,
+        requestId = obj['requestId'] as int?,
+        title = obj['title'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['cancellable'] is! bool?) {
+      return false;
+    }
+    if (obj['message'] is! String?) {
+      return false;
+    }
+    if (obj['percentage'] is! num?) {
+      return false;
+    }
+    if (obj['progressId'] is! String) {
+      return false;
+    }
+    if (obj['requestId'] is! int?) {
+      return false;
+    }
+    if (obj['title'] is! String) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (cancellable != null) 'cancellable': cancellable,
+        if (message != null) 'message': message,
+        if (percentage != null) 'percentage': percentage,
+        'progressId': progressId,
+        if (requestId != null) 'requestId': requestId,
+        'title': title,
+      };
+}
+
+class ProgressUpdateEventBody extends EventBody {
+  /// Optional, more detailed progress message. If omitted, the previous message
+  /// (if any) is used.
+  final String? message;
+
+  /// Optional progress percentage to display (value range: 0 to 100). If
+  /// omitted no percentage will be shown.
+  final num? percentage;
+
+  /// The ID that was introduced in the initial 'progressStart' event.
+  final String progressId;
+
+  static ProgressUpdateEventBody fromJson(Map<String, Object?> obj) =>
+      ProgressUpdateEventBody.fromMap(obj);
+
+  ProgressUpdateEventBody({
+    this.message,
+    this.percentage,
+    required this.progressId,
+  });
+
+  ProgressUpdateEventBody.fromMap(Map<String, Object?> obj)
+      : message = obj['message'] as String?,
+        percentage = obj['percentage'] as num?,
+        progressId = obj['progressId'] as String;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['message'] is! String?) {
+      return false;
+    }
+    if (obj['percentage'] is! num?) {
+      return false;
+    }
+    if (obj['progressId'] is! String) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (message != null) 'message': message,
+        if (percentage != null) 'percentage': percentage,
+        'progressId': progressId,
+      };
+}
+
+class ReadMemoryResponseBody {
+  /// The address of the first byte of data returned.
+  /// Treated as a hex value if prefixed with '0x', or as a decimal value
+  /// otherwise.
+  final String address;
+
+  /// The bytes read from memory, encoded using base64.
+  final String? data;
+
+  /// The number of unreadable bytes encountered after the last successfully
+  /// read byte.
+  /// This can be used to determine the number of bytes that must be skipped
+  /// before a subsequent 'readMemory' request will succeed.
+  final int? unreadableBytes;
+
+  static ReadMemoryResponseBody fromJson(Map<String, Object?> obj) =>
+      ReadMemoryResponseBody.fromMap(obj);
+
+  ReadMemoryResponseBody({
+    required this.address,
+    this.data,
+    this.unreadableBytes,
+  });
+
+  ReadMemoryResponseBody.fromMap(Map<String, Object?> obj)
+      : address = obj['address'] as String,
+        data = obj['data'] as String?,
+        unreadableBytes = obj['unreadableBytes'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['address'] is! String) {
+      return false;
+    }
+    if (obj['data'] is! String?) {
+      return false;
+    }
+    if (obj['unreadableBytes'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'address': address,
+        if (data != null) 'data': data,
+        if (unreadableBytes != null) 'unreadableBytes': unreadableBytes,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class RestartFrameResponseBody {
+  static RestartFrameResponseBody fromJson(Map<String, Object?> obj) =>
+      RestartFrameResponseBody.fromMap(obj);
+
+  RestartFrameResponseBody();
+
+  RestartFrameResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class RestartResponseBody {
+  static RestartResponseBody fromJson(Map<String, Object?> obj) =>
+      RestartResponseBody.fromMap(obj);
+
+  RestartResponseBody();
+
+  RestartResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class ReverseContinueResponseBody {
+  static ReverseContinueResponseBody fromJson(Map<String, Object?> obj) =>
+      ReverseContinueResponseBody.fromMap(obj);
+
+  ReverseContinueResponseBody();
+
+  ReverseContinueResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class RunInTerminalResponseBody {
+  /// The process ID. The value should be less than or equal to 2147483647
+  /// (2^31-1).
+  final int? processId;
+
+  /// The process ID of the terminal shell. The value should be less than or
+  /// equal to 2147483647 (2^31-1).
+  final int? shellProcessId;
+
+  static RunInTerminalResponseBody fromJson(Map<String, Object?> obj) =>
+      RunInTerminalResponseBody.fromMap(obj);
+
+  RunInTerminalResponseBody({
+    this.processId,
+    this.shellProcessId,
+  });
+
+  RunInTerminalResponseBody.fromMap(Map<String, Object?> obj)
+      : processId = obj['processId'] as int?,
+        shellProcessId = obj['shellProcessId'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['processId'] is! int?) {
+      return false;
+    }
+    if (obj['shellProcessId'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (processId != null) 'processId': processId,
+        if (shellProcessId != null) 'shellProcessId': shellProcessId,
+      };
+}
+
+class ScopesResponseBody {
+  /// The scopes of the stackframe. If the array has length zero, there are no
+  /// scopes available.
+  final List<Scope> scopes;
+
+  static ScopesResponseBody fromJson(Map<String, Object?> obj) =>
+      ScopesResponseBody.fromMap(obj);
+
+  ScopesResponseBody({
+    required this.scopes,
+  });
+
+  ScopesResponseBody.fromMap(Map<String, Object?> obj)
+      : scopes = (obj['scopes'] as List)
+            .map((item) => Scope.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['scopes'] is! List ||
+        (obj['scopes'].any((item) => !Scope.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'scopes': scopes,
+      };
+}
+
+class SetBreakpointsResponseBody {
+  /// Information about the breakpoints.
+  /// The array elements are in the same order as the elements of the
+  /// 'breakpoints' (or the deprecated 'lines') array in the arguments.
+  final List<Breakpoint> breakpoints;
+
+  static SetBreakpointsResponseBody fromJson(Map<String, Object?> obj) =>
+      SetBreakpointsResponseBody.fromMap(obj);
+
+  SetBreakpointsResponseBody({
+    required this.breakpoints,
+  });
+
+  SetBreakpointsResponseBody.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) => Breakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !Breakpoint.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+class SetDataBreakpointsResponseBody {
+  /// Information about the data breakpoints. The array elements correspond to
+  /// the elements of the input argument 'breakpoints' array.
+  final List<Breakpoint> breakpoints;
+
+  static SetDataBreakpointsResponseBody fromJson(Map<String, Object?> obj) =>
+      SetDataBreakpointsResponseBody.fromMap(obj);
+
+  SetDataBreakpointsResponseBody({
+    required this.breakpoints,
+  });
+
+  SetDataBreakpointsResponseBody.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) => Breakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !Breakpoint.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+class SetExceptionBreakpointsResponseBody {
+  /// Information about the exception breakpoints or filters.
+  /// The breakpoints returned are in the same order as the elements of the
+  /// 'filters', 'filterOptions', 'exceptionOptions' arrays in the arguments. If
+  /// both 'filters' and 'filterOptions' are given, the returned array must
+  /// start with 'filters' information first, followed by 'filterOptions'
+  /// information.
+  final List<Breakpoint>? breakpoints;
+
+  static SetExceptionBreakpointsResponseBody fromJson(
+          Map<String, Object?> obj) =>
+      SetExceptionBreakpointsResponseBody.fromMap(obj);
+
+  SetExceptionBreakpointsResponseBody({
+    this.breakpoints,
+  });
+
+  SetExceptionBreakpointsResponseBody.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List?)
+            ?.map((item) => Breakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !Breakpoint.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (breakpoints != null) 'breakpoints': breakpoints,
+      };
+}
+
+class SetExpressionResponseBody {
+  /// The number of indexed child variables.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? indexedVariables;
+
+  /// The number of named child variables.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? namedVariables;
+
+  /// Properties of a value that can be used to determine how to render the
+  /// result in the UI.
+  final VariablePresentationHint? presentationHint;
+
+  /// The optional type of the value.
+  /// This attribute should only be returned by a debug adapter if the client
+  /// has passed the value true for the 'supportsVariableType' capability of the
+  /// 'initialize' request.
+  final String? type;
+
+  /// The new value of the expression.
+  final String value;
+
+  /// If variablesReference is > 0, the value is structured and its children can
+  /// be retrieved by passing variablesReference to the VariablesRequest.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? variablesReference;
+
+  static SetExpressionResponseBody fromJson(Map<String, Object?> obj) =>
+      SetExpressionResponseBody.fromMap(obj);
+
+  SetExpressionResponseBody({
+    this.indexedVariables,
+    this.namedVariables,
+    this.presentationHint,
+    this.type,
+    required this.value,
+    this.variablesReference,
+  });
+
+  SetExpressionResponseBody.fromMap(Map<String, Object?> obj)
+      : indexedVariables = obj['indexedVariables'] as int?,
+        namedVariables = obj['namedVariables'] as int?,
+        presentationHint = obj['presentationHint'] == null
+            ? null
+            : VariablePresentationHint.fromJson(
+                obj['presentationHint'] as Map<String, Object?>),
+        type = obj['type'] as String?,
+        value = obj['value'] as String,
+        variablesReference = obj['variablesReference'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['indexedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['namedVariables'] is! int?) {
+      return false;
+    }
+    if (!VariablePresentationHint?.canParse(obj['presentationHint'])) {
+      return false;
+    }
+    if (obj['type'] is! String?) {
+      return false;
+    }
+    if (obj['value'] is! String) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (indexedVariables != null) 'indexedVariables': indexedVariables,
+        if (namedVariables != null) 'namedVariables': namedVariables,
+        if (presentationHint != null) 'presentationHint': presentationHint,
+        if (type != null) 'type': type,
+        'value': value,
+        if (variablesReference != null)
+          'variablesReference': variablesReference,
+      };
+}
+
+class SetFunctionBreakpointsResponseBody {
+  /// Information about the breakpoints. The array elements correspond to the
+  /// elements of the 'breakpoints' array.
+  final List<Breakpoint> breakpoints;
+
+  static SetFunctionBreakpointsResponseBody fromJson(
+          Map<String, Object?> obj) =>
+      SetFunctionBreakpointsResponseBody.fromMap(obj);
+
+  SetFunctionBreakpointsResponseBody({
+    required this.breakpoints,
+  });
+
+  SetFunctionBreakpointsResponseBody.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) => Breakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !Breakpoint.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+class SetInstructionBreakpointsResponseBody {
+  /// Information about the breakpoints. The array elements correspond to the
+  /// elements of the 'breakpoints' array.
+  final List<Breakpoint> breakpoints;
+
+  static SetInstructionBreakpointsResponseBody fromJson(
+          Map<String, Object?> obj) =>
+      SetInstructionBreakpointsResponseBody.fromMap(obj);
+
+  SetInstructionBreakpointsResponseBody({
+    required this.breakpoints,
+  });
+
+  SetInstructionBreakpointsResponseBody.fromMap(Map<String, Object?> obj)
+      : breakpoints = (obj['breakpoints'] as List)
+            .map((item) => Breakpoint.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['breakpoints'] is! List ||
+        (obj['breakpoints'].any((item) => !Breakpoint.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'breakpoints': breakpoints,
+      };
+}
+
+class SetVariableResponseBody {
+  /// The number of indexed child variables.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? indexedVariables;
+
+  /// The number of named child variables.
+  /// The client can use this optional information to present the variables in a
+  /// paged UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? namedVariables;
+
+  /// The type of the new value. Typically shown in the UI when hovering over
+  /// the value.
+  final String? type;
+
+  /// The new value of the variable.
+  final String value;
+
+  /// If variablesReference is > 0, the new value is structured and its children
+  /// can be retrieved by passing variablesReference to the VariablesRequest.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  final int? variablesReference;
+
+  static SetVariableResponseBody fromJson(Map<String, Object?> obj) =>
+      SetVariableResponseBody.fromMap(obj);
+
+  SetVariableResponseBody({
+    this.indexedVariables,
+    this.namedVariables,
+    this.type,
+    required this.value,
+    this.variablesReference,
+  });
+
+  SetVariableResponseBody.fromMap(Map<String, Object?> obj)
+      : indexedVariables = obj['indexedVariables'] as int?,
+        namedVariables = obj['namedVariables'] as int?,
+        type = obj['type'] as String?,
+        value = obj['value'] as String,
+        variablesReference = obj['variablesReference'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['indexedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['namedVariables'] is! int?) {
+      return false;
+    }
+    if (obj['type'] is! String?) {
+      return false;
+    }
+    if (obj['value'] is! String) {
+      return false;
+    }
+    if (obj['variablesReference'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        if (indexedVariables != null) 'indexedVariables': indexedVariables,
+        if (namedVariables != null) 'namedVariables': namedVariables,
+        if (type != null) 'type': type,
+        'value': value,
+        if (variablesReference != null)
+          'variablesReference': variablesReference,
+      };
+}
+
+class SourceResponseBody {
+  /// Content of the source reference.
+  final String content;
+
+  /// Optional content type (mime type) of the source.
+  final String? mimeType;
+
+  static SourceResponseBody fromJson(Map<String, Object?> obj) =>
+      SourceResponseBody.fromMap(obj);
+
+  SourceResponseBody({
+    required this.content,
+    this.mimeType,
+  });
+
+  SourceResponseBody.fromMap(Map<String, Object?> obj)
+      : content = obj['content'] as String,
+        mimeType = obj['mimeType'] as String?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['content'] is! String) {
+      return false;
+    }
+    if (obj['mimeType'] is! String?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'content': content,
+        if (mimeType != null) 'mimeType': mimeType,
+      };
+}
+
+class StackTraceResponseBody {
+  /// The frames of the stackframe. If the array has length zero, there are no
+  /// stackframes available.
+  /// This means that there is no location information available.
+  final List<StackFrame> stackFrames;
+
+  /// The total number of frames available in the stack. If omitted or if
+  /// totalFrames is larger than the available frames, a client is expected to
+  /// request frames until a request returns less frames than requested (which
+  /// indicates the end of the stack). Returning monotonically increasing
+  /// totalFrames values for subsequent requests can be used to enforce paging
+  /// in the client.
+  final int? totalFrames;
+
+  static StackTraceResponseBody fromJson(Map<String, Object?> obj) =>
+      StackTraceResponseBody.fromMap(obj);
+
+  StackTraceResponseBody({
+    required this.stackFrames,
+    this.totalFrames,
+  });
+
+  StackTraceResponseBody.fromMap(Map<String, Object?> obj)
+      : stackFrames = (obj['stackFrames'] as List)
+            .map((item) => StackFrame.fromJson(item as Map<String, Object?>))
+            .toList(),
+        totalFrames = obj['totalFrames'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['stackFrames'] is! List ||
+        (obj['stackFrames'].any((item) => !StackFrame.canParse(item))))) {
+      return false;
+    }
+    if (obj['totalFrames'] is! int?) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'stackFrames': stackFrames,
+        if (totalFrames != null) 'totalFrames': totalFrames,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class StepBackResponseBody {
+  static StepBackResponseBody fromJson(Map<String, Object?> obj) =>
+      StepBackResponseBody.fromMap(obj);
+
+  StepBackResponseBody();
+
+  StepBackResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class StepInResponseBody {
+  static StepInResponseBody fromJson(Map<String, Object?> obj) =>
+      StepInResponseBody.fromMap(obj);
+
+  StepInResponseBody();
+
+  StepInResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class StepInTargetsResponseBody {
+  /// The possible stepIn targets of the specified source location.
+  final List<StepInTarget> targets;
+
+  static StepInTargetsResponseBody fromJson(Map<String, Object?> obj) =>
+      StepInTargetsResponseBody.fromMap(obj);
+
+  StepInTargetsResponseBody({
+    required this.targets,
+  });
+
+  StepInTargetsResponseBody.fromMap(Map<String, Object?> obj)
+      : targets = (obj['targets'] as List)
+            .map((item) => StepInTarget.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['targets'] is! List ||
+        (obj['targets'].any((item) => !StepInTarget.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'targets': targets,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class StepOutResponseBody {
+  static StepOutResponseBody fromJson(Map<String, Object?> obj) =>
+      StepOutResponseBody.fromMap(obj);
+
+  StepOutResponseBody();
+
+  StepOutResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class StoppedEventBody extends EventBody {
+  /// If 'allThreadsStopped' is true, a debug adapter can announce that all
+  /// threads have stopped.
+  /// - The client should use this information to enable that all threads can be expanded to access their stacktraces.
+  /// - If the attribute is missing or false, only the thread with the given threadId can be expanded.
+  final bool? allThreadsStopped;
+
+  /// The full reason for the event, e.g. 'Paused on exception'. This string is
+  /// shown in the UI as is and must be translated.
+  final String? description;
+
+  /// Ids of the breakpoints that triggered the event. In most cases there will
+  /// be only a single breakpoint but here are some examples for multiple
+  /// breakpoints:
+  /// - Different types of breakpoints map to the same location.
+  /// - Multiple source breakpoints get collapsed to the same instruction by the compiler/runtime.
+  /// - Multiple function breakpoints with different function names map to the same location.
+  final List<int>? hitBreakpointIds;
+
+  /// A value of true hints to the frontend that this event should not change
+  /// the focus.
+  final bool? preserveFocusHint;
+
+  /// The reason for the event.
+  /// For backward compatibility this string is shown in the UI if the
+  /// 'description' attribute is missing (but it must not be translated).
+  final String reason;
+
+  /// Additional information. E.g. if reason is 'exception', text contains the
+  /// exception name. This string is shown in the UI.
+  final String? text;
+
+  /// The thread which was stopped.
+  final int? threadId;
+
+  static StoppedEventBody fromJson(Map<String, Object?> obj) =>
+      StoppedEventBody.fromMap(obj);
+
+  StoppedEventBody({
+    this.allThreadsStopped,
+    this.description,
+    this.hitBreakpointIds,
+    this.preserveFocusHint,
+    required this.reason,
+    this.text,
+    this.threadId,
+  });
+
+  StoppedEventBody.fromMap(Map<String, Object?> obj)
+      : allThreadsStopped = obj['allThreadsStopped'] as bool?,
+        description = obj['description'] as String?,
+        hitBreakpointIds = (obj['hitBreakpointIds'] as List?)
+            ?.map((item) => item as int)
+            .toList(),
+        preserveFocusHint = obj['preserveFocusHint'] as bool?,
+        reason = obj['reason'] as String,
+        text = obj['text'] as String?,
+        threadId = obj['threadId'] as int?;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['allThreadsStopped'] is! bool?) {
+      return false;
+    }
+    if (obj['description'] is! String?) {
+      return false;
+    }
+    if ((obj['hitBreakpointIds'] is! List ||
+        (obj['hitBreakpointIds'].any((item) => item is! int)))) {
+      return false;
+    }
+    if (obj['preserveFocusHint'] is! bool?) {
+      return false;
+    }
+    if (obj['reason'] is! String) {
+      return false;
+    }
+    if (obj['text'] is! String?) {
+      return false;
+    }
+    if (obj['threadId'] is! int?) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (allThreadsStopped != null) 'allThreadsStopped': allThreadsStopped,
+        if (description != null) 'description': description,
+        if (hitBreakpointIds != null) 'hitBreakpointIds': hitBreakpointIds,
+        if (preserveFocusHint != null) 'preserveFocusHint': preserveFocusHint,
+        'reason': reason,
+        if (text != null) 'text': text,
+        if (threadId != null) 'threadId': threadId,
+      };
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class TerminateResponseBody {
+  static TerminateResponseBody fromJson(Map<String, Object?> obj) =>
+      TerminateResponseBody.fromMap(obj);
+
+  TerminateResponseBody();
+
+  TerminateResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+/// Contains request result if success is true and optional error details if
+/// success is false.
+class TerminateThreadsResponseBody {
+  static TerminateThreadsResponseBody fromJson(Map<String, Object?> obj) =>
+      TerminateThreadsResponseBody.fromMap(obj);
+
+  TerminateThreadsResponseBody();
+
+  TerminateThreadsResponseBody.fromMap(Map<String, Object?> obj);
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {};
+}
+
+class TerminatedEventBody extends EventBody {
+  /// A debug adapter may set 'restart' to true (or to an arbitrary object) to
+  /// request that the front end restarts the session.
+  /// The value is not interpreted by the client and passed unmodified as an
+  /// attribute '__restart' to the 'launch' and 'attach' requests.
+  final Object? restart;
+
+  static TerminatedEventBody fromJson(Map<String, Object?> obj) =>
+      TerminatedEventBody.fromMap(obj);
+
+  TerminatedEventBody({
+    this.restart,
+  });
+
+  TerminatedEventBody.fromMap(Map<String, Object?> obj)
+      : restart = obj['restart'];
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        if (restart != null) 'restart': restart,
+      };
+}
+
+class ThreadEventBody extends EventBody {
+  /// The reason for the event.
+  final String reason;
+
+  /// The identifier of the thread.
+  final int threadId;
+
+  static ThreadEventBody fromJson(Map<String, Object?> obj) =>
+      ThreadEventBody.fromMap(obj);
+
+  ThreadEventBody({
+    required this.reason,
+    required this.threadId,
+  });
+
+  ThreadEventBody.fromMap(Map<String, Object?> obj)
+      : reason = obj['reason'] as String,
+        threadId = obj['threadId'] as int;
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if (obj['reason'] is! String) {
+      return false;
+    }
+    if (obj['threadId'] is! int) {
+      return false;
+    }
+    return EventBody.canParse(obj);
+  }
+
+  Map<String, Object?> toJson() => {
+        'reason': reason,
+        'threadId': threadId,
+      };
+}
+
+class ThreadsResponseBody {
+  /// All threads.
+  final List<Thread> threads;
+
+  static ThreadsResponseBody fromJson(Map<String, Object?> obj) =>
+      ThreadsResponseBody.fromMap(obj);
+
+  ThreadsResponseBody({
+    required this.threads,
+  });
+
+  ThreadsResponseBody.fromMap(Map<String, Object?> obj)
+      : threads = (obj['threads'] as List)
+            .map((item) => Thread.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['threads'] is! List ||
+        (obj['threads'].any((item) => !Thread.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'threads': threads,
+      };
+}
+
+class VariablesResponseBody {
+  /// All (or a range) of variables for the given variable reference.
+  final List<Variable> variables;
+
+  static VariablesResponseBody fromJson(Map<String, Object?> obj) =>
+      VariablesResponseBody.fromMap(obj);
+
+  VariablesResponseBody({
+    required this.variables,
+  });
+
+  VariablesResponseBody.fromMap(Map<String, Object?> obj)
+      : variables = (obj['variables'] as List)
+            .map((item) => Variable.fromJson(item as Map<String, Object?>))
+            .toList();
+
+  static bool canParse(Object? obj) {
+    if (obj is! Map<String, dynamic>) {
+      return false;
+    }
+    if ((obj['variables'] is! List ||
+        (obj['variables'].any((item) => !Variable.canParse(item))))) {
+      return false;
+    }
+    return true;
+  }
+
+  Map<String, Object?> toJson() => {
+        'variables': variables,
+      };
+}
+
+const eventTypes = {
+  BreakpointEventBody: 'breakpoint',
+  CapabilitiesEventBody: 'capabilities',
+  ContinuedEventBody: 'continued',
+  ExitedEventBody: 'exited',
+  InitializedEventBody: 'initialized',
+  InvalidatedEventBody: 'invalidated',
+  LoadedSourceEventBody: 'loadedSource',
+  ModuleEventBody: 'module',
+  OutputEventBody: 'output',
+  ProcessEventBody: 'process',
+  ProgressEndEventBody: 'progressEnd',
+  ProgressStartEventBody: 'progressStart',
+  ProgressUpdateEventBody: 'progressUpdate',
+  StoppedEventBody: 'stopped',
+  TerminatedEventBody: 'terminated',
+  ThreadEventBody: 'thread',
+};
+
+const commandTypes = {
+  AttachRequestArguments: 'attach',
+  BreakpointLocationsArguments: 'breakpointLocations',
+  CancelArguments: 'cancel',
+  CompletionsArguments: 'completions',
+  ConfigurationDoneArguments: 'configurationDone',
+  ContinueArguments: 'continue',
+  DataBreakpointInfoArguments: 'dataBreakpointInfo',
+  DisassembleArguments: 'disassemble',
+  DisconnectArguments: 'disconnect',
+  EvaluateArguments: 'evaluate',
+  ExceptionInfoArguments: 'exceptionInfo',
+  GotoArguments: 'goto',
+  GotoTargetsArguments: 'gotoTargets',
+  InitializeRequestArguments: 'initialize',
+  LaunchRequestArguments: 'launch',
+  LoadedSourcesArguments: 'loadedSources',
+  ModulesArguments: 'modules',
+  NextArguments: 'next',
+  PauseArguments: 'pause',
+  ReadMemoryArguments: 'readMemory',
+  RestartFrameArguments: 'restartFrame',
+  RestartArguments: 'restart',
+  ReverseContinueArguments: 'reverseContinue',
+  RunInTerminalRequestArguments: 'runInTerminal',
+  ScopesArguments: 'scopes',
+  SetBreakpointsArguments: 'setBreakpoints',
+  SetDataBreakpointsArguments: 'setDataBreakpoints',
+  SetExceptionBreakpointsArguments: 'setExceptionBreakpoints',
+  SetExpressionArguments: 'setExpression',
+  SetFunctionBreakpointsArguments: 'setFunctionBreakpoints',
+  SetInstructionBreakpointsArguments: 'setInstructionBreakpoints',
+  SetVariableArguments: 'setVariable',
+  SourceArguments: 'source',
+  StackTraceArguments: 'stackTrace',
+  StepBackArguments: 'stepBack',
+  StepInArguments: 'stepIn',
+  StepInTargetsArguments: 'stepInTargets',
+  StepOutArguments: 'stepOut',
+  TerminateArguments: 'terminate',
+  TerminateThreadsArguments: 'terminateThreads',
+  VariablesArguments: 'variables',
+};
diff --git a/dds/lib/src/dap/protocol_special.dart b/dds/lib/src/dap/protocol_special.dart
new file mode 100644
index 0000000..346673e
--- /dev/null
+++ b/dds/lib/src/dap/protocol_special.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(dantup): Consolidate this with the equiv file in analysis_server (most
+//  likely by having analysis_server reference this one).
+
+Object? specToJson(Object? obj) {
+  if (obj is ToJsonable) {
+    return obj.toJson();
+  } else {
+    return obj;
+  }
+}
+
+/// Represents either a [T1] or [T2].
+///
+/// This class is used for fields generated from the LSP/DAP specs that are
+/// defined as unions in TypeScript (for example `String | number`) that cannot
+/// directly be represented as Dart types.
+///
+/// Use the [map] function to access the element, providing a handler for each
+/// of the possible types.
+class Either2<T1, T2> extends ToJsonable {
+  final int _which;
+  final T1? _t1;
+  final T2? _t2;
+
+  Either2.t1(T1 this._t1)
+      : _t2 = null,
+        _which = 1;
+  Either2.t2(T2 this._t2)
+      : _t1 = null,
+        _which = 2;
+
+  T map<T>(T Function(T1) f1, T Function(T2) f2) {
+    return _which == 1 ? f1(_t1 as T1) : f2(_t2 as T2);
+  }
+
+  @override
+  Object? toJson() => map(specToJson, specToJson);
+
+  @override
+  String toString() => map((t) => t.toString(), (t) => t.toString());
+
+  /// Checks whether the value of the union equals the supplied value.
+  bool valueEquals(o) => map((t) => t == o, (t) => t == o);
+}
+
+/// An object from the LSP/DAP specs that can be converted to JSON.
+abstract class ToJsonable {
+  Object? toJson();
+}
diff --git a/dds/lib/src/dap/protocol_stream.dart b/dds/lib/src/dap/protocol_stream.dart
new file mode 100644
index 0000000..a362b93
--- /dev/null
+++ b/dds/lib/src/dap/protocol_stream.dart
@@ -0,0 +1,126 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'logging.dart';
+import 'protocol_generated.dart';
+import 'protocol_stream_transformers.dart';
+
+// TODO(dantup): This class should mostly be shareable with the LSP version,
+// but the ProtocolMessage, Request, Response, Event classes are different so
+// will need specializations.
+
+/// A wrapper over a Stream/StreamSink that encodes/decores DAP/LSP
+/// request/response/event messages.
+class ByteStreamServerChannel {
+  final Stream<List<int>> _input;
+
+  final StreamSink<List<int>> _output;
+
+  final Logger? _logger;
+
+  /// Completer that will be signalled when the input stream is closed.
+  final Completer _closed = Completer();
+
+  /// True if [close] has been called.
+  bool _closeRequested = false;
+
+  ByteStreamServerChannel(this._input, this._output, this._logger);
+
+  /// Future that will be completed when the input stream is closed.
+  Future get closed {
+    return _closed.future;
+  }
+
+  void close() {
+    if (!_closeRequested) {
+      _closeRequested = true;
+      assert(!_closed.isCompleted);
+      _output.close();
+      _closed.complete();
+    }
+  }
+
+  StreamSubscription<String> listen(
+      void Function(ProtocolMessage message) onMessage,
+      {Function? onError,
+      void Function()? onDone}) {
+    return _input.transform(PacketTransformer()).listen(
+      (String data) => _readMessage(data, onMessage),
+      onError: onError,
+      onDone: () {
+        close();
+        if (onDone != null) {
+          onDone();
+        }
+      },
+    );
+  }
+
+  void sendEvent(Event event) => _sendLsp(event.toJson());
+
+  void sendRequest(Request request) => _sendLsp(request.toJson());
+
+  void sendResponse(Response response) => _sendLsp(response.toJson());
+
+  /// Read a request from the given [data] and use the given function to handle
+  /// the message.
+  void _readMessage(String data, void Function(ProtocolMessage) onMessage) {
+    // Ignore any further requests after the communication channel is closed.
+    if (_closed.isCompleted) {
+      return;
+    }
+    _logger?.call('<== [DAP] $data');
+    try {
+      final Map<String, Object?> json = jsonDecode(data);
+      final type = json['type'] as String;
+      if (type == 'request') {
+        onMessage(Request.fromJson(json));
+      } else if (type == 'event') {
+        onMessage(Event.fromJson(json));
+      } else if (type == 'response') {
+        onMessage(Response.fromJson(json));
+      } else {
+        _sendParseError(data);
+      }
+    } catch (e) {
+      _sendParseError(data);
+    }
+  }
+
+  /// Sends a message prefixed with the required LSP headers.
+  void _sendLsp(Map<String, Object?> json) {
+    // Don't send any further responses after the communication channel is
+    // closed.
+    if (_closeRequested) {
+      return;
+    }
+    final jsonEncodedBody = jsonEncode(json);
+    final utf8EncodedBody = utf8.encode(jsonEncodedBody);
+    final header = 'Content-Length: ${utf8EncodedBody.length}\r\n'
+        'Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n';
+    final asciiEncodedHeader = ascii.encode(header);
+
+    // Header is always ascii, body is always utf8!
+    _write(asciiEncodedHeader);
+    _write(utf8EncodedBody);
+
+    _logger?.call('==> [DAP] $jsonEncodedBody');
+  }
+
+  void _sendParseError(String data) {
+    // TODO(dantup): Review LSP implementation of this when consolidating classes.
+    throw 'Message does not confirm to DAP spec: $data';
+  }
+
+  /// Send [bytes] to [_output].
+  void _write(List<int> bytes) {
+    runZonedGuarded(
+      () => _output.add(bytes),
+      (e, s) => close(),
+    );
+  }
+}
diff --git a/dds/lib/src/dap/protocol_stream_transformers.dart b/dds/lib/src/dap/protocol_stream_transformers.dart
new file mode 100644
index 0000000..6bd6caa
--- /dev/null
+++ b/dds/lib/src/dap/protocol_stream_transformers.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+
+class InvalidEncodingError {
+  final String headers;
+  InvalidEncodingError(this.headers);
+
+  @override
+  String toString() =>
+      'Encoding in supplied headers is not supported.\n\nHeaders:\n$headers';
+}
+
+/// Transforms a stream of LSP/DAP data in the form:
+///
+///     Content-Length: xxx\r\n
+///     Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
+///     \r\n
+///     { JSON payload }
+///
+/// into just the JSON payload, decoded with the specified encoding. Line endings
+/// for headers must be \r\n on all platforms as defined in the LSP spec.
+class PacketTransformer extends StreamTransformerBase<List<int>, String> {
+  @override
+  Stream<String> bind(Stream<List<int>> stream) {
+    late StreamSubscription<int> input;
+    late StreamController<String> _output;
+    final buffer = <int>[];
+    var isParsingHeaders = true;
+    ProtocolHeaders? headers;
+    _output = StreamController<String>(
+      onListen: () {
+        input = stream.expand((b) => b).listen(
+          (codeUnit) {
+            buffer.add(codeUnit);
+            if (isParsingHeaders && _endsWithCrLfCrLf(buffer)) {
+              headers = _parseHeaders(buffer);
+              buffer.clear();
+              isParsingHeaders = false;
+            } else if (!isParsingHeaders &&
+                buffer.length >= headers!.contentLength) {
+              // UTF-8 is the default - and only supported - encoding for LSP.
+              // The string 'utf8' is valid since it was published in the original spec.
+              // Any other encodings should be rejected with an error.
+              if ([null, 'utf-8', 'utf8']
+                  .contains(headers?.encoding?.toLowerCase())) {
+                _output.add(utf8.decode(buffer));
+              } else {
+                _output.addError(InvalidEncodingError(headers!.rawHeaders));
+              }
+              buffer.clear();
+              isParsingHeaders = true;
+            }
+          },
+          onError: _output.addError,
+          onDone: _output.close,
+        );
+      },
+      onPause: () => input.pause(),
+      onResume: () => input.resume(),
+      onCancel: () => input.cancel(),
+    );
+    return _output.stream;
+  }
+
+  /// Whether [buffer] ends in '\r\n\r\n'.
+  static bool _endsWithCrLfCrLf(List<int> buffer) {
+    var l = buffer.length;
+    return l > 4 &&
+        buffer[l - 1] == 10 &&
+        buffer[l - 2] == 13 &&
+        buffer[l - 3] == 10 &&
+        buffer[l - 4] == 13;
+  }
+
+  static String? _extractEncoding(String header) {
+    final charset = header
+        .split(';')
+        .map((s) => s.trim().toLowerCase())
+        .firstWhere((s) => s.startsWith('charset='), orElse: () => '');
+
+    return charset == '' ? null : charset.split('=')[1];
+  }
+
+  /// Decodes [buffer] into a String and returns the 'Content-Length' header value.
+  static ProtocolHeaders _parseHeaders(List<int> buffer) {
+    // Headers are specified as always ASCII in LSP.
+    final asString = ascii.decode(buffer);
+    final headers = asString.split('\r\n');
+    final lengthHeader =
+        headers.firstWhere((h) => h.startsWith('Content-Length'));
+    final length = lengthHeader.split(':').last.trim();
+    final contentTypeHeader = headers
+        .firstWhere((h) => h.startsWith('Content-Type'), orElse: () => '');
+    final encoding = _extractEncoding(contentTypeHeader);
+    return ProtocolHeaders(asString, int.parse(length), encoding);
+  }
+}
+
+class ProtocolHeaders {
+  final String rawHeaders;
+  final int contentLength;
+  final String? encoding;
+  ProtocolHeaders(this.rawHeaders, this.contentLength, this.encoding);
+}
+
+/// Transforms a stream of [Uint8List]s to [List<int>]s. Used because
+/// [ServerSocket] and [Socket] use [Uint8List] but stdin and stdout use
+/// [List<int>] and the LSP server needs to operate against both.
+class Uint8ListTransformer extends StreamTransformerBase<Uint8List, List<int>> {
+  // TODO(dantup): Is there a built-in (or better) way to do this?
+  @override
+  Stream<List<int>> bind(Stream<Uint8List> stream) {
+    late StreamSubscription<Uint8List> input;
+    late StreamController<List<int>> _output;
+    _output = StreamController<List<int>>(
+      onListen: () {
+        input = stream.listen(
+          (uints) => _output.add(uints),
+          onError: _output.addError,
+          onDone: _output.close,
+        );
+      },
+      onPause: () => input.pause(),
+      onResume: () => input.resume(),
+      onCancel: () => input.cancel(),
+    );
+    return _output.stream;
+  }
+}
diff --git a/dds/lib/src/dap/server.dart b/dds/lib/src/dap/server.dart
new file mode 100644
index 0000000..f110e4e
--- /dev/null
+++ b/dds/lib/src/dap/server.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:dds/src/dap/adapters/dart.dart';
+import 'package:pedantic/pedantic.dart';
+
+import 'adapters/dart_cli.dart';
+import 'logging.dart';
+import 'protocol_stream.dart';
+import 'protocol_stream_transformers.dart';
+
+/// A DAP server that binds to a port and runs in multi-session mode.
+class DapServer {
+  static const defaultPort = 9200;
+
+  final ServerSocket _socket;
+  final Logger? _logger;
+  final _channels = <ByteStreamServerChannel>{};
+  final _adapters = <DartDebugAdapter>{};
+
+  DapServer._(this._socket, this._logger) {
+    _socket.listen(_acceptConnection);
+  }
+
+  String get host => _socket.address.host;
+  int get port => _socket.port;
+
+  FutureOr<void> stop() async {
+    _channels.forEach((client) => client.close());
+    await _socket.close();
+  }
+
+  void _acceptConnection(Socket client) {
+    _logger?.call('Accepted connection from ${client.remoteAddress}');
+    client.done.then((_) {
+      _logger?.call('Connection from ${client.remoteAddress} closed');
+    });
+    _createAdapter(client.transform(Uint8ListTransformer()), client, _logger);
+  }
+
+  void _createAdapter(
+      Stream<List<int>> _input, StreamSink<List<int>> _output, Logger? logger) {
+    // TODO(dantup): This is hard-coded to DartCliDebugAdapter but will
+    //   ultimately need to support having a factory passed in to support
+    //   tests and/or being used in flutter_tools.
+    final channel = ByteStreamServerChannel(_input, _output, logger);
+    final adapter = DartCliDebugAdapter(channel);
+    _channels.add(channel);
+    _adapters.add(adapter);
+    unawaited(channel.closed.then((_) {
+      _channels.remove(channel);
+      _adapters.remove(adapter);
+    }));
+  }
+
+  /// Starts a DAP Server listening on [host]:[port].
+  static Future<DapServer> create({
+    String host = 'localhost',
+    int port = 0,
+    Logger? logger,
+  }) async {
+    final _socket = await ServerSocket.bind(host, port);
+    return DapServer._(_socket, logger);
+  }
+}
diff --git a/dds/lib/src/dds_impl.dart b/dds/lib/src/dds_impl.dart
index db355fb..c65ba5d 100644
--- a/dds/lib/src/dds_impl.dart
+++ b/dds/lib/src/dds_impl.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -24,6 +22,8 @@
 import 'binary_compatible_peer.dart';
 import 'client.dart';
 import 'client_manager.dart';
+import 'constants.dart';
+import 'devtools/devtools_handler.dart';
 import 'expression_evaluator.dart';
 import 'isolate_manager.dart';
 import 'stream_manager.dart';
@@ -51,7 +51,13 @@
 
 class DartDevelopmentServiceImpl implements DartDevelopmentService {
   DartDevelopmentServiceImpl(
-      this._remoteVmServiceUri, this._uri, this._authCodesEnabled, this._ipv6) {
+    this._remoteVmServiceUri,
+    this._uri,
+    this._authCodesEnabled,
+    this._ipv6,
+    this._devToolsConfiguration,
+    this.shouldLogRequests,
+  ) {
     _clientManager = ClientManager(this);
     _expressionEvaluator = ExpressionEvaluator(this);
     _isolateManager = IsolateManager(this);
@@ -113,20 +119,26 @@
         (_ipv6 ? InternetAddress.loopbackIPv6 : InternetAddress.loopbackIPv4)
             .host;
     final port = uri?.port ?? 0;
-
+    var pipeline = const Pipeline();
+    if (shouldLogRequests) {
+      pipeline = pipeline.addMiddleware(
+        logRequests(
+          logger: (String message, bool isError) {
+            print('Log: $message');
+          },
+        ),
+      );
+    }
+    pipeline = pipeline.addMiddleware(_authCodeMiddleware);
+    final handler = pipeline.addHandler(_handlers().handler);
     // Start the DDS server.
-    _server = await io.serve(
-        const Pipeline()
-            .addMiddleware(_authCodeMiddleware)
-            .addHandler(_handlers().handler),
-        host,
-        port);
+    _server = await io.serve(handler, host, port);
 
     final tmpUri = Uri(
       scheme: 'http',
       host: host,
       port: _server.port,
-      path: '$_authCode/',
+      path: '$authCode/',
     );
 
     // Notify the VM service that this client is DDS and that it should close
@@ -157,14 +169,14 @@
       return;
     }
     _shuttingDown = true;
-    // Don't accept anymore HTTP requests.
-    await _server?.close();
+    // Don't accept any more HTTP requests.
+    await _server.close();
 
     // Close connections to clients.
     await clientManager.shutdown();
 
     // Close connection to VM service.
-    await _vmServiceSocket?.sink?.close();
+    await _vmServiceSocket.sink.close();
 
     _done.complete();
   }
@@ -197,7 +209,7 @@
             return forbidden;
           }
           final authToken = pathSegments[0];
-          if (authToken != _authCode) {
+          if (authToken != authCode) {
             return forbidden;
           }
           // Creates a new request with the authentication code stripped from
@@ -233,18 +245,12 @@
       });
 
   Handler _sseHandler() {
-    // Give connections time to reestablish before considering them closed.
-    // Required to reestablish connections killed by UberProxy.
-    const keepAlive = Duration(seconds: 30);
-    final handler = authCodesEnabled
-        ? SseHandler(
-            Uri.parse('/$_authCode/$_kSseHandlerPath'),
-            keepAlive: keepAlive,
-          )
-        : SseHandler(
-            Uri.parse('/$_kSseHandlerPath'),
-            keepAlive: keepAlive,
-          );
+    final handler = SseHandler(
+      authCodesEnabled
+          ? Uri.parse('/$authCode/$_kSseHandlerPath')
+          : Uri.parse('/$_kSseHandlerPath'),
+      keepAlive: sseKeepAlive,
+    );
 
     handler.connections.rest.listen((sseConnection) {
       final client = DartDevelopmentServiceClient.fromSSEConnection(
@@ -259,10 +265,18 @@
   }
 
   Handler _httpHandler() {
-    // DDS doesn't support any HTTP requests itself, so we just forward all of
-    // them to the VM service.
-    final cascade = Cascade().add(proxyHandler(remoteVmServiceUri));
-    return cascade.handler;
+    if (_devToolsConfiguration != null && _devToolsConfiguration!.enable) {
+      // Install the DevTools handlers and forward any unhandled HTTP requests to
+      // the VM service.
+      final String buildDir =
+          _devToolsConfiguration!.customBuildDirectoryPath.toFilePath();
+      return devtoolsHandler(
+        dds: this,
+        buildDir: buildDir,
+        notFoundHandler: proxyHandler(remoteVmServiceUri),
+      ) as FutureOr<Response> Function(Request);
+    }
+    return proxyHandler(remoteVmServiceUri);
   }
 
   List<String> _cleanupPathSegments(Uri uri) {
@@ -278,7 +292,7 @@
     return pathSegments;
   }
 
-  Uri _toWebSocket(Uri uri) {
+  Uri? _toWebSocket(Uri? uri) {
     if (uri == null) {
       return null;
     }
@@ -287,7 +301,7 @@
     return uri.replace(scheme: 'ws', pathSegments: pathSegments);
   }
 
-  Uri _toSse(Uri uri) {
+  Uri? _toSse(Uri? uri) {
     if (uri == null) {
       return null;
     }
@@ -296,53 +310,87 @@
     return uri.replace(scheme: 'sse', pathSegments: pathSegments);
   }
 
-  String getNamespace(DartDevelopmentServiceClient client) =>
+  Uri? _toDevTools(Uri? uri) {
+    // The DevTools URI is a bit strange as the query parameters appear after
+    // the fragment. There's no nice way to encode the query parameters
+    // properly, so we create another Uri just to grab the formatted query.
+    // The result will need to have '/?' prepended when being used as the
+    // fragment to get the correct format.
+    final query = Uri(
+      queryParameters: {
+        'uri': wsUri.toString(),
+      },
+    ).query;
+    return Uri(
+      scheme: 'http',
+      host: uri!.host,
+      port: uri.port,
+      pathSegments: [
+        ...uri.pathSegments.where(
+          (e) => e.isNotEmpty,
+        ),
+        'devtools',
+        '',
+      ],
+      fragment: '/?$query',
+    );
+  }
+
+  String? getNamespace(DartDevelopmentServiceClient client) =>
       clientManager.clients.keyOf(client);
 
-  @override
   bool get authCodesEnabled => _authCodesEnabled;
   final bool _authCodesEnabled;
-  String _authCode;
+  String? get authCode => _authCode;
+  String? _authCode;
 
-  @override
+  final bool shouldLogRequests;
+
   Uri get remoteVmServiceUri => _remoteVmServiceUri;
 
   @override
-  Uri get remoteVmServiceWsUri => _toWebSocket(_remoteVmServiceUri);
+  Uri get remoteVmServiceWsUri => _toWebSocket(_remoteVmServiceUri)!;
   Uri _remoteVmServiceUri;
 
   @override
-  Uri get uri => _uri;
+  Uri? get uri => _uri;
+  Uri? _uri;
 
   @override
-  Uri get sseUri => _toSse(_uri);
+  Uri? get sseUri => _toSse(_uri);
 
-  Uri get wsUri => _toWebSocket(_uri);
-  Uri _uri;
+  @override
+  Uri? get wsUri => _toWebSocket(_uri);
+
+  @override
+  Uri? get devToolsUri =>
+      _devToolsConfiguration?.enable ?? false ? _toDevTools(_uri) : null;
 
   final bool _ipv6;
 
   bool get isRunning => _uri != null;
 
+  final DevToolsConfiguration? _devToolsConfiguration;
+
   Future<void> get done => _done.future;
   Completer _done = Completer<void>();
   bool _shuttingDown = false;
 
   ClientManager get clientManager => _clientManager;
-  ClientManager _clientManager;
+  late ClientManager _clientManager;
 
   ExpressionEvaluator get expressionEvaluator => _expressionEvaluator;
-  ExpressionEvaluator _expressionEvaluator;
+  late ExpressionEvaluator _expressionEvaluator;
 
   IsolateManager get isolateManager => _isolateManager;
-  IsolateManager _isolateManager;
+  late IsolateManager _isolateManager;
 
   StreamManager get streamManager => _streamManager;
-  StreamManager _streamManager;
+  late StreamManager _streamManager;
 
   static const _kSseHandlerPath = '\$debugHandler';
 
-  json_rpc.Peer vmServiceClient;
-  WebSocketChannel _vmServiceSocket;
-  HttpServer _server;
+  late json_rpc.Peer vmServiceClient;
+  late WebSocketChannel _vmServiceSocket;
+  late HttpServer _server;
 }
diff --git a/dds/lib/src/devtools/devtools_client.dart b/dds/lib/src/devtools/devtools_client.dart
new file mode 100644
index 0000000..8bc3564
--- /dev/null
+++ b/dds/lib/src/devtools/devtools_client.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:json_rpc_2/src/server.dart' as json_rpc;
+import 'package:sse/src/server/sse_handler.dart';
+import 'package:stream_channel/stream_channel.dart';
+
+import 'server_api.dart';
+
+class LoggingMiddlewareSink<S> implements StreamSink<S> {
+  LoggingMiddlewareSink(this.sink);
+
+  @override
+  void add(S event) {
+    print('DevTools SSE response: $event');
+    sink.add(event);
+  }
+
+  @override
+  void addError(Object error, [StackTrace? stackTrace]) {
+    print('DevTools SSE error response: $error');
+    sink.addError(error);
+  }
+
+  @override
+  Future addStream(Stream<S> stream) {
+    return sink.addStream(stream);
+  }
+
+  @override
+  Future close() => sink.close();
+
+  @override
+  Future get done => sink.done;
+
+  final StreamSink sink;
+}
+
+/// Represents a DevTools client connection to the DevTools server API.
+class DevToolsClient {
+  DevToolsClient.fromSSEConnection(
+    SseConnection sse,
+    bool loggingEnabled,
+  ) {
+    Stream<String> stream = sse.stream;
+    StreamSink sink = sse.sink;
+
+    if (loggingEnabled) {
+      stream = stream.map<String>((String e) {
+        print('DevTools SSE request: $e');
+        return e;
+      });
+      sink = LoggingMiddlewareSink(sink);
+    }
+
+    _server = json_rpc.Server(
+      StreamChannel(stream, sink as StreamSink<String>),
+      strictProtocolChecks: false,
+    );
+    _registerJsonRpcMethods();
+    _server.listen();
+  }
+
+  void _registerJsonRpcMethods() {
+    _server.registerMethod('connected', (parameters) {
+      // Nothing to do here.
+    });
+
+    _server.registerMethod('currentPage', (parameters) {
+      // Nothing to do here.
+    });
+
+    _server.registerMethod('disconnected', (parameters) {
+      // Nothing to do here.
+    });
+
+    _server.registerMethod('getPreferenceValue', (parameters) {
+      final key = parameters['key'].asString;
+      final value = ServerApi.devToolsPreferences.properties[key];
+      return value;
+    });
+
+    _server.registerMethod('setPreferenceValue', (parameters) {
+      final key = parameters['key'].asString;
+      final value = parameters['value'].value;
+      ServerApi.devToolsPreferences.properties[key] = value;
+    });
+  }
+
+  late json_rpc.Server _server;
+}
diff --git a/dds/lib/src/devtools/devtools_handler.dart b/dds/lib/src/devtools/devtools_handler.dart
new file mode 100644
index 0000000..87c4fa6
--- /dev/null
+++ b/dds/lib/src/devtools/devtools_handler.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:dds/src/constants.dart';
+import 'package:shelf/shelf.dart';
+import 'package:shelf_static/shelf_static.dart';
+import 'package:sse/server/sse_handler.dart';
+
+import '../dds_impl.dart';
+import 'devtools_client.dart';
+import 'server_api.dart';
+
+/// Returns a [Handler] which handles serving DevTools and the DevTools server
+/// API under $DDS_URI/devtools/.
+///
+/// [buildDir] is the path to the pre-compiled DevTools instance to be served.
+///
+/// [notFoundHandler] is a [Handler] to which requests that could not be handled
+/// by the DevTools handler are forwarded (e.g., a proxy to the VM service).
+FutureOr<Handler> devtoolsHandler({
+  required DartDevelopmentServiceImpl dds,
+  required String buildDir,
+  required Handler notFoundHandler,
+}) {
+  // Serves the web assets for DevTools.
+  final devtoolsAssetHandler = createStaticHandler(
+    buildDir,
+    defaultDocument: 'index.html',
+  );
+
+  // Support DevTools client-server interface via SSE.
+  // Note: the handler path needs to match the full *original* path, not the
+  // current request URL (we remove '/devtools' in the initial router but we
+  // need to include it here).
+  const devToolsSseHandlerPath = '/devtools/api/sse';
+  final devToolsApiHandler = SseHandler(
+    dds.authCodesEnabled
+        ? Uri.parse('/${dds.authCode}$devToolsSseHandlerPath')
+        : Uri.parse(devToolsSseHandlerPath),
+    keepAlive: sseKeepAlive,
+  );
+
+  devToolsApiHandler.connections.rest.listen(
+    (sseConnection) => DevToolsClient.fromSSEConnection(
+      sseConnection,
+      dds.shouldLogRequests,
+    ),
+  );
+
+  final devtoolsHandler = (Request request) {
+    // If the request isn't of the form api/<method> assume it's a request for
+    // DevTools assets.
+    if (request.url.pathSegments.length < 2 ||
+        request.url.pathSegments.first != 'api') {
+      return devtoolsAssetHandler(request);
+    }
+    final method = request.url.pathSegments[1];
+    if (method == 'ping') {
+      // Note: we have an 'OK' body response, otherwise the response has an
+      // incorrect status code (204 instead of 200).
+      return Response.ok('OK');
+    }
+    if (method == 'sse') {
+      return devToolsApiHandler.handler(request);
+    }
+    if (!ServerApi.canHandle(request)) {
+      return Response.notFound('$method is not a valid API');
+    }
+    return ServerApi.handle(request);
+  };
+
+  return (request) {
+    final pathSegments = request.url.pathSegments;
+    if (pathSegments.isEmpty || pathSegments.first != 'devtools') {
+      return notFoundHandler(request);
+    }
+    // Forward all requests to /devtools/* to the DevTools handler.
+    request = request.change(path: 'devtools');
+    return devtoolsHandler(request);
+  };
+}
diff --git a/dds/lib/src/devtools/file_system.dart b/dds/lib/src/devtools/file_system.dart
new file mode 100644
index 0000000..d5d6c6a
--- /dev/null
+++ b/dds/lib/src/devtools/file_system.dart
@@ -0,0 +1,82 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(bkonyi): remove once package:devtools_server_api is available
+// See https://github.com/flutter/devtools/issues/2958.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'usage.dart';
+
+class LocalFileSystem {
+  static String _userHomeDir() {
+    final String envKey =
+        Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME';
+    final String? value = Platform.environment[envKey];
+    return value == null ? '.' : value;
+  }
+
+  /// Returns the path to the DevTools storage directory.
+  static String devToolsDir() {
+    return path.join(_userHomeDir(), '.flutter-devtools');
+  }
+
+  /// Moves the .devtools file to ~/.flutter-devtools/.devtools if the .devtools file
+  /// exists in the user's home directory.
+  static void maybeMoveLegacyDevToolsStore() {
+    final file = File(path.join(_userHomeDir(), DevToolsUsage.storeName));
+    if (file.existsSync()) {
+      ensureDevToolsDirectory();
+      file.copySync(path.join(devToolsDir(), DevToolsUsage.storeName));
+      file.deleteSync();
+    }
+  }
+
+  /// Creates the ~/.flutter-devtools directory if it does not already exist.
+  static void ensureDevToolsDirectory() {
+    Directory('${LocalFileSystem.devToolsDir()}').createSync();
+  }
+
+  /// Returns a DevTools file from the given path.
+  ///
+  /// Only files within ~/.flutter-devtools/ can be accessed.
+  static File? devToolsFileFromPath(String pathFromDevToolsDir) {
+    if (pathFromDevToolsDir.contains('..')) {
+      // The passed in path should not be able to walk up the directory tree
+      // outside of the ~/.flutter-devtools/ directory.
+      return null;
+    }
+    ensureDevToolsDirectory();
+    final file = File(path.join(devToolsDir(), pathFromDevToolsDir));
+    if (!file.existsSync()) {
+      return null;
+    }
+    return file;
+  }
+
+  /// Returns a DevTools file from the given path as encoded json.
+  ///
+  /// Only files within ~/.flutter-devtools/ can be accessed.
+  static String? devToolsFileAsJson(String pathFromDevToolsDir) {
+    final file = devToolsFileFromPath(pathFromDevToolsDir);
+    if (file == null) return null;
+
+    final fileName = path.basename(file.path);
+    if (!fileName.endsWith('.json')) return null;
+
+    final content = file.readAsStringSync();
+    final json = jsonDecode(content);
+    json['lastModifiedTime'] = file.lastModifiedSync().toString();
+    return jsonEncode(json);
+  }
+
+  /// Whether the flutter store file exists.
+  static bool flutterStoreExists() {
+    final flutterStore = File('${_userHomeDir()}/.flutter');
+    return flutterStore.existsSync();
+  }
+}
diff --git a/dds/lib/src/devtools/server_api.dart b/dds/lib/src/devtools/server_api.dart
new file mode 100644
index 0000000..5f21a03
--- /dev/null
+++ b/dds/lib/src/devtools/server_api.dart
@@ -0,0 +1,228 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(bkonyi): remove once package:devtools_server_api is available
+// See https://github.com/flutter/devtools/issues/2958.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:devtools_shared/devtools_shared.dart';
+import 'package:shelf/shelf.dart' as shelf;
+
+import 'file_system.dart';
+import 'usage.dart';
+
+/// The DevTools server API.
+///
+/// This defines endpoints that serve all requests that come in over api/.
+class ServerApi {
+  static const errorNoActiveSurvey = 'ERROR: setActiveSurvey not called.';
+
+  /// Determines whether or not [request] is an API call.
+  static bool canHandle(shelf.Request request) {
+    return request.url.path.startsWith(apiPrefix);
+  }
+
+  /// Handles all requests.
+  ///
+  /// To override an API call, pass in a subclass of [ServerApi].
+  static FutureOr<shelf.Response> handle(
+    shelf.Request request, [
+    ServerApi? api,
+  ]) {
+    api ??= ServerApi();
+    switch (request.url.path) {
+      // ----- Flutter Tool GA store. -----
+      case apiGetFlutterGAEnabled:
+        // Is Analytics collection enabled?
+        return api.getCompleted(
+          request,
+          json.encode(FlutterUsage.doesStoreExist ? _usage!.enabled : null),
+        );
+      case apiGetFlutterGAClientId:
+        // Flutter Tool GA clientId - ONLY get Flutter's clientId if enabled is
+        // true.
+        return (FlutterUsage.doesStoreExist)
+            ? api.getCompleted(
+                request,
+                json.encode(_usage!.enabled ? _usage!.clientId : null),
+              )
+            : api.getCompleted(
+                request,
+                json.encode(null),
+              );
+
+      // ----- DevTools GA store. -----
+
+      case apiResetDevTools:
+        _devToolsUsage.reset();
+        return api.getCompleted(request, json.encode(true));
+      case apiGetDevToolsFirstRun:
+        // Has DevTools been run first time? To bring up welcome screen.
+        return api.getCompleted(
+          request,
+          json.encode(_devToolsUsage.isFirstRun),
+        );
+      case apiGetDevToolsEnabled:
+        // Is DevTools Analytics collection enabled?
+        return api.getCompleted(request, json.encode(_devToolsUsage.enabled));
+      case apiSetDevToolsEnabled:
+        // Enable or disable DevTools analytics collection.
+        final queryParams = request.requestedUri.queryParameters;
+        if (queryParams.containsKey(devToolsEnabledPropertyName)) {
+          _devToolsUsage.enabled =
+              json.decode(queryParams[devToolsEnabledPropertyName]!);
+        }
+        return api.setCompleted(request, json.encode(_devToolsUsage.enabled));
+
+      // ----- DevTools survey store. -----
+
+      case apiSetActiveSurvey:
+        // Assume failure.
+        bool result = false;
+
+        // Set the active survey used to store subsequent apiGetSurveyActionTaken,
+        // apiSetSurveyActionTaken, apiGetSurveyShownCount, and
+        // apiIncrementSurveyShownCount calls.
+        final queryParams = request.requestedUri.queryParameters;
+        if (queryParams.keys.length == 1 &&
+            queryParams.containsKey(activeSurveyName)) {
+          final String theSurveyName = queryParams[activeSurveyName]!;
+
+          // Set the current activeSurvey.
+          _devToolsUsage.activeSurvey = theSurveyName;
+          result = true;
+        }
+
+        return api.getCompleted(request, json.encode(result));
+      case apiGetSurveyActionTaken:
+        // Request setActiveSurvey has not been requested.
+        if (_devToolsUsage.activeSurvey == null) {
+          return api.badRequest('$errorNoActiveSurvey '
+              '- $apiGetSurveyActionTaken');
+        }
+        // SurveyActionTaken has the survey been acted upon (taken or dismissed)
+        return api.getCompleted(
+          request,
+          json.encode(_devToolsUsage.surveyActionTaken),
+        );
+      // TODO(terry): remove the query param logic for this request.
+      // setSurveyActionTaken should only be called with the value of true, so
+      // we can remove the extra complexity.
+      case apiSetSurveyActionTaken:
+        // Request setActiveSurvey has not been requested.
+        if (_devToolsUsage.activeSurvey == null) {
+          return api.badRequest('$errorNoActiveSurvey '
+              '- $apiSetSurveyActionTaken');
+        }
+        // Set the SurveyActionTaken.
+        // Has the survey been taken or dismissed..
+        final queryParams = request.requestedUri.queryParameters;
+        if (queryParams.containsKey(surveyActionTakenPropertyName)) {
+          _devToolsUsage.surveyActionTaken =
+              json.decode(queryParams[surveyActionTakenPropertyName]!);
+        }
+        return api.setCompleted(
+          request,
+          json.encode(_devToolsUsage.surveyActionTaken),
+        );
+      case apiGetSurveyShownCount:
+        // Request setActiveSurvey has not been requested.
+        if (_devToolsUsage.activeSurvey == null) {
+          return api.badRequest('$errorNoActiveSurvey '
+              '- $apiGetSurveyShownCount');
+        }
+        // SurveyShownCount how many times have we asked to take survey.
+        return api.getCompleted(
+          request,
+          json.encode(_devToolsUsage.surveyShownCount),
+        );
+      case apiIncrementSurveyShownCount:
+        // Request setActiveSurvey has not been requested.
+        if (_devToolsUsage.activeSurvey == null) {
+          return api.badRequest('$errorNoActiveSurvey '
+              '- $apiIncrementSurveyShownCount');
+        }
+        // Increment the SurveyShownCount, we've asked about the survey.
+        _devToolsUsage.incrementSurveyShownCount();
+        return api.getCompleted(
+          request,
+          json.encode(_devToolsUsage.surveyShownCount),
+        );
+      case apiGetBaseAppSizeFile:
+        final queryParams = request.requestedUri.queryParameters;
+        if (queryParams.containsKey(baseAppSizeFilePropertyName)) {
+          final filePath = queryParams[baseAppSizeFilePropertyName]!;
+          final fileJson = LocalFileSystem.devToolsFileAsJson(filePath);
+          if (fileJson == null) {
+            return api.badRequest('No JSON file available at $filePath.');
+          }
+          return api.getCompleted(request, fileJson);
+        }
+        return api.badRequest('Request for base app size file does not '
+            'contain a query parameter with the expected key: '
+            '$baseAppSizeFilePropertyName');
+      case apiGetTestAppSizeFile:
+        final queryParams = request.requestedUri.queryParameters;
+        if (queryParams.containsKey(testAppSizeFilePropertyName)) {
+          final filePath = queryParams[testAppSizeFilePropertyName]!;
+          final fileJson = LocalFileSystem.devToolsFileAsJson(filePath);
+          if (fileJson == null) {
+            return api.badRequest('No JSON file available at $filePath.');
+          }
+          return api.getCompleted(request, fileJson);
+        }
+        return api.badRequest('Request for test app size file does not '
+            'contain a query parameter with the expected key: '
+            '$testAppSizeFilePropertyName');
+      default:
+        return api.notImplemented(request);
+    }
+  }
+
+  // Accessing Flutter usage file e.g., ~/.flutter.
+  // NOTE: Only access the file if it exists otherwise Flutter Tool hasn't yet
+  //       been run.
+  static final FlutterUsage? _usage =
+      FlutterUsage.doesStoreExist ? FlutterUsage() : null;
+
+  // Accessing DevTools usage file e.g., ~/.devtools
+  static final DevToolsUsage _devToolsUsage = DevToolsUsage();
+
+  static DevToolsUsage get devToolsPreferences => _devToolsUsage;
+
+  /// Logs a page view in the DevTools server.
+  ///
+  /// In the open-source version of DevTools, Google Analytics handles this
+  /// without any need to involve the server.
+  FutureOr<shelf.Response> logScreenView(shelf.Request request) =>
+      notImplemented(request);
+
+  /// Return the value of the property.
+  FutureOr<shelf.Response> getCompleted(shelf.Request request, String value) =>
+      shelf.Response.ok('$value');
+
+  /// Return the value of the property after the property value has been set.
+  FutureOr<shelf.Response> setCompleted(shelf.Request request, String value) =>
+      shelf.Response.ok('$value');
+
+  /// A [shelf.Response] for API calls that encountered a request problem e.g.,
+  /// setActiveSurvey not called.
+  ///
+  /// This is a 400 Bad Request response.
+  FutureOr<shelf.Response> badRequest([String? logError]) {
+    if (logError != null) print(logError);
+    return shelf.Response(HttpStatus.badRequest);
+  }
+
+  /// A [shelf.Response] for API calls that have not been implemented in this
+  /// server.
+  ///
+  /// This is a no-op 204 No Content response because returning 404 Not Found
+  /// creates unnecessary noise in the console.
+  FutureOr<shelf.Response> notImplemented(shelf.Request request) =>
+      shelf.Response(HttpStatus.noContent);
+}
diff --git a/dds/lib/src/devtools/usage.dart b/dds/lib/src/devtools/usage.dart
new file mode 100644
index 0000000..8e61b90
--- /dev/null
+++ b/dds/lib/src/devtools/usage.dart
@@ -0,0 +1,227 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(bkonyi): remove once package:devtools_server_api is available
+// See https://github.com/flutter/devtools/issues/2958.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+import 'package:usage/usage_io.dart';
+
+import 'file_system.dart';
+
+/// Access the file '~/.flutter'.
+class FlutterUsage {
+  /// Create a new Usage instance; [versionOverride] and [configDirOverride] are
+  /// used for testing.
+  FlutterUsage({
+    String settingsName = 'flutter',
+    String? versionOverride,
+    String? configDirOverride,
+  }) {
+    _analytics = AnalyticsIO('', settingsName, '');
+  }
+
+  late Analytics _analytics;
+
+  /// Does the .flutter store exist?
+  static bool get doesStoreExist {
+    return LocalFileSystem.flutterStoreExists();
+  }
+
+  bool get isFirstRun => _analytics.firstRun;
+
+  bool get enabled => _analytics.enabled;
+
+  set enabled(bool value) => _analytics.enabled = value;
+
+  String get clientId => _analytics.clientId;
+}
+
+// Access the DevTools on disk store (~/.devtools/.devtools).
+class DevToolsUsage {
+  /// Create a new Usage instance; [versionOverride] and [configDirOverride] are
+  /// used for testing.
+  DevToolsUsage({
+    String? versionOverride,
+    String? configDirOverride,
+  }) {
+    LocalFileSystem.maybeMoveLegacyDevToolsStore();
+    properties = IOPersistentProperties(
+      storeName,
+      documentDirPath: LocalFileSystem.devToolsDir(),
+    );
+  }
+
+  static const storeName = '.devtools';
+
+  /// The activeSurvey is the property name of a top-level property
+  /// existing or created in the file ~/.devtools
+  /// If the property doesn't exist it is created with default survey values:
+  ///
+  ///   properties[activeSurvey]['surveyActionTaken'] = false;
+  ///   properties[activeSurvey]['surveyShownCount'] = 0;
+  ///
+  /// It is a requirement that the API apiSetActiveSurvey must be called before
+  /// calling any survey method on DevToolsUsage (addSurvey, rewriteActiveSurvey,
+  /// surveyShownCount, incrementSurveyShownCount, or surveyActionTaken).
+  String? _activeSurvey;
+
+  late IOPersistentProperties properties;
+
+  static const _surveyActionTaken = 'surveyActionTaken';
+  static const _surveyShownCount = 'surveyShownCount';
+
+  void reset() {
+    properties.remove('firstRun');
+    properties['enabled'] = false;
+  }
+
+  bool get isFirstRun {
+    properties['firstRun'] = properties['firstRun'] == null;
+    return properties['firstRun'];
+  }
+
+  bool get enabled {
+    if (properties['enabled'] == null) {
+      properties['enabled'] = false;
+    }
+
+    return properties['enabled'];
+  }
+
+  set enabled(bool? value) {
+    properties['enabled'] = value;
+    return properties['enabled'];
+  }
+
+  bool surveyNameExists(String? surveyName) => properties[surveyName] != null;
+
+  void _addSurvey(String? surveyName) {
+    assert(activeSurvey == surveyName);
+    rewriteActiveSurvey(false, 0);
+  }
+
+  String? get activeSurvey => _activeSurvey;
+
+  set activeSurvey(String? surveyName) {
+    _activeSurvey = surveyName;
+
+    if (!surveyNameExists(activeSurvey)) {
+      // Create the survey if property is non-existent in ~/.devtools
+      _addSurvey(activeSurvey);
+    }
+  }
+
+  /// Need to rewrite the entire survey structure for property to be persisted.
+  void rewriteActiveSurvey(bool? actionTaken, int? shownCount) {
+    properties[activeSurvey] = {
+      _surveyActionTaken: actionTaken,
+      _surveyShownCount: shownCount,
+    };
+  }
+
+  int? get surveyShownCount {
+    final prop = properties[activeSurvey];
+    if (prop[_surveyShownCount] == null) {
+      rewriteActiveSurvey(prop[_surveyActionTaken], 0);
+    }
+    return properties[activeSurvey][_surveyShownCount];
+  }
+
+  void incrementSurveyShownCount() {
+    surveyShownCount; // Ensure surveyShownCount has been initialized.
+    final prop = properties[activeSurvey];
+    rewriteActiveSurvey(prop[_surveyActionTaken], prop[_surveyShownCount] + 1);
+  }
+
+  bool get surveyActionTaken {
+    return properties[activeSurvey][_surveyActionTaken] == true;
+  }
+
+  set surveyActionTaken(bool? value) {
+    final prop = properties[activeSurvey];
+    rewriteActiveSurvey(value, prop[_surveyShownCount]);
+  }
+}
+
+abstract class PersistentProperties {
+  PersistentProperties(this.name);
+
+  final String name;
+
+  dynamic operator [](String key);
+
+  void operator []=(String key, dynamic value);
+
+  /// Re-read settings from the backing store.
+  ///
+  /// May be a no-op on some platforms.
+  void syncSettings();
+}
+
+const JsonEncoder _jsonEncoder = JsonEncoder.withIndent('  ');
+
+class IOPersistentProperties extends PersistentProperties {
+  IOPersistentProperties(
+    String name, {
+    String? documentDirPath,
+  }) : super(name) {
+    final String fileName = name.replaceAll(' ', '_');
+    documentDirPath ??= LocalFileSystem.devToolsDir();
+    _file = File(path.join(documentDirPath, fileName));
+    if (!_file.existsSync()) {
+      _file.createSync(recursive: true);
+    }
+    syncSettings();
+  }
+
+  IOPersistentProperties.fromFile(File file) : super(path.basename(file.path)) {
+    _file = file;
+    if (!_file.existsSync()) {
+      _file.createSync(recursive: true);
+    }
+    syncSettings();
+  }
+
+  late File _file;
+
+  Map? _map;
+
+  @override
+  dynamic operator [](String? key) => _map![key];
+
+  @override
+  void operator []=(String? key, dynamic value) {
+    if (value == null && !_map!.containsKey(key)) return;
+    if (_map![key] == value) return;
+
+    if (value == null) {
+      _map!.remove(key);
+    } else {
+      _map![key] = value;
+    }
+
+    try {
+      _file.writeAsStringSync(_jsonEncoder.convert(_map) + '\n');
+    } catch (_) {}
+  }
+
+  @override
+  void syncSettings() {
+    try {
+      String contents = _file.readAsStringSync();
+      if (contents.isEmpty) contents = '{}';
+      _map = jsonDecode(contents);
+    } catch (_) {
+      _map = {};
+    }
+  }
+
+  void remove(String propertyName) {
+    _map!.remove(propertyName);
+  }
+}
diff --git a/dds/lib/src/expression_evaluator.dart b/dds/lib/src/expression_evaluator.dart
index 59bbe36..76026b6 100644
--- a/dds/lib/src/expression_evaluator.dart
+++ b/dds/lib/src/expression_evaluator.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
+import 'dart:async';
 
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
 
@@ -17,16 +17,16 @@
   ExpressionEvaluator(this.dds);
 
   Future<Map<String, dynamic>> execute(json_rpc.Parameters parameters) async {
-    DartDevelopmentServiceClient externalClient =
+    DartDevelopmentServiceClient? externalClient =
         dds.clientManager.findFirstClientThatHandlesService(
       'compileExpression',
     );
     // If no compilation service is registered, just forward to the VM service.
     if (externalClient == null) {
-      return await dds.vmServiceClient.sendRequest(
+      return (await dds.vmServiceClient.sendRequest(
         parameters.method,
         parameters.value,
-      );
+      )) as Map<String, dynamic>;
     }
 
     final isolateId = parameters['isolateId'].asString;
@@ -59,18 +59,18 @@
       json_rpc.Parameters parameters) async {
     final params = _setupParams(parameters);
     params['isolateId'] = parameters['isolateId'].asString;
-    if (parameters['scope'].asMapOr(null) != null) {
+    if (parameters['scope'].asMapOr({}).isNotEmpty) {
       params['scope'] = parameters['scope'].asMap;
     }
-    return await dds.vmServiceClient.sendRequest(
+    return (await dds.vmServiceClient.sendRequest(
       '_buildExpressionEvaluationScope',
       params,
-    );
+    )) as Map<String, dynamic>;
   }
 
   Future<String> _compileExpression(String isolateId, String expression,
       Map<String, dynamic> buildScopeResponseResult) async {
-    DartDevelopmentServiceClient externalClient =
+    DartDevelopmentServiceClient? externalClient =
         dds.clientManager.findFirstClientThatHandlesService(
       'compileExpression',
     );
@@ -116,13 +116,13 @@
     params['kernelBytes'] = kernelBase64;
     params['disableBreakpoints'] =
         parameters['disableBreakpoints'].asBoolOr(false);
-    if (parameters['scope'].asMapOr(null) != null) {
+    if (parameters['scope'].asMapOr({}).isNotEmpty) {
       params['scope'] = parameters['scope'].asMap;
     }
-    return await dds.vmServiceClient.sendRequest(
+    return (await dds.vmServiceClient.sendRequest(
       '_evaluateCompiledExpression',
       params,
-    );
+    )) as Map<String, dynamic>;
   }
 
   Map<String, dynamic> _setupParams(json_rpc.Parameters parameters) {
diff --git a/dds/lib/src/isolate_manager.dart b/dds/lib/src/isolate_manager.dart
index a5139d5..e9e14df 100644
--- a/dds/lib/src/isolate_manager.dart
+++ b/dds/lib/src/isolate_manager.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
 
 import 'client.dart';
@@ -56,7 +54,7 @@
   /// 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(String clientName) async {
+  Future<void> maybeResumeAfterClientChange(String? clientName) async {
     // Remove approvals from the disconnected client.
     _resumeApprovalsByName.remove(clientName);
 
@@ -75,7 +73,7 @@
   /// which have provided approval to resume this isolate. If not provided,
   /// the existing approvals state will be examined to see if the isolate
   /// should resume due to a client disconnect or name change.
-  bool shouldResume({DartDevelopmentServiceClient resumingClient}) {
+  bool shouldResume({DartDevelopmentServiceClient? resumingClient}) {
     if (resumingClient != null) {
       // Mark approval by the client.
       _resumeApprovalsByName.add(resumingClient.name);
@@ -85,9 +83,9 @@
         isolateManager.dds.clientManager.clientResumePermissions;
 
     // Determine which clients require approval for this pause type.
-    permissions.forEach((name, clientNamePermissions) {
+    permissions.forEach((clientName, clientNamePermissions) {
       if (clientNamePermissions.permissionsMask & _isolateStateMask != 0) {
-        requiredClientApprovals.add(name);
+        requiredClientApprovals.add(clientName!);
       }
     });
 
@@ -116,8 +114,8 @@
   final IsolateManager isolateManager;
   final String name;
   final String id;
-  final Set<String> _resumeApprovalsByName = {};
-  _IsolateState _state;
+  final Set<String?> _resumeApprovalsByName = {};
+  _IsolateState? _state;
 }
 
 class IsolateManager {
@@ -152,16 +150,16 @@
         final isolate = isolates[id];
         switch (eventKind) {
           case ServiceEvents.pauseExit:
-            isolate.pausedOnExit();
+            isolate!.pausedOnExit();
             break;
           case ServiceEvents.pausePostRequest:
-            isolate.pausedPostRequest();
+            isolate!.pausedPostRequest();
             break;
           case ServiceEvents.pauseStart:
-            isolate.pausedOnStart();
+            isolate!.pausedOnStart();
             break;
           case ServiceEvents.resume:
-            isolate.resumed();
+            isolate!.resumed();
             break;
           default:
             break;
@@ -237,12 +235,13 @@
     String isolateId,
     json_rpc.Parameters parameters,
   ) async {
-    final step = parameters['step'].asStringOr(null);
-    final frameIndex = parameters['frameIndex'].asIntOr(null);
+    const invalidFrameIndex = -1;
+    final step = parameters['step'].asStringOr('');
+    final frameIndex = parameters['frameIndex'].asIntOr(invalidFrameIndex);
     final resumeResult = await dds.vmServiceClient.sendRequest('resume', {
       'isolateId': isolateId,
-      if (step != null) 'step': step,
-      if (frameIndex != null) 'frameIndex': frameIndex,
+      if (step.isNotEmpty) 'step': step,
+      if (frameIndex != invalidFrameIndex) 'frameIndex': frameIndex,
     });
     return resumeResult;
   }
diff --git a/dds/lib/src/logging_repository.dart b/dds/lib/src/logging_repository.dart
index 0b31ca6..4537d7e 100644
--- a/dds/lib/src/logging_repository.dart
+++ b/dds/lib/src/logging_repository.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'dart:math';
 
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
@@ -50,9 +48,8 @@
 
 // TODO(bkonyi): move to standalone file if we decide to use this elsewhere.
 class _RingBuffer<T> {
-  _RingBuffer(int initialSize) {
-    _bufferSize = initialSize;
-    _buffer = List<T>.filled(
+  _RingBuffer(this._bufferSize) {
+    _buffer = List<T?>.filled(
       _bufferSize,
       null,
     );
@@ -60,7 +57,7 @@
 
   Iterable<T> call() sync* {
     for (int i = _size - 1; i >= 0; --i) {
-      yield _buffer[(_count - i - 1) % _bufferSize];
+      yield _buffer[(_count - i - 1) % _bufferSize]!;
     }
   }
 
@@ -76,7 +73,7 @@
     if (size == _bufferSize) {
       return;
     }
-    final resized = List<T>.filled(
+    final resized = List<T?>.filled(
       size,
       null,
     );
@@ -97,5 +94,5 @@
 
   int _bufferSize;
   int _count = 0;
-  List<T> _buffer;
+  late List<T?> _buffer;
 }
diff --git a/dds/lib/src/named_lookup.dart b/dds/lib/src/named_lookup.dart
index 8515411..7671d28 100644
--- a/dds/lib/src/named_lookup.dart
+++ b/dds/lib/src/named_lookup.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 // Originally pulled from dart:_vmservice.
 
 import 'dart:collection';
@@ -25,14 +23,14 @@
   }
 
   void remove(E e) {
-    final id = _ids.remove(e);
+    final id = _ids.remove(e)!;
     _elements.remove(id);
     _generator.release(id);
   }
 
-  E operator [](String id) => _elements[id];
+  E? operator [](String id) => _elements[id];
 
-  String keyOf(E e) => _ids[e];
+  String? keyOf(E e) => _ids[e];
 
   Iterator<E> get iterator => _ids.keys.iterator;
 }
diff --git a/dds/lib/src/rpc_error_codes.dart b/dds/lib/src/rpc_error_codes.dart
index 895c07c..74a3723 100644
--- a/dds/lib/src/rpc_error_codes.dart
+++ b/dds/lib/src/rpc_error_codes.dart
@@ -2,15 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
 
 abstract class RpcErrorCodes {
   static json_rpc.RpcException buildRpcException(int code, {dynamic data}) {
     return json_rpc.RpcException(
       code,
-      errorMessages[code],
+      errorMessages[code]!,
       data: data,
     );
   }
diff --git a/dds/lib/src/stream_manager.dart b/dds/lib/src/stream_manager.dart
index 26bc6d7..94f791a 100644
--- a/dds/lib/src/stream_manager.dart
+++ b/dds/lib/src/stream_manager.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'dart:typed_data';
 
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
@@ -27,10 +25,10 @@
   void streamNotify(
     String streamId,
     data, {
-    DartDevelopmentServiceClient excludedClient,
+    DartDevelopmentServiceClient? excludedClient,
   }) {
     if (streamListeners.containsKey(streamId)) {
-      final listeners = streamListeners[streamId];
+      final listeners = streamListeners[streamId]!;
       final isBinaryData = data is Uint8List;
       for (final listener in listeners) {
         if (listener == excludedClient) {
@@ -64,7 +62,7 @@
     String service,
     String alias,
   ) {
-    final namespace = dds.getNamespace(client);
+    final namespace = dds.getNamespace(client)!;
     streamNotify(
       kServiceStream,
       _buildStreamRegisteredEvent(namespace, service, alias),
@@ -86,7 +84,7 @@
             'kind': 'ServiceUnregistered',
             'timestamp': DateTime.now().millisecondsSinceEpoch,
             'service': service,
-            'method': namespace + '.' + service,
+            'method': namespace! + '.' + service,
           },
         },
         excludedClient: client,
@@ -120,7 +118,7 @@
         // Keep a history of messages to send to clients when they first
         // subscribe to a stream with an event history.
         if (loggingRepositories.containsKey(streamId)) {
-          loggingRepositories[streamId].add(parameters.asMap);
+          loggingRepositories[streamId]!.add(parameters.asMap);
         }
         streamNotify(streamId, parameters.value);
       },
@@ -132,11 +130,16 @@
   /// If `client` is the first client to listen to `stream`, DDS will send a
   /// `streamListen` request for `stream` to the VM service.
   Future<void> streamListen(
-    DartDevelopmentServiceClient client,
+    DartDevelopmentServiceClient? client,
     String stream,
   ) async {
-    assert(stream != null && stream.isNotEmpty);
+    assert(stream.isNotEmpty);
     if (!streamListeners.containsKey(stream)) {
+      // Initialize the list of clients for the new stream before we do
+      // anything else to ensure multiple clients registering for the same
+      // stream in quick succession doesn't result in multiple streamListen
+      // requests being sent to the VM service.
+      streamListeners[stream] = <DartDevelopmentServiceClient>[];
       if ((stream == kDebugStream && client == null) ||
           stream != kDebugStream) {
         // This will return an RPC exception if the stream doesn't exist. This
@@ -146,15 +149,14 @@
         });
         assert(result['type'] == 'Success');
       }
-      streamListeners[stream] = <DartDevelopmentServiceClient>[];
     }
-    if (streamListeners[stream].contains(client)) {
+    if (streamListeners[stream]!.contains(client)) {
       throw kStreamAlreadySubscribedException;
     }
     if (client != null) {
-      streamListeners[stream].add(client);
+      streamListeners[stream]!.add(client);
       if (loggingRepositories.containsKey(stream)) {
-        loggingRepositories[stream].sendHistoricalLogs(client);
+        loggingRepositories[stream]!.sendHistoricalLogs(client);
       } else if (stream == kServiceStream) {
         // Send all previously registered service extensions when a client
         // subscribes to the Service stream.
@@ -167,9 +169,9 @@
             client.sendNotification(
               'streamNotify',
               _buildStreamRegisteredEvent(
-                namespace,
+                namespace!,
                 service,
-                c.services[service],
+                c.services[service]!,
               ),
             );
           }
@@ -178,12 +180,12 @@
     }
   }
 
-  List<Map<String, dynamic>> getStreamHistory(String stream) {
+  List<Map<String, dynamic>>? getStreamHistory(String stream) {
     if (!loggingRepositories.containsKey(stream)) {
       return null;
     }
     return [
-      for (final event in loggingRepositories[stream]()) event['event'],
+      for (final event in loggingRepositories[stream]!()) event['event'],
     ];
   }
 
@@ -192,13 +194,13 @@
   /// If `client` is the last client to unsubscribe from `stream`, DDS will
   /// send a `streamCancel` request for `stream` to the VM service.
   Future<void> streamCancel(
-    DartDevelopmentServiceClient client,
+    DartDevelopmentServiceClient? client,
     String stream, {
     bool cancelCoreStream = false,
   }) async {
-    assert(stream != null && stream.isNotEmpty);
+    assert(stream.isNotEmpty);
     final listeners = streamListeners[stream];
-    if (client != null && (listeners == null || !listeners.contains(client))) {
+    if (listeners == null || client != null && !listeners.contains(client)) {
       throw kStreamNotSubscribedException;
     }
     listeners.remove(client);
diff --git a/dds/lib/vm_service_extensions.dart b/dds/lib/vm_service_extensions.dart
index 0b6aaf4..903c14a 100644
--- a/dds/lib/vm_service_extensions.dart
+++ b/dds/lib/vm_service_extensions.dart
@@ -2,19 +2,16 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.10
-
 import 'dart:async';
 import 'dart:collection';
 
 import 'package:async/async.dart';
-import 'package:meta/meta.dart';
 import 'package:pedantic/pedantic.dart';
 import 'package:vm_service/src/vm_service.dart';
 
 extension DdsExtension on VmService {
   static bool _factoriesRegistered = false;
-  static Version _ddsVersion;
+  static Version? _ddsVersion;
 
   /// The _getDartDevelopmentServiceVersion_ RPC is used to determine what version of
   /// the Dart Development Service Protocol is served by a DDS instance.
@@ -25,7 +22,7 @@
       _ddsVersion =
           await _callHelper<Version>('getDartDevelopmentServiceVersion');
     }
-    return _ddsVersion;
+    return _ddsVersion!;
   }
 
   /// Retrieve the event history for `stream`.
@@ -47,19 +44,19 @@
   /// If `stream` does not have event history collected, a parameter error is
   /// sent over the returned [Stream].
   Stream<Event> onEventWithHistory(String stream) {
-    StreamController<Event> controller;
-    StreamQueue<Event> streamEvents;
+    late StreamController<Event> controller;
+    late StreamQueue<Event> streamEvents;
 
     controller = StreamController<Event>(onListen: () async {
       streamEvents = StreamQueue<Event>(onEvent(stream));
       final history = (await getStreamHistory(stream)).history;
-      Event firstStreamEvent;
+      Event? firstStreamEvent;
       unawaited(streamEvents.peek.then((e) {
         firstStreamEvent = e;
       }));
       for (final event in history) {
         if (firstStreamEvent != null &&
-            event.timestamp > firstStreamEvent.timestamp) {
+            event.timestamp! > firstStreamEvent!.timestamp!) {
           break;
         }
         controller.sink.add(event);
@@ -109,12 +106,12 @@
     if (_ddsVersion == null) {
       _ddsVersion = await getDartDevelopmentServiceVersion();
     }
-    return ((_ddsVersion.major == major && _ddsVersion.minor >= minor) ||
-        (_ddsVersion.major > major));
+    return ((_ddsVersion!.major == major && _ddsVersion!.minor! >= minor) ||
+        (_ddsVersion!.major! > major));
   }
 
   Future<T> _callHelper<T>(String method,
-      {String isolateId, Map args = const {}}) {
+      {String? isolateId, Map args = const {}}) {
     if (!_factoriesRegistered) {
       _registerFactories();
     }
@@ -135,10 +132,10 @@
 
 /// A collection of historical [Event]s from some stream.
 class StreamHistory extends Response {
-  static StreamHistory parse(Map<String, dynamic> json) =>
+  static StreamHistory? parse(Map<String, dynamic>? json) =>
       json == null ? null : StreamHistory._fromJson(json);
 
-  StreamHistory({@required List<Event> history}) : _history = history;
+  StreamHistory({required List<Event> history}) : _history = history;
 
   StreamHistory._fromJson(Map<String, dynamic> json)
       : _history = json['history']
diff --git a/dds/pubspec.yaml b/dds/pubspec.yaml
index 221e3df..2bf3136 100644
--- a/dds/pubspec.yaml
+++ b/dds/pubspec.yaml
@@ -3,7 +3,7 @@
   A library used to spawn the Dart Developer Service, used to communicate with
   a Dart VM Service instance.
 
-version: 1.7.6
+version: 2.0.1
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
 
@@ -12,18 +12,24 @@
 
 dependencies:
   async: ^2.4.1
-  json_rpc_2: ^2.2.0
+  collection: ^1.15.0
+  devtools_shared: ^2.3.0
+  json_rpc_2: ^3.0.0
   meta: ^1.1.8
+  path: ^1.8.0
   pedantic: ^1.7.0
   shelf: ^1.0.0
   shelf_proxy: ^1.0.0
+  shelf_static: ^1.0.0
   shelf_web_socket: ^1.0.0
-  sse: ^3.7.0
+  sse: ^4.0.0
   stream_channel: ^2.0.0
-  vm_service: ^6.0.1-nullsafety.0
+  usage: ^4.0.0
+  vm_service: ^7.0.0
   web_socket_channel: ^2.0.0
 
 dev_dependencies:
-  shelf_static: ^1.0.0
+  args: ^2.0.0
+  http: ^0.13.0
   test: ^1.0.0
   webdriver: ^3.0.0
diff --git a/dds/tool/dap/codegen.dart b/dds/tool/dap/codegen.dart
new file mode 100644
index 0000000..edd064b
--- /dev/null
+++ b/dds/tool/dap/codegen.dart
@@ -0,0 +1,689 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:math';
+
+import 'package:collection/collection.dart';
+
+import 'json_schema.dart';
+import 'json_schema_extensions.dart';
+
+/// Generates Dart classes from the Debug Adapter Protocol's JSON Schema.
+class CodeGenerator {
+  /// Writes all required Dart classes for the supplied DAP [schema].
+  void writeAll(IndentableStringBuffer buffer, JsonSchema schema) {
+    _writeDefinitionClasses(buffer, schema);
+    buffer.writeln();
+    _writeBodyClasses(buffer, schema);
+    buffer.writeln();
+    _writeEventTypeLookup(buffer, schema);
+    buffer.writeln();
+    _writeCommandArgumentTypeLookup(buffer, schema);
+  }
+
+  /// Maps a name used in the DAP spec to a valid name for use in Dart.
+  ///
+  /// Reserved words like `default` will be mapped to a suitable alternative.
+  /// Prefixed underscores are removed to avoid making things private.
+  ///
+  /// Underscores between words are swapped for camelCase.
+  String _dartSafeName(String name) {
+    const improvedName = {
+      'default': 'defaultValue',
+    };
+    return improvedName[name] ??
+        // Some types are prefixed with _ in the spec but that will make them
+        // private in Dart and inaccessible to the adapter so we strip it off.
+        name
+            .replaceAll(RegExp(r'^_+'), '')
+            // Also replace any other underscores to make camelCase
+            .replaceAllMapped(
+                RegExp(r'_(.)'), (m) => m.group(1)!.toUpperCase());
+  }
+
+  /// Re-wraps [lines] at [maxLength] to help keep comments for indented code
+  /// within 80 characters.
+  Iterable<String> _wrapLines(List<String> lines, int maxLength) sync* {
+    lines = lines.map((l) => l.trimRight()).toList();
+    for (var line in lines) {
+      while (true) {
+        if (line.length <= maxLength || line.startsWith('-')) {
+          yield line;
+          break;
+        } else {
+          var lastSpace = line.lastIndexOf(' ', max(maxLength, 0));
+          // If there was no valid place to wrap, yield the whole string.
+          if (lastSpace == -1) {
+            yield line;
+            break;
+          } else {
+            yield line.substring(0, lastSpace);
+            line = line.substring(lastSpace + 1);
+          }
+        }
+      }
+    }
+  }
+
+  /// For each Response/Event class in the spec, generate a specific class to
+  /// represent its body.
+  ///
+  /// These classes are used to simplify sending responses/events from the
+  /// Debug Adapters by avoiding the need to construct the entire response/event
+  /// which requires additional fields (for example the corresponding requests
+  /// id/command and sequences):
+  ///
+  ///     this.sendResponse(FooBody(x: 1))
+  ///
+  /// instead of
+  ///
+  ///     this.sendResponse(Response(
+  ///       seq: seq++,
+  ///       request_seq: request.seq,
+  ///       command: request.command,
+  ///       body: {
+  ///         x: 1
+  ///         ...
+  ///       }
+  ///     ))
+  void _writeBodyClasses(IndentableStringBuffer buffer, JsonSchema schema) {
+    for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
+      final name = entry.key;
+      final type = entry.value;
+      final baseType = type.baseType;
+
+      if (baseType?.refName == 'Response' || baseType?.refName == 'Event') {
+        final baseClass = baseType?.refName == 'Event'
+            ? JsonType.named(schema, 'EventBody')
+            : null;
+        final classProperties = schema.propertiesFor(type);
+        final bodyProperty = classProperties['body'];
+        var bodyPropertyProperties = bodyProperty?.properties;
+
+        _writeClass(
+          buffer,
+          bodyProperty ?? JsonType.empty(schema),
+          '${name}Body',
+          bodyPropertyProperties ?? {},
+          {},
+          baseClass,
+          null,
+        );
+      }
+    }
+  }
+
+  /// Writes a `canParse` function for a DAP spec class.
+  ///
+  /// The function checks whether an Object? is a a valid map that contains all
+  /// required fields and matches the types of the spec class.
+  ///
+  /// This is used where the spec contains union classes and we need to decide
+  /// which of the allowed types a given value is.
+  void _writeCanParseMethod(
+    IndentableStringBuffer buffer,
+    JsonType type,
+    Map<String, JsonType> properties, {
+    required String? baseTypeRefName,
+  }) {
+    buffer
+      ..writeIndentedln('static bool canParse(Object? obj) {')
+      ..indent()
+      ..writeIndentedln('if (obj is! Map<String, dynamic>) {')
+      ..indent()
+      ..writeIndentedln('return false;')
+      ..outdent()
+      ..writeIndentedln('}');
+    // In order to consider this valid for parsing, all fields that must not be
+    // undefined must be present and also type check for the correct type.
+    // Any fields that are optional but present, must still type check.
+    for (final entry in properties.entries.sortedBy((e) => e.key)) {
+      final propertyName = entry.key;
+      final propertyType = entry.value;
+      final isOptional = !type.requiresField(propertyName);
+
+      if (propertyType.isAny && isOptional) {
+        continue;
+      }
+
+      buffer.writeIndented('if (');
+      _writeTypeCheckCondition(buffer, propertyType, "obj['$propertyName']",
+          isOptional: isOptional, invert: true);
+      buffer
+        ..writeln(') {')
+        ..indent()
+        ..writeIndentedln('return false;')
+        ..outdent()
+        ..writeIndentedln('}');
+    }
+    buffer
+      ..writeIndentedln(
+        baseTypeRefName != null
+            ? 'return $baseTypeRefName.canParse(obj);'
+            : 'return true;',
+      )
+      ..outdent()
+      ..writeIndentedln('}');
+  }
+
+  /// Writes the Dart class for [type].
+  void _writeClass(
+    IndentableStringBuffer buffer,
+    JsonType type,
+    String name,
+    Map<String, JsonType> classProperties,
+    Map<String, JsonType> baseProperties,
+    JsonType? baseType,
+    JsonType? resolvedBaseType, {
+    Map<String, String> additionalValues = const {},
+  }) {
+    // Some properties are defined in both the base and the class, because the
+    // type may be narrowed, but sometimes we only want those that are defined
+    // only in this class.
+    final classOnlyProperties = {
+      for (final property in classProperties.entries)
+        if (!baseProperties.containsKey(property.key))
+          property.key: property.value,
+    };
+    _writeTypeDescription(buffer, type);
+    buffer.write('class $name ');
+    if (baseType != null) {
+      buffer.write('extends ${baseType.refName} ');
+    }
+    buffer
+      ..writeln('{')
+      ..indent();
+    for (final val in additionalValues.entries) {
+      buffer
+        ..writeIndentedln('@override')
+        ..writeIndentedln("final ${val.key} = '${val.value}';");
+    }
+    _writeFields(buffer, type, classOnlyProperties);
+    buffer.writeln();
+    _writeFromJsonStaticMethod(buffer, name);
+    buffer.writeln();
+    _writeConstructor(buffer, name, type, classProperties, baseProperties,
+        classOnlyProperties,
+        baseType: resolvedBaseType);
+    buffer.writeln();
+    _writeFromMapConstructor(buffer, name, type, classOnlyProperties,
+        callSuper: resolvedBaseType != null);
+    buffer.writeln();
+    _writeCanParseMethod(buffer, type, classProperties,
+        baseTypeRefName: baseType?.refName);
+    buffer.writeln();
+    _writeToJsonMethod(buffer, name, type, classOnlyProperties,
+        callSuper: resolvedBaseType != null);
+    buffer
+      ..outdent()
+      ..writeln('}')
+      ..writeln();
+  }
+
+  /// Write a map to look up the `command` for a given `RequestArguments` type
+  /// to simplify sending requests back to the client:
+  ///
+  ///     this.sendRequest(FooArguments(x: 1))
+  ///
+  /// instead of
+  ///
+  ///     this.sendRequest(Request(
+  ///       seq: seq++,
+  ///       command: request.command,
+  ///       arguments: {
+  ///         x: 1
+  ///         ...
+  ///       }
+  ///     ))
+  void _writeCommandArgumentTypeLookup(
+      IndentableStringBuffer buffer, JsonSchema schema) {
+    buffer
+      ..writeln('const commandTypes = {')
+      ..indent();
+    for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
+      final type = entry.value;
+      final baseType = type.baseType;
+
+      if (baseType?.refName == 'Request') {
+        final classProperties = schema.propertiesFor(type);
+        final argumentsProperty = classProperties['arguments'];
+        final commandType = classProperties['command']?.literalValue;
+        if (argumentsProperty?.dollarRef != null && commandType != null) {
+          buffer.writeIndentedln(
+              "${argumentsProperty!.refName}: '$commandType',");
+        }
+      }
+    }
+    buffer
+      ..writeln('};')
+      ..outdent();
+  }
+
+  /// Writes a constructor for [type].
+  ///
+  /// The constructor will have named arguments for all fields, with those that
+  /// are mandatory marked with `required`.
+  void _writeConstructor(
+    IndentableStringBuffer buffer,
+    String name,
+    JsonType type,
+    Map<String, JsonType> classProperties,
+    Map<String, JsonType> baseProperties,
+    Map<String, JsonType> classOnlyProperties, {
+    required JsonType? baseType,
+  }) {
+    buffer.writeIndented('$name(');
+    if (classProperties.isNotEmpty || baseProperties.isNotEmpty) {
+      buffer
+        ..writeln('{')
+        ..indent();
+
+      // Properties for this class are written as 'this.foo'.
+      for (final entry in classOnlyProperties.entries.sortedBy((e) => e.key)) {
+        final propertyName = entry.key;
+        final fieldName = _dartSafeName(propertyName);
+        final isOptional = !type.requiresField(propertyName);
+        buffer.writeIndented('');
+        if (!isOptional) {
+          buffer.write('required ');
+        }
+        buffer.writeln('this.$fieldName, ');
+      }
+
+      // Properties from the base class are standard arguments that will be
+      // passed to a super() call.
+      for (final entry in baseProperties.entries.sortedBy((e) => e.key)) {
+        final propertyName = entry.key;
+        // If this field is defined by the class and the base, prefer the
+        // class one as it may contain things like the literal values.
+        final propertyType = classProperties[propertyName] ?? entry.value;
+
+        final fieldName = _dartSafeName(propertyName);
+        if (propertyType.literalValue != null) {
+          continue;
+        }
+        final isOptional = !type.requiresField(propertyName);
+        final dartType = propertyType.asDartType(isOptional: isOptional);
+        buffer.writeIndented('');
+        if (!isOptional) {
+          buffer.write('required ');
+        }
+        buffer.writeln('$dartType $fieldName, ');
+      }
+      buffer
+        ..outdent()
+        ..writeIndented('}');
+    }
+    buffer.write(')');
+
+    if (baseType != null) {
+      buffer.write(': super(');
+      if (baseProperties.isNotEmpty) {
+        buffer
+          ..writeln()
+          ..indent();
+        for (final entry in baseProperties.entries) {
+          final propertyName = entry.key;
+          // Skip any properties that have literal values defined by the base
+          // as we won't need to supply them.
+          if (entry.value.literalValue != null) {
+            continue;
+          }
+          // If this field is defined by the class and the base, prefer the
+          // class one as it may contain things like the literal values.
+          final propertyType = classProperties[propertyName] ?? entry.value;
+          final fieldName = _dartSafeName(propertyName);
+          final literalValue = propertyType.literalValue;
+          final value = literalValue != null ? "'$literalValue'" : fieldName;
+          buffer.writeIndentedln('$fieldName: $value, ');
+        }
+        buffer
+          ..outdent()
+          ..writeIndented('');
+      }
+      buffer.write(')');
+    }
+    buffer.writeln(';');
+  }
+
+  /// Write a class for each item in the DAP spec.
+  ///
+  /// Skips over the Request and Event sub-classes, as they are handled by the
+  /// simplified body classes written by [_writeBodyClasses]. Uses
+  /// [RequestArguments] as the base class for all argument classes.
+  void _writeDefinitionClasses(
+      IndentableStringBuffer buffer, JsonSchema schema) {
+    for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
+      final name = entry.key;
+      final type = entry.value;
+
+      var baseType = type.baseType;
+      final resolvedBaseType =
+          baseType != null ? schema.typeFor(baseType) : null;
+      final classProperties = schema.propertiesFor(type, includeBase: false);
+      final baseProperties = resolvedBaseType != null
+          ? schema.propertiesFor(resolvedBaseType)
+          : <String, JsonType>{};
+
+      // Skip creation of Request sub-classes, as we don't use these we just
+      // pass the arguments in to the method directly.
+      if (name != 'Request' && name.endsWith('Request')) {
+        continue;
+      }
+
+      // Skip creation of Event sub-classes, as we don't use these we just
+      // pass the body in to sendEvent directly.
+      if (name != 'Event' && name.endsWith('Event')) {
+        continue;
+      }
+
+      // Create a synthetic base class for arguments to provide type safety
+      // for sending requests.
+      if (baseType == null && name.endsWith('Arguments')) {
+        baseType = JsonType.named(schema, 'RequestArguments');
+      }
+
+      _writeClass(
+        buffer,
+        type,
+        name,
+        classProperties,
+        baseProperties,
+        baseType,
+        resolvedBaseType,
+      );
+    }
+  }
+
+  /// Writes a DartDoc comment, wrapped at 80 characters taking into account
+  /// the indentation.
+  void _writeDescription(IndentableStringBuffer buffer, String? description) {
+    final maxLength = 80 - buffer.totalIndent - 4;
+    if (description != null) {
+      for (final line in _wrapLines(description.split('\n'), maxLength)) {
+        buffer.writeIndentedln('/// $line');
+      }
+    }
+  }
+
+  /// Write a map to look up the `event` for a given `EventBody` type
+  /// to simplify sending events back to the client:
+  ///
+  ///     this.sendEvent(FooEvent(x: 1))
+  ///
+  /// instead of
+  ///
+  ///     this.sendEvent(Event(
+  ///       seq: seq++,
+  ///       event: 'FooEvent',
+  ///       arguments: {
+  ///         x: 1
+  ///         ...
+  ///       }
+  ///     ))
+  void _writeEventTypeLookup(IndentableStringBuffer buffer, JsonSchema schema) {
+    buffer
+      ..writeln('const eventTypes = {')
+      ..indent();
+    for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
+      final name = entry.key;
+      final type = entry.value;
+      final baseType = type.baseType;
+
+      if (baseType?.refName == 'Event') {
+        final classProperties = schema.propertiesFor(type);
+        final eventType = classProperties['event']!.literalValue;
+        buffer.writeIndentedln("${name}Body: '$eventType',");
+      }
+    }
+    buffer
+      ..writeln('};')
+      ..outdent();
+  }
+
+  /// Writes Dart fields for [properties], taking into account whether they are
+  /// required for [type].
+  void _writeFields(IndentableStringBuffer buffer, JsonType type,
+      Map<String, JsonType> properties) {
+    for (final entry in properties.entries.sortedBy((e) => e.key)) {
+      final propertyName = entry.key;
+      final fieldName = _dartSafeName(propertyName);
+      final propertyType = entry.value;
+      final isOptional = !type.requiresField(propertyName);
+      final dartType = propertyType.asDartType(isOptional: isOptional);
+      _writeDescription(buffer, propertyType.description);
+      buffer.writeIndentedln('final $dartType $fieldName;');
+    }
+  }
+
+  /// Writes an expression to deserialise a [valueCode].
+  ///
+  /// If [type] represents a spec type, it's `fromJson` function will be called.
+  /// If [type] is a [List], it will be mapped over this function again.
+  /// If [type] is an union, the appropriate `canParse` functions will be used to
+  ///   determine which `fromJson` function to call.
+  void _writeFromJsonExpression(
+      IndentableStringBuffer buffer, JsonType type, String valueCode,
+      {bool isOptional = false}) {
+    final dartType = type.asDartType(isOptional: isOptional);
+    final dartTypeNotNullable = type.asDartType();
+    final nullOp = isOptional ? '?' : '';
+
+    if (type.isAny || type.isSimple) {
+      buffer.write('$valueCode');
+      if (dartType != 'Object?') {
+        buffer.write(' as $dartType');
+      }
+    } else if (type.isList) {
+      buffer.write('($valueCode as List$nullOp)$nullOp.map((item) => ');
+      _writeFromJsonExpression(buffer, type.items!, 'item');
+      buffer.write(').toList()');
+    } else if (type.isUnion) {
+      final types = type.unionTypes;
+
+      // Write a check against each type, eg.:
+      // x is y ? new Either.tx(x) : (...)
+      for (var i = 0; i < types.length; i++) {
+        final isLast = i == types.length - 1;
+
+        // For the last item, if we're optional we won't wrap if in a check, as
+        // the constructor will only be called if canParse() returned true, so
+        // it'll the only remaining option.
+        if (!isLast || isOptional) {
+          _writeTypeCheckCondition(buffer, types[i], valueCode,
+              isOptional: false);
+          buffer.write(' ? ');
+        }
+        buffer.write('$dartTypeNotNullable.t${i + 1}(');
+        _writeFromJsonExpression(buffer, types[i], valueCode);
+        buffer.write(')');
+
+        if (!isLast) {
+          buffer.write(' : ');
+        } else if (isLast && isOptional) {
+          buffer.write(' : null');
+        }
+      }
+    } else if (type.isSpecType) {
+      if (isOptional) {
+        buffer.write('$valueCode == null ? null : ');
+      }
+      buffer.write(
+          '$dartTypeNotNullable.fromJson($valueCode as Map<String, Object?>)');
+    } else {
+      throw 'Unable to type check $valueCode against $type';
+    }
+  }
+
+  /// Writes a static `fromJson` method that converts an object into a spec type
+  /// by calling its fromMap constructor.
+  ///
+  /// This is a helper method used as a tear-off since the constructor cannot be.
+  void _writeFromJsonStaticMethod(
+    IndentableStringBuffer buffer,
+    String name,
+  ) =>
+      buffer.writeIndentedln(
+          'static $name fromJson(Map<String, Object?> obj) => $name.fromMap(obj);');
+
+  /// Writes a fromMap constructor to construct an object from a JSON map.
+  void _writeFromMapConstructor(
+    IndentableStringBuffer buffer,
+    String name,
+    JsonType type,
+    Map<String, JsonType> properties, {
+    bool callSuper = false,
+  }) {
+    buffer.writeIndented('$name.fromMap(Map<String, Object?> obj)');
+    if (properties.isNotEmpty || callSuper) {
+      buffer
+        ..writeln(':')
+        ..indent();
+      var isFirst = true;
+      for (final entry in properties.entries.sortedBy((e) => e.key)) {
+        if (isFirst) {
+          isFirst = false;
+        } else {
+          buffer.writeln(',');
+        }
+
+        final propertyName = entry.key;
+        final fieldName = _dartSafeName(propertyName);
+        final propertyType = entry.value;
+        final isOptional = !type.requiresField(propertyName);
+
+        buffer.writeIndented('$fieldName = ');
+        _writeFromJsonExpression(buffer, propertyType, "obj['$propertyName']",
+            isOptional: isOptional);
+      }
+      if (callSuper) {
+        if (!isFirst) {
+          buffer.writeln(',');
+        }
+        buffer.writeIndented('super.fromMap(obj)');
+      }
+      buffer.outdent();
+    }
+    buffer.writeln(';');
+  }
+
+  /// Writes a toJson method to construct a JSON map for this class, recursively
+  /// calling through base classes.
+  void _writeToJsonMethod(
+    IndentableStringBuffer buffer,
+    String name,
+    JsonType type,
+    Map<String, JsonType> properties, {
+    bool callSuper = false,
+  }) {
+    if (callSuper) {
+      buffer.writeIndentedln('@override');
+    }
+    buffer
+      ..writeIndentedln('Map<String, Object?> toJson() => {')
+      ..indent();
+    if (callSuper) {
+      buffer.writeIndentedln('...super.toJson(),');
+    }
+    for (final entry in properties.entries.sortedBy((e) => e.key)) {
+      final propertyName = entry.key;
+      final fieldName = _dartSafeName(propertyName);
+      final isOptional = !type.requiresField(propertyName);
+      buffer.writeIndented('');
+      if (isOptional) {
+        buffer.write('if ($fieldName != null) ');
+      }
+      buffer.writeln("'$propertyName': $fieldName, ");
+    }
+    buffer
+      ..outdent()
+      ..writeIndentedln('};');
+  }
+
+  /// Writes an expression that checks whether [valueCode] represents a [type].
+  void _writeTypeCheckCondition(
+      IndentableStringBuffer buffer, JsonType type, String valueCode,
+      {required bool isOptional, bool invert = false}) {
+    final dartType = type.asDartType(isOptional: isOptional);
+
+    // When the expression is inverted, invert the operators so the generated
+    // code is easier to read.
+    final opBang = invert ? '!' : '';
+    final opTrue = invert ? 'false' : 'true';
+    final opIs = invert ? 'is!' : 'is';
+    final opEquals = invert ? '!=' : '==';
+    final opAnd = invert ? '||' : '&&';
+    final opOr = invert ? '&&' : '||';
+    final opEvery = invert ? 'any' : 'every';
+
+    if (type.isAny) {
+      buffer.write(opTrue);
+    } else if (dartType == 'Null') {
+      buffer.write('$valueCode $opEquals null');
+    } else if (type.isSimple) {
+      buffer.write('$valueCode $opIs $dartType');
+    } else if (type.isList) {
+      buffer.write('($valueCode $opIs List');
+      buffer.write(' $opAnd ($valueCode.$opEvery((item) => ');
+      _writeTypeCheckCondition(buffer, type.items!, 'item',
+          isOptional: false, invert: invert);
+      buffer.write('))');
+      buffer.write(')');
+    } else if (type.isUnion) {
+      final types = type.unionTypes;
+      // To type check a union, we recursively check against each of its types.
+      buffer.write('(');
+      for (var i = 0; i < types.length; i++) {
+        if (i != 0) {
+          buffer.write(' $opOr ');
+        }
+        _writeTypeCheckCondition(buffer, types[i], valueCode,
+            isOptional: false, invert: invert);
+      }
+      if (isOptional) {
+        buffer.write(' $opOr $valueCode $opEquals null');
+      }
+      buffer.write(')');
+    } else if (type.isSpecType) {
+      buffer.write('$opBang$dartType.canParse($valueCode)');
+    } else {
+      throw 'Unable to type check $valueCode against $type';
+    }
+  }
+
+  /// Writes the description for [type], looking at the base type from the
+  /// DAP spec if necessary.
+  void _writeTypeDescription(IndentableStringBuffer buffer, JsonType type) {
+    // In the DAP spec, many of the descriptions are on one of the allOf types
+    // rather than the type itself.
+    final description = type.description ??
+        type.allOf
+            ?.firstWhereOrNull((element) => element.description != null)
+            ?.description;
+
+    _writeDescription(buffer, description);
+  }
+}
+
+/// A [StringBuffer] with support for indenting.
+class IndentableStringBuffer extends StringBuffer {
+  int _indentLevel = 0;
+  final int _indentSpaces = 2;
+
+  int get totalIndent => _indentLevel * _indentSpaces;
+  String get _indentString => ' ' * totalIndent;
+
+  void indent() => _indentLevel++;
+  void outdent() => _indentLevel--;
+
+  void writeIndented(Object obj) {
+    write(_indentString);
+    write(obj);
+  }
+
+  void writeIndentedln(Object obj) {
+    write(_indentString);
+    writeln(obj);
+  }
+}
diff --git a/dds/tool/dap/external_dap_spec/debugAdapterProtocol.json b/dds/tool/dap/external_dap_spec/debugAdapterProtocol.json
new file mode 100644
index 0000000..df4f9ad
--- /dev/null
+++ b/dds/tool/dap/external_dap_spec/debugAdapterProtocol.json
@@ -0,0 +1,3947 @@
+{
+	"$schema": "http://json-schema.org/draft-04/schema#",
+	"title": "Debug Adapter Protocol",
+	"description": "The Debug Adapter Protocol defines the protocol used between an editor or IDE and a debugger or runtime.",
+	"type": "object",
+
+
+	"definitions": {
+
+		"ProtocolMessage": {
+			"type": "object",
+			"title": "Base Protocol",
+			"description": "Base class of requests, responses, and events.",
+			"properties": {
+				"seq": {
+					"type": "integer",
+					"description": "Sequence number (also known as message ID). For protocol messages of type 'request' this ID can be used to cancel the request."
+				},
+				"type": {
+					"type": "string",
+					"description": "Message type.",
+					"_enum": [ "request", "response", "event" ]
+				}
+			},
+			"required": [ "seq", "type" ]
+		},
+
+		"Request": {
+			"allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
+				"type": "object",
+				"description": "A client or debug adapter initiated request.",
+				"properties": {
+					"type": {
+						"type": "string",
+						"enum": [ "request" ]
+					},
+					"command": {
+						"type": "string",
+						"description": "The command to execute."
+					},
+					"arguments": {
+						"type": [ "array", "boolean", "integer", "null", "number" , "object", "string" ],
+						"description": "Object containing arguments for the command."
+					}
+				},
+				"required": [ "type", "command" ]
+			}]
+		},
+
+		"Event": {
+			"allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
+				"type": "object",
+				"description": "A debug adapter initiated event.",
+				"properties": {
+					"type": {
+						"type": "string",
+						"enum": [ "event" ]
+					},
+					"event": {
+						"type": "string",
+						"description": "Type of event."
+					},
+					"body": {
+						"type": [ "array", "boolean", "integer", "null", "number" , "object", "string" ],
+						"description": "Event-specific information."
+					}
+				},
+				"required": [ "type", "event" ]
+			}]
+		},
+
+		"Response": {
+			"allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
+				"type": "object",
+				"description": "Response for a request.",
+				"properties": {
+					"type": {
+						"type": "string",
+						"enum": [ "response" ]
+					},
+					"request_seq": {
+						"type": "integer",
+						"description": "Sequence number of the corresponding request."
+					},
+					"success": {
+						"type": "boolean",
+						"description": "Outcome of the request.\nIf true, the request was successful and the 'body' attribute may contain the result of the request.\nIf the value is false, the attribute 'message' contains the error in short form and the 'body' may contain additional information (see 'ErrorResponse.body.error')."
+					},
+					"command": {
+						"type": "string",
+						"description": "The command requested."
+					},
+					"message": {
+						"type": "string",
+						"description": "Contains the raw error in short form if 'success' is false.\nThis raw error might be interpreted by the frontend and is not shown in the UI.\nSome predefined values exist.",
+						"_enum": [ "cancelled" ],
+						"enumDescriptions": [
+							"request was cancelled."
+						]
+					},
+					"body": {
+						"type": [ "array", "boolean", "integer", "null", "number" , "object", "string" ],
+						"description": "Contains request result if success is true and optional error details if success is false."
+					}
+				},
+				"required": [ "type", "request_seq", "success", "command" ]
+			}]
+		},
+
+		"ErrorResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "On error (whenever 'success' is false), the body can provide more details.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"error": {
+								"$ref": "#/definitions/Message",
+								"description": "An optional, structured error message."
+							}
+						}
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"CancelRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The 'cancel' request is used by the frontend in two situations:\n- to indicate that it is no longer interested in the result produced by a specific request issued earlier\n- to cancel a progress sequence. Clients should only call this request if the capability 'supportsCancelRequest' is true.\nThis request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honouring this request but there are no guarantees.\nThe 'cancel' request may return an error if it could not cancel an operation but a frontend should refrain from presenting this error to end users.\nA frontend client should only call this request if the capability 'supportsCancelRequest' is true.\nThe request that got canceled still needs to send a response back. This can either be a normal result ('success' attribute true)\nor an error response ('success' attribute false and the 'message' set to 'cancelled').\nReturning partial results from a cancelled request is possible but please note that a frontend client has no generic way for detecting that a response is partial or not.\n The progress that got cancelled still needs to send a 'progressEnd' event back.\n A client should not assume that progress just got cancelled after sending the 'cancel' request.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "cancel" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/CancelArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"CancelArguments": {
+			"type": "object",
+			"description": "Arguments for 'cancel' request.",
+			"properties": {
+				"requestId": {
+					"type": "integer",
+					"description": "The ID (attribute 'seq') of the request to cancel. If missing no request is cancelled.\nBoth a 'requestId' and a 'progressId' can be specified in one request."
+				},
+				"progressId": {
+					"type": "string",
+					"description": "The ID (attribute 'progressId') of the progress to cancel. If missing no progress is cancelled.\nBoth a 'requestId' and a 'progressId' can be specified in one request."
+				}
+			}
+		},
+		"CancelResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'cancel' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"InitializedEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"title": "Events",
+				"description": "This event indicates that the debug adapter is ready to accept configuration requests (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest).\nA debug adapter is expected to send this event when it is ready to accept configuration requests (but not before the 'initialize' request has finished).\nThe sequence of events/requests is as follows:\n- adapters sends 'initialized' event (after the 'initialize' request has returned)\n- frontend sends zero or more 'setBreakpoints' requests\n- frontend sends one 'setFunctionBreakpoints' request (if capability 'supportsFunctionBreakpoints' is true)\n- frontend sends a 'setExceptionBreakpoints' request if one or more 'exceptionBreakpointFilters' have been defined (or if 'supportsConfigurationDoneRequest' is not defined or false)\n- frontend sends other future configuration requests\n- frontend sends one 'configurationDone' request to indicate the end of the configuration.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "initialized" ]
+					}
+				},
+				"required": [ "event" ]
+			}]
+		},
+
+		"StoppedEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that the execution of the debuggee has stopped due to some condition.\nThis can be caused by a break point previously set, a stepping request has completed, by executing a debugger statement etc.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "stopped" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"reason": {
+								"type": "string",
+								"description": "The reason for the event.\nFor backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated).",
+								"_enum": [ "step", "breakpoint", "exception", "pause", "entry", "goto", "function breakpoint", "data breakpoint", "instruction breakpoint" ]
+							},
+							"description": {
+								"type": "string",
+								"description": "The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated."
+							},
+							"threadId": {
+								"type": "integer",
+								"description": "The thread which was stopped."
+							},
+							"preserveFocusHint": {
+								"type": "boolean",
+								"description": "A value of true hints to the frontend that this event should not change the focus."
+							},
+							"text": {
+								"type": "string",
+								"description": "Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI."
+							},
+							"allThreadsStopped": {
+								"type": "boolean",
+								"description": "If 'allThreadsStopped' is true, a debug adapter can announce that all threads have stopped.\n- The client should use this information to enable that all threads can be expanded to access their stacktraces.\n- If the attribute is missing or false, only the thread with the given threadId can be expanded."
+							},
+							"hitBreakpointIds": {
+								"type": "array",
+								"items": {
+									"type": "integer"
+								},
+								"description": "Ids of the breakpoints that triggered the event. In most cases there will be only a single breakpoint but here are some examples for multiple breakpoints:\n- Different types of breakpoints map to the same location.\n- Multiple source breakpoints get collapsed to the same instruction by the compiler/runtime.\n- Multiple function breakpoints with different function names map to the same location."
+							}
+						},
+						"required": [ "reason" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ContinuedEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that the execution of the debuggee has continued.\nPlease note: a debug adapter is not expected to send this event in response to a request that implies that execution continues, e.g. 'launch' or 'continue'.\nIt is only necessary to send a 'continued' event if there was no previous request that implied this.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "continued" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"threadId": {
+								"type": "integer",
+								"description": "The thread which was continued."
+							},
+							"allThreadsContinued": {
+								"type": "boolean",
+								"description": "If 'allThreadsContinued' is true, a debug adapter can announce that all threads have continued."
+							}
+						},
+						"required": [ "threadId" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ExitedEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that the debuggee has exited and returns its exit code.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "exited" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"exitCode": {
+								"type": "integer",
+								"description": "The exit code returned from the debuggee."
+							}
+						},
+						"required": [ "exitCode" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"TerminatedEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that debugging of the debuggee has terminated. This does **not** mean that the debuggee itself has exited.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "terminated" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"restart": {
+								"type": [ "array", "boolean", "integer", "null", "number", "object", "string" ],
+								"description": "A debug adapter may set 'restart' to true (or to an arbitrary object) to request that the front end restarts the session.\nThe value is not interpreted by the client and passed unmodified as an attribute '__restart' to the 'launch' and 'attach' requests."
+							}
+						}
+					}
+				},
+				"required": [ "event" ]
+			}]
+		},
+
+		"ThreadEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that a thread has started or exited.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "thread" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"reason": {
+								"type": "string",
+								"description": "The reason for the event.",
+								"_enum": [ "started", "exited" ]
+							},
+							"threadId": {
+								"type": "integer",
+								"description": "The identifier of the thread."
+							}
+						},
+						"required": ["reason", "threadId"]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"OutputEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that the target has produced some output.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "output" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"category": {
+								"type": "string",
+								"description": "The output category. If not specified, 'console' is assumed.",
+								"_enum": [ "console", "stdout", "stderr", "telemetry" ]
+							},
+							"output": {
+								"type": "string",
+								"description": "The output to report."
+							},
+							"group": {
+								"type": "string",
+								"description": "Support for keeping an output log organized by grouping related messages.",
+								"enum": [ "start", "startCollapsed", "end" ],
+								"enumDescriptions": [
+									"Start a new group in expanded mode. Subsequent output events are members of the group and should be shown indented.\nThe 'output' attribute becomes the name of the group and is not indented.",
+									"Start a new group in collapsed mode. Subsequent output events are members of the group and should be shown indented (as soon as the group is expanded).\nThe 'output' attribute becomes the name of the group and is not indented.",
+									"End the current group and decreases the indentation of subsequent output events.\nA non empty 'output' attribute is shown as the unindented end of the group."
+								]
+							},
+							"variablesReference": {
+								"type": "integer",
+								"description": "If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"source": {
+								"$ref": "#/definitions/Source",
+								"description": "An optional source location where the output was produced."
+							},
+							"line": {
+								"type": "integer",
+								"description": "An optional source location line where the output was produced."
+							},
+							"column": {
+								"type": "integer",
+								"description": "An optional source location column where the output was produced."
+							},
+							"data": {
+								"type": [ "array", "boolean", "integer", "null", "number" , "object", "string" ],
+								"description": "Optional data to report. For the 'telemetry' category the data will be sent to telemetry, for the other categories the data is shown in JSON format."
+							}
+						},
+						"required": ["output"]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"BreakpointEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that some information about a breakpoint has changed.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "breakpoint" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"reason": {
+								"type": "string",
+								"description": "The reason for the event.",
+								"_enum": [ "changed", "new", "removed" ]
+							},
+							"breakpoint": {
+								"$ref": "#/definitions/Breakpoint",
+								"description": "The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values."
+							}
+						},
+						"required": [ "reason", "breakpoint" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ModuleEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that some information about a module has changed.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "module" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"reason": {
+								"type": "string",
+								"description": "The reason for the event.",
+								"enum": [ "new", "changed", "removed" ]
+							},
+							"module": {
+								"$ref": "#/definitions/Module",
+								"description": "The new, changed, or removed module. In case of 'removed' only the module id is used."
+							}
+						},
+						"required": [ "reason", "module" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"LoadedSourceEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that some source has been added, changed, or removed from the set of all loaded sources.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "loadedSource" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"reason": {
+								"type": "string",
+								"description": "The reason for the event.",
+								"enum": [ "new", "changed", "removed" ]
+							},
+							"source": {
+								"$ref": "#/definitions/Source",
+								"description": "The new, changed, or removed source."
+							}
+						},
+						"required": [ "reason", "source" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ProcessEvent": {
+			"allOf": [
+				{ "$ref": "#/definitions/Event" },
+				{
+					"type": "object",
+					"description": "The event indicates that the debugger has begun debugging a new process. Either one that it has launched, or one that it has attached to.",
+					"properties": {
+						"event": {
+							"type": "string",
+							"enum": [ "process" ]
+						},
+						"body": {
+							"type": "object",
+							"properties": {
+								"name": {
+									"type": "string",
+									"description": "The logical name of the process. This is usually the full path to process's executable file. Example: /home/example/myproj/program.js."
+								},
+								"systemProcessId": {
+									"type": "integer",
+									"description": "The system process id of the debugged process. This property will be missing for non-system processes."
+								},
+								"isLocalProcess": {
+									"type": "boolean",
+									"description": "If true, the process is running on the same computer as the debug adapter."
+								},
+								"startMethod": {
+									"type": "string",
+									"enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
+									"description": "Describes how the debug engine started debugging this process.",
+									"enumDescriptions": [
+										"Process was launched under the debugger.",
+										"Debugger attached to an existing process.",
+										"A project launcher component has launched a new process in a suspended state and then asked the debugger to attach."
+									]
+								},
+								"pointerSize": {
+									"type": "integer",
+									"description": "The size of a pointer or address for this process, in bits. This value may be used by clients when formatting addresses for display."
+								}
+							},
+							"required": [ "name" ]
+						}
+					},
+					"required": [ "event", "body" ]
+				}
+			]
+		},
+
+		"CapabilitiesEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event indicates that one or more capabilities have changed.\nSince the capabilities are dependent on the frontend and its UI, it might not be possible to change that at random times (or too late).\nConsequently this event has a hint characteristic: a frontend can only be expected to make a 'best effort' in honouring individual capabilities but there are no guarantees.\nOnly changed capabilities need to be included, all other capabilities keep their values.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "capabilities" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"capabilities": {
+								"$ref": "#/definitions/Capabilities",
+								"description": "The set of updated capabilities."
+							}
+						},
+						"required": [ "capabilities" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ProgressStartEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event signals that a long running operation is about to start and\nprovides additional information for the client to set up a corresponding progress and cancellation UI.\nThe client is free to delay the showing of the UI in order to reduce flicker.\nThis event should only be sent if the client has passed the value true for the 'supportsProgressReporting' capability of the 'initialize' request.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "progressStart" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"progressId": {
+								"type": "string",
+								"description": "An ID that must be used in subsequent 'progressUpdate' and 'progressEnd' events to make them refer to the same progress reporting.\nIDs must be unique within a debug session."
+							},
+							"title": {
+								"type": "string",
+								"description": "Mandatory (short) title of the progress reporting. Shown in the UI to describe the long running operation."
+							},
+							"requestId": {
+								"type": "integer",
+								"description": "The request ID that this progress report is related to. If specified a debug adapter is expected to emit\nprogress events for the long running request until the request has been either completed or cancelled.\nIf the request ID is omitted, the progress report is assumed to be related to some general activity of the debug adapter."
+							},
+							"cancellable": {
+								"type": "boolean",
+								"description": "If true, the request that reports progress may be canceled with a 'cancel' request.\nSo this property basically controls whether the client should use UX that supports cancellation.\nClients that don't support cancellation are allowed to ignore the setting."
+							},
+							"message": {
+								"type": "string",
+								"description": "Optional, more detailed progress message."
+							},
+							"percentage": {
+								"type": "number",
+								"description": "Optional progress percentage to display (value range: 0 to 100). If omitted no percentage will be shown."
+							}
+						},
+						"required": [ "progressId", "title" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ProgressUpdateEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event signals that the progress reporting needs to updated with a new message and/or percentage.\nThe client does not have to update the UI immediately, but the clients needs to keep track of the message and/or percentage values.\nThis event should only be sent if the client has passed the value true for the 'supportsProgressReporting' capability of the 'initialize' request.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "progressUpdate" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"progressId": {
+								"type": "string",
+								"description": "The ID that was introduced in the initial 'progressStart' event."
+							},
+							"message": {
+								"type": "string",
+								"description": "Optional, more detailed progress message. If omitted, the previous message (if any) is used."
+							},
+							"percentage": {
+								"type": "number",
+								"description": "Optional progress percentage to display (value range: 0 to 100). If omitted no percentage will be shown."
+							}
+						},
+						"required": [ "progressId" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"ProgressEndEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "The event signals the end of the progress reporting with an optional final message.\nThis event should only be sent if the client has passed the value true for the 'supportsProgressReporting' capability of the 'initialize' request.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "progressEnd" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"progressId": {
+								"type": "string",
+								"description": "The ID that was introduced in the initial 'ProgressStartEvent'."
+							},
+							"message": {
+								"type": "string",
+								"description": "Optional, more detailed progress message. If omitted, the previous message (if any) is used."
+							}
+						},
+						"required": [ "progressId" ]
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"InvalidatedEvent": {
+			"allOf": [ { "$ref": "#/definitions/Event" }, {
+				"type": "object",
+				"description": "This event signals that some state in the debug adapter has changed and requires that the client needs to re-render the data snapshot previously requested.\nDebug adapters do not have to emit this event for runtime changes like stopped or thread events because in that case the client refetches the new state anyway. But the event can be used for example to refresh the UI after rendering formatting has changed in the debug adapter.\nThis event should only be sent if the debug adapter has received a value true for the 'supportsInvalidatedEvent' capability of the 'initialize' request.",
+				"properties": {
+					"event": {
+						"type": "string",
+						"enum": [ "invalidated" ]
+					},
+					"body": {
+						"type": "object",
+						"properties": {
+							"areas": {
+								"type": "array",
+								"description": "Optional set of logical areas that got invalidated. This property has a hint characteristic: a client can only be expected to make a 'best effort' in honouring the areas but there are no guarantees. If this property is missing, empty, or if values are not understand the client should assume a single value 'all'.",
+								"items": {
+									"$ref": "#/definitions/InvalidatedAreas"
+								}
+							},
+							"threadId": {
+								"type": "integer",
+								"description": "If specified, the client only needs to refetch data related to this thread."
+							},
+							"stackFrameId": {
+								"type": "integer",
+								"description": "If specified, the client only needs to refetch data related to this stack frame (and the 'threadId' is ignored)."
+							}
+						}
+					}
+				},
+				"required": [ "event", "body" ]
+			}]
+		},
+
+		"RunInTerminalRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"title": "Reverse Requests",
+				"description": "This optional request is sent from the debug adapter to the client to run a command in a terminal.\nThis is typically used to launch the debuggee in a terminal provided by the client.\nThis request should only be called if the client has passed the value true for the 'supportsRunInTerminalRequest' capability of the 'initialize' request.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "runInTerminal" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/RunInTerminalRequestArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"RunInTerminalRequestArguments": {
+			"type": "object",
+			"description": "Arguments for 'runInTerminal' request.",
+			"properties": {
+				"kind": {
+					"type": "string",
+					"enum": [ "integrated", "external" ],
+					"description": "What kind of terminal to launch."
+				},
+				"title": {
+					"type": "string",
+					"description": "Optional title of the terminal."
+				},
+				"cwd": {
+					"type": "string",
+					"description": "Working directory for the command. For non-empty, valid paths this typically results in execution of a change directory command."
+				},
+				"args": {
+					"type": "array",
+					"items": {
+						"type": "string"
+					},
+					"description": "List of arguments. The first argument is the command to run."
+				},
+				"env": {
+					"type": "object",
+					"description": "Environment key-value pairs that are added to or removed from the default environment.",
+					"additionalProperties": {
+						"type": [ "string", "null" ],
+						"description": "Proper values must be strings. A value of 'null' removes the variable from the environment."
+					}
+				}
+			},
+			"required": [ "args", "cwd" ]
+		},
+		"RunInTerminalResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'runInTerminal' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"processId": {
+								"type": "integer",
+								"description": "The process ID. The value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"shellProcessId": {
+								"type": "integer",
+								"description": "The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31-1)."
+							}
+						}
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"InitializeRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"title": "Requests",
+				"description": "The 'initialize' request is sent as the first request from the client to the debug adapter\nin order to configure it with client capabilities and to retrieve capabilities from the debug adapter.\nUntil the debug adapter has responded to with an 'initialize' response, the client must not send any additional requests or events to the debug adapter.\nIn addition the debug adapter is not allowed to send any requests or events to the client until it has responded with an 'initialize' response.\nThe 'initialize' request may only be sent once.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "initialize" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/InitializeRequestArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"InitializeRequestArguments": {
+			"type": "object",
+			"description": "Arguments for 'initialize' request.",
+			"properties": {
+				"clientID": {
+					"type": "string",
+					"description": "The ID of the (frontend) client using this adapter."
+				},
+				"clientName": {
+					"type": "string",
+					"description": "The human readable name of the (frontend) client using this adapter."
+				},
+				"adapterID": {
+					"type": "string",
+					"description": "The ID of the debug adapter."
+				},
+				"locale": {
+					"type": "string",
+					"description": "The ISO-639 locale of the (frontend) client using this adapter, e.g. en-US or de-CH."
+				},
+				"linesStartAt1": {
+					"type": "boolean",
+					"description": "If true all line numbers are 1-based (default)."
+				},
+				"columnsStartAt1": {
+					"type": "boolean",
+					"description": "If true all column numbers are 1-based (default)."
+				},
+				"pathFormat": {
+					"type": "string",
+					"_enum": [ "path", "uri" ],
+					"description": "Determines in what format paths are specified. The default is 'path', which is the native format."
+				},
+				"supportsVariableType": {
+					"type": "boolean",
+					"description": "Client supports the optional type attribute for variables."
+				},
+				"supportsVariablePaging": {
+					"type": "boolean",
+					"description": "Client supports the paging of variables."
+				},
+				"supportsRunInTerminalRequest": {
+					"type": "boolean",
+					"description": "Client supports the runInTerminal request."
+				},
+				"supportsMemoryReferences": {
+					"type": "boolean",
+					"description": "Client supports memory references."
+				},
+				"supportsProgressReporting": {
+					"type": "boolean",
+					"description": "Client supports progress reporting."
+				},
+				"supportsInvalidatedEvent": {
+					"type": "boolean",
+					"description": "Client supports the invalidated event."
+				}
+			},
+			"required": [ "adapterID" ]
+		},
+		"InitializeResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'initialize' request.",
+				"properties": {
+					"body": {
+						"$ref": "#/definitions/Capabilities",
+						"description": "The capabilities of this debug adapter."
+					}
+				}
+			}]
+		},
+
+		"ConfigurationDoneRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "This optional request indicates that the client has finished initialization of the debug adapter.\nSo it is the last request in the sequence of configuration requests (which was started by the 'initialized' event).\nClients should only call this request if the capability 'supportsConfigurationDoneRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "configurationDone" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ConfigurationDoneArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"ConfigurationDoneArguments": {
+			"type": "object",
+			"description": "Arguments for 'configurationDone' request."
+		},
+		"ConfigurationDoneResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'configurationDone' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"LaunchRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "This launch request is sent from the client to the debug adapter to start the debuggee with or without debugging (if 'noDebug' is true).\nSince launching is debugger/runtime specific, the arguments for this request are not part of this specification.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "launch" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/LaunchRequestArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"LaunchRequestArguments": {
+			"type": "object",
+			"description": "Arguments for 'launch' request. Additional attributes are implementation specific.",
+			"properties": {
+				"noDebug": {
+					"type": "boolean",
+					"description": "If noDebug is true the launch request should launch the program without enabling debugging."
+				},
+				"__restart": {
+					"type": [ "array", "boolean", "integer", "null", "number", "object", "string" ],
+					"description": "Optional data from the previous, restarted session.\nThe data is sent as the 'restart' attribute of the 'terminated' event.\nThe client should leave the data intact."
+				}
+			}
+		},
+		"LaunchResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'launch' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"AttachRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The attach request is sent from the client to the debug adapter to attach to a debuggee that is already running.\nSince attaching is debugger/runtime specific, the arguments for this request are not part of this specification.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "attach" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/AttachRequestArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"AttachRequestArguments": {
+			"type": "object",
+			"description": "Arguments for 'attach' request. Additional attributes are implementation specific.",
+			"properties": {
+				"__restart": {
+					"type": [ "array", "boolean", "integer", "null", "number", "object", "string" ],
+					"description": "Optional data from the previous, restarted session.\nThe data is sent as the 'restart' attribute of the 'terminated' event.\nThe client should leave the data intact."
+				}
+			}
+		},
+		"AttachResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'attach' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"RestartRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Restarts a debug session. Clients should only call this request if the capability 'supportsRestartRequest' is true.\nIf the capability is missing or has the value false, a typical client will emulate 'restart' by terminating the debug adapter first and then launching it anew.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "restart" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/RestartArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"RestartArguments": {
+			"type": "object",
+			"description": "Arguments for 'restart' request.",
+			"properties": {
+				"arguments": {
+					"oneOf": [
+						{ "$ref": "#/definitions/LaunchRequestArguments" },
+						{ "$ref": "#/definitions/AttachRequestArguments" }
+					],
+					"description": "The latest version of the 'launch' or 'attach' configuration."
+				}
+			}
+		},
+		"RestartResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'restart' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"DisconnectRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The 'disconnect' request is sent from the client to the debug adapter in order to stop debugging.\nIt asks the debug adapter to disconnect from the debuggee and to terminate the debug adapter.\nIf the debuggee has been started with the 'launch' request, the 'disconnect' request terminates the debuggee.\nIf the 'attach' request was used to connect to the debuggee, 'disconnect' does not terminate the debuggee.\nThis behavior can be controlled with the 'terminateDebuggee' argument (if supported by the debug adapter).",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "disconnect" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/DisconnectArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"DisconnectArguments": {
+			"type": "object",
+			"description": "Arguments for 'disconnect' request.",
+			"properties": {
+				"restart": {
+					"type": "boolean",
+					"description": "A value of true indicates that this 'disconnect' request is part of a restart sequence."
+				},
+				"terminateDebuggee": {
+					"type": "boolean",
+					"description": "Indicates whether the debuggee should be terminated when the debugger is disconnected.\nIf unspecified, the debug adapter is free to do whatever it thinks is best.\nThe attribute is only honored by a debug adapter if the capability 'supportTerminateDebuggee' is true."
+				},
+				"suspendDebuggee": {
+					"type": "boolean",
+					"description": "Indicates whether the debuggee should stay suspended when the debugger is disconnected.\nIf unspecified, the debuggee should resume execution.\nThe attribute is only honored by a debug adapter if the capability 'supportSuspendDebuggee' is true."
+				}
+			}
+		},
+		"DisconnectResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'disconnect' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"TerminateRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The 'terminate' request is sent from the client to the debug adapter in order to give the debuggee a chance for terminating itself.\nClients should only call this request if the capability 'supportsTerminateRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "terminate" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/TerminateArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"TerminateArguments": {
+			"type": "object",
+			"description": "Arguments for 'terminate' request.",
+			"properties": {
+				"restart": {
+					"type": "boolean",
+					"description": "A value of true indicates that this 'terminate' request is part of a restart sequence."
+				}
+			}
+		},
+		"TerminateResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'terminate' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"BreakpointLocationsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The 'breakpointLocations' request returns all possible locations for source breakpoints in a given range.\nClients should only call this request if the capability 'supportsBreakpointLocationsRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "breakpointLocations" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/BreakpointLocationsArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+
+		},
+		"BreakpointLocationsArguments": {
+			"type": "object",
+			"description": "Arguments for 'breakpointLocations' request.",
+			"properties": {
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified."
+				},
+				"line": {
+					"type": "integer",
+					"description": "Start line of range to search possible breakpoint locations in. If only the line is specified, the request returns all possible locations in that line."
+				},
+				"column": {
+					"type": "integer",
+					"description": "Optional start column of range to search possible breakpoint locations in. If no start column is given, the first column in the start line is assumed."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "Optional end line of range to search possible breakpoint locations in. If no end line is given, then the end line is assumed to be the start line."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "Optional end column of range to search possible breakpoint locations in. If no end column is given, then it is assumed to be in the last column of the end line."
+				}
+			},
+			"required": [ "source", "line" ]
+		},
+		"BreakpointLocationsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'breakpointLocations' request.\nContains possible locations for source breakpoints.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"breakpoints": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/BreakpointLocation"
+								},
+								"description": "Sorted set of possible breakpoint locations."
+							}
+						},
+						"required": [ "breakpoints" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetBreakpointsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Sets multiple breakpoints for a single source and clears all previous breakpoints in that source.\nTo clear all breakpoint for a source, specify an empty array.\nWhen a breakpoint is hit, a 'stopped' event (with reason 'breakpoint') is generated.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "setBreakpoints" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetBreakpointsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SetBreakpointsArguments": {
+			"type": "object",
+			"description": "Arguments for 'setBreakpoints' request.",
+			"properties": {
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified."
+				},
+				"breakpoints": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/SourceBreakpoint"
+					},
+					"description": "The code locations of the breakpoints."
+				},
+				"lines": {
+					"type": "array",
+					"items": {
+						"type": "integer"
+					},
+					"description": "Deprecated: The code locations of the breakpoints."
+				},
+				"sourceModified": {
+					"type": "boolean",
+					"description": "A value of true indicates that the underlying source has been modified which results in new breakpoint locations."
+				}
+			},
+			"required": [ "source" ]
+		},
+		"SetBreakpointsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'setBreakpoints' request.\nReturned is information about each breakpoint created by this request.\nThis includes the actual code location and whether the breakpoint could be verified.\nThe breakpoints returned are in the same order as the elements of the 'breakpoints'\n(or the deprecated 'lines') array in the arguments.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"breakpoints": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Breakpoint"
+								},
+								"description": "Information about the breakpoints.\nThe array elements are in the same order as the elements of the 'breakpoints' (or the deprecated 'lines') array in the arguments."
+							}
+						},
+						"required": [ "breakpoints" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetFunctionBreakpointsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Replaces all existing function breakpoints with new function breakpoints.\nTo clear all function breakpoints, specify an empty array.\nWhen a function breakpoint is hit, a 'stopped' event (with reason 'function breakpoint') is generated.\nClients should only call this request if the capability 'supportsFunctionBreakpoints' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "setFunctionBreakpoints" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetFunctionBreakpointsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SetFunctionBreakpointsArguments": {
+			"type": "object",
+			"description": "Arguments for 'setFunctionBreakpoints' request.",
+			"properties": {
+				"breakpoints": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/FunctionBreakpoint"
+					},
+					"description": "The function names of the breakpoints."
+				}
+			},
+			"required": [ "breakpoints" ]
+		},
+		"SetFunctionBreakpointsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'setFunctionBreakpoints' request.\nReturned is information about each breakpoint created by this request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"breakpoints": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Breakpoint"
+								},
+								"description": "Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array."
+							}
+						},
+						"required": [ "breakpoints" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetExceptionBreakpointsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request configures the debuggers response to thrown exceptions.\nIf an exception is configured to break, a 'stopped' event is fired (with reason 'exception').\nClients should only call this request if the capability 'exceptionBreakpointFilters' returns one or more filters.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "setExceptionBreakpoints" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetExceptionBreakpointsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SetExceptionBreakpointsArguments": {
+			"type": "object",
+			"description": "Arguments for 'setExceptionBreakpoints' request.",
+			"properties": {
+				"filters": {
+					"type": "array",
+					"items": {
+						"type": "string"
+					},
+					"description": "Set of exception filters specified by their ID. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. The 'filter' and 'filterOptions' sets are additive."
+				},
+				"filterOptions": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ExceptionFilterOptions"
+					},
+					"description": "Set of exception filters and their options. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. This attribute is only honored by a debug adapter if the capability 'supportsExceptionFilterOptions' is true. The 'filter' and 'filterOptions' sets are additive."
+				},
+				"exceptionOptions": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ExceptionOptions"
+					},
+					"description": "Configuration options for selected exceptions.\nThe attribute is only honored by a debug adapter if the capability 'supportsExceptionOptions' is true."
+				}
+			},
+			"required": [ "filters" ]
+		},
+		"SetExceptionBreakpointsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'setExceptionBreakpoints' request.\nThe response contains an array of Breakpoint objects with information about each exception breakpoint or filter. The Breakpoint objects are in the same order as the elements of the 'filters', 'filterOptions', 'exceptionOptions' arrays given as arguments. If both 'filters' and 'filterOptions' are given, the returned array must start with 'filters' information first, followed by 'filterOptions' information.\nThe mandatory 'verified' property of a Breakpoint object signals whether the exception breakpoint or filter could be successfully created and whether the optional condition or hit count expressions are valid. In case of an error the 'message' property explains the problem. An optional 'id' property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events.\nFor backward compatibility both the 'breakpoints' array and the enclosing 'body' are optional. If these elements are missing a client will not be able to show problems for individual exception breakpoints or filters.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"breakpoints": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Breakpoint"
+								},
+								"description": "Information about the exception breakpoints or filters.\nThe breakpoints returned are in the same order as the elements of the 'filters', 'filterOptions', 'exceptionOptions' arrays in the arguments. If both 'filters' and 'filterOptions' are given, the returned array must start with 'filters' information first, followed by 'filterOptions' information."
+							}
+						}
+					}
+				}
+			}]
+		},
+
+		"DataBreakpointInfoRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Obtains information on a possible data breakpoint that could be set on an expression or variable.\nClients should only call this request if the capability 'supportsDataBreakpoints' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "dataBreakpointInfo" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/DataBreakpointInfoArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"DataBreakpointInfoArguments": {
+			"type": "object",
+			"description": "Arguments for 'dataBreakpointInfo' request.",
+			"properties": {
+				"variablesReference": {
+					"type": "integer",
+					"description": "Reference to the Variable container if the data breakpoint is requested for a child of the container."
+				},
+				"name": {
+					"type": "string",
+					"description": "The name of the Variable's child to obtain data breakpoint information for.\nIf variablesReference isn’t provided, this can be an expression."
+				}
+			},
+			"required": [ "name" ]
+		},
+		"DataBreakpointInfoResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'dataBreakpointInfo' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"dataId": {
+								"type": [ "string", "null" ],
+								"description": "An identifier for the data on which a data breakpoint can be registered with the setDataBreakpoints request or null if no data breakpoint is available."
+							},
+							"description": {
+								"type": "string",
+								"description": "UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available."
+							},
+							"accessTypes": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/DataBreakpointAccessType"
+								},
+								"description": "Optional attribute listing the available access types for a potential data breakpoint. A UI frontend could surface this information."
+							},
+							"canPersist": {
+								"type": "boolean",
+								"description": "Optional attribute indicating that a potential data breakpoint could be persisted across sessions."
+							}
+						},
+						"required": [ "dataId", "description" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetDataBreakpointsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Replaces all existing data breakpoints with new data breakpoints.\nTo clear all data breakpoints, specify an empty array.\nWhen a data breakpoint is hit, a 'stopped' event (with reason 'data breakpoint') is generated.\nClients should only call this request if the capability 'supportsDataBreakpoints' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "setDataBreakpoints" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetDataBreakpointsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SetDataBreakpointsArguments": {
+			"type": "object",
+			"description": "Arguments for 'setDataBreakpoints' request.",
+			"properties": {
+				"breakpoints": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/DataBreakpoint"
+					},
+					"description": "The contents of this array replaces all existing data breakpoints. An empty array clears all data breakpoints."
+				}
+			},
+			"required": [ "breakpoints" ]
+		},
+		"SetDataBreakpointsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'setDataBreakpoints' request.\nReturned is information about each breakpoint created by this request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"breakpoints": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Breakpoint"
+								},
+								"description": "Information about the data breakpoints. The array elements correspond to the elements of the input argument 'breakpoints' array."
+							}
+						},
+						"required": [ "breakpoints" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetInstructionBreakpointsRequest": {
+			"allOf": [
+				{ "$ref": "#/definitions/Request" },
+				{
+					"type": "object",
+					"description": "Replaces all existing instruction breakpoints. Typically, instruction breakpoints would be set from a diassembly window. \nTo clear all instruction breakpoints, specify an empty array.\nWhen an instruction breakpoint is hit, a 'stopped' event (with reason 'instruction breakpoint') is generated.\nClients should only call this request if the capability 'supportsInstructionBreakpoints' is true.",
+					"properties": {
+						"command": {
+						"type": "string",
+						"enum": [ "setInstructionBreakpoints" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetInstructionBreakpointsArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"SetInstructionBreakpointsArguments": {
+			"type": "object",
+			"description": "Arguments for 'setInstructionBreakpoints' request",
+			"properties": {
+				"breakpoints": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/InstructionBreakpoint"
+					},
+					"description": "The instruction references of the breakpoints"
+				}
+			},
+			"required": ["breakpoints"]
+		},
+		"SetInstructionBreakpointsResponse": {
+			"allOf": [
+				{ "$ref": "#/definitions/Response" },
+				{
+					"type": "object",
+					"description": "Response to 'setInstructionBreakpoints' request",
+					"properties": {
+						"body": {
+							"type": "object",
+							"properties": {
+								"breakpoints": {
+									"type": "array",
+									"items": {
+										"$ref": "#/definitions/Breakpoint"
+									},
+								"description": "Information about the breakpoints. The array elements correspond to the elements of the 'breakpoints' array."
+							}
+						},
+						"required": [ "breakpoints" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"ContinueRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request starts the debuggee to run again.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "continue" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ContinueArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"ContinueArguments": {
+			"type": "object",
+			"description": "Arguments for 'continue' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Continue execution for the specified thread (if possible).\nIf the backend cannot continue on a single thread but will continue on all threads, it should set the 'allThreadsContinued' attribute in the response to true."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"ContinueResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'continue' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"allThreadsContinued": {
+								"type": "boolean",
+								"description": "If true, the 'continue' request has ignored the specified thread and continued all threads instead.\nIf this attribute is missing a value of 'true' is assumed for backward compatibility."
+							}
+						}
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"NextRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request starts the debuggee to run again for one step.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "next" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/NextArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"NextArguments": {
+			"type": "object",
+			"description": "Arguments for 'next' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Execute 'next' for this thread."
+				},
+				"granularity": {
+					"$ref": "#/definitions/SteppingGranularity",
+					"description": "Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"NextResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'next' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"StepInRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request starts the debuggee to step into a function/method if possible.\nIf it cannot step into a target, 'stepIn' behaves like 'next'.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.\nIf there are multiple function/method calls (or other targets) on the source line,\nthe optional argument 'targetId' can be used to control into which target the 'stepIn' should occur.\nThe list of possible targets for a given source line can be retrieved via the 'stepInTargets' request.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "stepIn" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/StepInArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"StepInArguments": {
+			"type": "object",
+			"description": "Arguments for 'stepIn' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Execute 'stepIn' for this thread."
+				},
+				"targetId": {
+					"type": "integer",
+					"description": "Optional id of the target to step into."
+				},
+				"granularity": {
+					"$ref": "#/definitions/SteppingGranularity",
+					"description": "Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"StepInResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'stepIn' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"StepOutRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request starts the debuggee to run again for one step.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "stepOut" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/StepOutArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"StepOutArguments": {
+			"type": "object",
+			"description": "Arguments for 'stepOut' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Execute 'stepOut' for this thread."
+				},
+				"granularity": {
+					"$ref": "#/definitions/SteppingGranularity",
+					"description": "Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"StepOutResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'stepOut' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"StepBackRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request starts the debuggee to run one step backwards.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'step') after the step has completed.\nClients should only call this request if the capability 'supportsStepBack' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "stepBack" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/StepBackArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"StepBackArguments": {
+			"type": "object",
+			"description": "Arguments for 'stepBack' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Execute 'stepBack' for this thread."
+				},
+				"granularity": {
+					"$ref": "#/definitions/SteppingGranularity",
+					"description": "Optional granularity to step. If no granularity is specified, a granularity of 'statement' is assumed."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"StepBackResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'stepBack' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"ReverseContinueRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request starts the debuggee to run backward.\nClients should only call this request if the capability 'supportsStepBack' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "reverseContinue" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ReverseContinueArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"ReverseContinueArguments": {
+			"type": "object",
+			"description": "Arguments for 'reverseContinue' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Execute 'reverseContinue' for this thread."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"ReverseContinueResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'reverseContinue' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"RestartFrameRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request restarts execution of the specified stackframe.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'restart') after the restart has completed.\nClients should only call this request if the capability 'supportsRestartFrame' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "restartFrame" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/RestartFrameArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"RestartFrameArguments": {
+			"type": "object",
+			"description": "Arguments for 'restartFrame' request.",
+			"properties": {
+				"frameId": {
+					"type": "integer",
+					"description": "Restart this stackframe."
+				}
+			},
+			"required": [ "frameId" ]
+		},
+		"RestartFrameResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'restartFrame' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"GotoRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request sets the location where the debuggee will continue to run.\nThis makes it possible to skip the execution of code or to executed code again.\nThe code between the current location and the goto target is not executed but skipped.\nThe debug adapter first sends the response and then a 'stopped' event with reason 'goto'.\nClients should only call this request if the capability 'supportsGotoTargetsRequest' is true (because only then goto targets exist that can be passed as arguments).",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "goto" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/GotoArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"GotoArguments": {
+			"type": "object",
+			"description": "Arguments for 'goto' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Set the goto target for this thread."
+				},
+				"targetId": {
+					"type": "integer",
+					"description": "The location where the debuggee will continue to run."
+				}
+			},
+			"required": [ "threadId", "targetId" ]
+		},
+		"GotoResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'goto' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"PauseRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request suspends the debuggee.\nThe debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "pause" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/PauseArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"PauseArguments": {
+			"type": "object",
+			"description": "Arguments for 'pause' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Pause execution for this thread."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"PauseResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'pause' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"StackTraceRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request returns a stacktrace from the current execution state of a given thread.\nA client can request all stack frames by omitting the startFrame and levels arguments. For performance conscious clients and if the debug adapter's 'supportsDelayedStackTraceLoading' capability is true, stack frames can be retrieved in a piecemeal way with the startFrame and levels arguments. The response of the stackTrace request may contain a totalFrames property that hints at the total number of frames in the stack. If a client needs this total number upfront, it can issue a request for a single (first) frame and depending on the value of totalFrames decide how to proceed. In any case a client should be prepared to receive less frames than requested, which is an indication that the end of the stack has been reached.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "stackTrace" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/StackTraceArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"StackTraceArguments": {
+			"type": "object",
+			"description": "Arguments for 'stackTrace' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Retrieve the stacktrace for this thread."
+				},
+				"startFrame": {
+					"type": "integer",
+					"description": "The index of the first frame to return; if omitted frames start at 0."
+				},
+				"levels": {
+					"type": "integer",
+					"description": "The maximum number of frames to return. If levels is not specified or 0, all frames are returned."
+				},
+				"format": {
+					"$ref": "#/definitions/StackFrameFormat",
+					"description": "Specifies details on how to format the stack frames.\nThe attribute is only honored by a debug adapter if the capability 'supportsValueFormattingOptions' is true."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"StackTraceResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'stackTrace' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"stackFrames": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/StackFrame"
+								},
+								"description": "The frames of the stackframe. If the array has length zero, there are no stackframes available.\nThis means that there is no location information available."
+							},
+							"totalFrames": {
+								"type": "integer",
+								"description": "The total number of frames available in the stack. If omitted or if totalFrames is larger than the available frames, a client is expected to request frames until a request returns less frames than requested (which indicates the end of the stack). Returning monotonically increasing totalFrames values for subsequent requests can be used to enforce paging in the client."
+							}
+						},
+						"required": [ "stackFrames" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"ScopesRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request returns the variable scopes for a given stackframe ID.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "scopes" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ScopesArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"ScopesArguments": {
+			"type": "object",
+			"description": "Arguments for 'scopes' request.",
+			"properties": {
+				"frameId": {
+					"type": "integer",
+					"description": "Retrieve the scopes for this stackframe."
+				}
+			},
+			"required": [ "frameId" ]
+		},
+		"ScopesResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'scopes' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"scopes": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Scope"
+								},
+								"description": "The scopes of the stackframe. If the array has length zero, there are no scopes available."
+							}
+						},
+						"required": [ "scopes" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"VariablesRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Retrieves all child variables for the given variable reference.\nAn optional filter can be used to limit the fetched children to either named or indexed children.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "variables" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/VariablesArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"VariablesArguments": {
+			"type": "object",
+			"description": "Arguments for 'variables' request.",
+			"properties": {
+				"variablesReference": {
+					"type": "integer",
+					"description": "The Variable reference."
+				},
+				"filter": {
+					"type": "string",
+					"enum": [ "indexed", "named" ],
+					"description": "Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched."
+				},
+				"start": {
+					"type": "integer",
+					"description": "The index of the first variable to return; if omitted children start at 0."
+				},
+				"count": {
+					"type": "integer",
+					"description": "The number of variables to return. If count is missing or 0, all variables are returned."
+				},
+				"format": {
+					"$ref": "#/definitions/ValueFormat",
+					"description": "Specifies details on how to format the Variable values.\nThe attribute is only honored by a debug adapter if the capability 'supportsValueFormattingOptions' is true."
+				}
+			},
+			"required": [ "variablesReference" ]
+		},
+		"VariablesResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'variables' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"variables": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Variable"
+								},
+								"description": "All (or a range) of variables for the given variable reference."
+							}
+						},
+						"required": [ "variables" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetVariableRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Set the variable with the given name in the variable container to a new value. Clients should only call this request if the capability 'supportsSetVariable' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "setVariable" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetVariableArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SetVariableArguments": {
+			"type": "object",
+			"description": "Arguments for 'setVariable' request.",
+			"properties": {
+				"variablesReference": {
+					"type": "integer",
+					"description": "The reference of the variable container."
+				},
+				"name": {
+					"type": "string",
+					"description": "The name of the variable in the container."
+				},
+				"value": {
+					"type": "string",
+					"description": "The value of the variable."
+				},
+				"format": {
+					"$ref": "#/definitions/ValueFormat",
+					"description": "Specifies details on how to format the response value."
+				}
+			},
+			"required": [ "variablesReference", "name", "value" ]
+		},
+		"SetVariableResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'setVariable' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"value": {
+								"type": "string",
+								"description": "The new value of the variable."
+							},
+							"type": {
+								"type": "string",
+								"description": "The type of the new value. Typically shown in the UI when hovering over the value."
+							},
+							"variablesReference": {
+								"type": "integer",
+								"description": "If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"namedVariables": {
+								"type": "integer",
+								"description": "The number of named child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"indexedVariables": {
+								"type": "integer",
+								"description": "The number of indexed child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							}
+						},
+						"required": [ "value" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SourceRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request retrieves the source code for a given source reference.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "source" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SourceArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SourceArguments": {
+			"type": "object",
+			"description": "Arguments for 'source' request.",
+			"properties": {
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "Specifies the source content to load. Either source.path or source.sourceReference must be specified."
+				},
+				"sourceReference": {
+					"type": "integer",
+					"description": "The reference to the source. This is the same as source.sourceReference.\nThis is provided for backward compatibility since old backends do not understand the 'source' attribute."
+				}
+			},
+			"required": [ "sourceReference" ]
+		},
+		"SourceResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'source' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"content": {
+								"type": "string",
+								"description": "Content of the source reference."
+							},
+							"mimeType": {
+								"type": "string",
+								"description": "Optional content type (mime type) of the source."
+							}
+						},
+						"required": [ "content" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"ThreadsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request retrieves a list of all threads.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "threads" ]
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"ThreadsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'threads' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"threads": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Thread"
+								},
+								"description": "All threads."
+							}
+						},
+						"required": [ "threads" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"TerminateThreadsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "The request terminates the threads with the given ids.\nClients should only call this request if the capability 'supportsTerminateThreadsRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "terminateThreads" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/TerminateThreadsArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"TerminateThreadsArguments": {
+			"type": "object",
+			"description": "Arguments for 'terminateThreads' request.",
+			"properties": {
+				"threadIds": {
+					"type": "array",
+					"items": {
+						"type": "integer"
+					},
+					"description": "Ids of threads to be terminated."
+				}
+			}
+		},
+		"TerminateThreadsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'terminateThreads' request. This is just an acknowledgement, so no body field is required."
+			}]
+		},
+
+		"ModulesRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Modules can be retrieved from the debug adapter with this request which can either return all modules or a range of modules to support paging.\nClients should only call this request if the capability 'supportsModulesRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "modules" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ModulesArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"ModulesArguments": {
+			"type": "object",
+			"description": "Arguments for 'modules' request.",
+			"properties": {
+				"startModule": {
+					"type": "integer",
+					"description": "The index of the first module to return; if omitted modules start at 0."
+				},
+				"moduleCount": {
+					"type": "integer",
+					"description": "The number of modules to return. If moduleCount is not specified or 0, all modules are returned."
+				}
+			}
+		},
+		"ModulesResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'modules' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"modules": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Module"
+								},
+								"description": "All modules or range of modules."
+							},
+							"totalModules": {
+								"type": "integer",
+								"description": "The total number of modules available."
+							}
+						},
+						"required": [ "modules" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"LoadedSourcesRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Retrieves the set of all sources currently loaded by the debugged process.\nClients should only call this request if the capability 'supportsLoadedSourcesRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "loadedSources" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/LoadedSourcesArguments"
+					}
+				},
+				"required": [ "command" ]
+			}]
+		},
+		"LoadedSourcesArguments": {
+			"type": "object",
+			"description": "Arguments for 'loadedSources' request."
+		},
+		"LoadedSourcesResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'loadedSources' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"sources": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/Source"
+								},
+								"description": "Set of loaded sources."
+							}
+						},
+						"required": [ "sources" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"EvaluateRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Evaluates the given expression in the context of the top most stack frame.\nThe expression has access to any variables and arguments that are in scope.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "evaluate" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/EvaluateArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"EvaluateArguments": {
+			"type": "object",
+			"description": "Arguments for 'evaluate' request.",
+			"properties": {
+				"expression": {
+					"type": "string",
+					"description": "The expression to evaluate."
+				},
+				"frameId": {
+					"type": "integer",
+					"description": "Evaluate the expression in the scope of this stack frame. If not specified, the expression is evaluated in the global scope."
+				},
+				"context": {
+					"type": "string",
+					"_enum": [ "watch", "repl", "hover", "clipboard" ],
+					"enumDescriptions": [
+						"evaluate is run in a watch.",
+						"evaluate is run from REPL console.",
+						"evaluate is run from a data hover.",
+						"evaluate is run to generate the value that will be stored in the clipboard.\nThe attribute is only honored by a debug adapter if the capability 'supportsClipboardContext' is true."
+					],
+					"description": "The context in which the evaluate request is run."
+				},
+				"format": {
+					"$ref": "#/definitions/ValueFormat",
+					"description": "Specifies details on how to format the Evaluate result.\nThe attribute is only honored by a debug adapter if the capability 'supportsValueFormattingOptions' is true."
+				}
+			},
+			"required": [ "expression" ]
+		},
+		"EvaluateResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'evaluate' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"result": {
+								"type": "string",
+								"description": "The result of the evaluate request."
+							},
+							"type": {
+								"type": "string",
+								"description": "The optional type of the evaluate result.\nThis attribute should only be returned by a debug adapter if the client has passed the value true for the 'supportsVariableType' capability of the 'initialize' request."
+							},
+							"presentationHint": {
+								"$ref": "#/definitions/VariablePresentationHint",
+								"description": "Properties of a evaluate result that can be used to determine how to render the result in the UI."
+							},
+							"variablesReference": {
+								"type": "integer",
+								"description": "If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"namedVariables": {
+								"type": "integer",
+								"description": "The number of named child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"indexedVariables": {
+								"type": "integer",
+								"description": "The number of indexed child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"memoryReference": {
+								"type": "string",
+								"description": "Optional memory reference to a location appropriate for this result.\nFor pointer type eval results, this is generally a reference to the memory address contained in the pointer.\nThis attribute should be returned by a debug adapter if the client has passed the value true for the 'supportsMemoryReferences' capability of the 'initialize' request."
+							}
+						},
+						"required": [ "result", "variablesReference" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"SetExpressionRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Evaluates the given 'value' expression and assigns it to the 'expression' which must be a modifiable l-value.\nThe expressions have access to any variables and arguments that are in scope of the specified frame.\nClients should only call this request if the capability 'supportsSetExpression' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "setExpression" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/SetExpressionArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"SetExpressionArguments": {
+			"type": "object",
+			"description": "Arguments for 'setExpression' request.",
+			"properties": {
+				"expression": {
+					"type": "string",
+					"description": "The l-value expression to assign to."
+				},
+				"value": {
+					"type": "string",
+					"description": "The value expression to assign to the l-value expression."
+				},
+				"frameId": {
+					"type": "integer",
+					"description": "Evaluate the expressions in the scope of this stack frame. If not specified, the expressions are evaluated in the global scope."
+				},
+				"format": {
+					"$ref": "#/definitions/ValueFormat",
+					"description": "Specifies how the resulting value should be formatted."
+				}
+			},
+			"required": [ "expression", "value" ]
+		},
+		"SetExpressionResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'setExpression' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"value": {
+								"type": "string",
+								"description": "The new value of the expression."
+							},
+							"type": {
+								"type": "string",
+								"description": "The optional type of the value.\nThis attribute should only be returned by a debug adapter if the client has passed the value true for the 'supportsVariableType' capability of the 'initialize' request."
+							},
+							"presentationHint": {
+								"$ref": "#/definitions/VariablePresentationHint",
+								"description": "Properties of a value that can be used to determine how to render the result in the UI."
+							},
+							"variablesReference": {
+								"type": "integer",
+								"description": "If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"namedVariables": {
+								"type": "integer",
+								"description": "The number of named child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							},
+							"indexedVariables": {
+								"type": "integer",
+								"description": "The number of indexed child variables.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+							}
+						},
+						"required": [ "value" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"StepInTargetsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "This request retrieves the possible stepIn targets for the specified stack frame.\nThese targets can be used in the 'stepIn' request.\nThe StepInTargets may only be called if the 'supportsStepInTargetsRequest' capability exists and is true.\nClients should only call this request if the capability 'supportsStepInTargetsRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "stepInTargets" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/StepInTargetsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"StepInTargetsArguments": {
+			"type": "object",
+			"description": "Arguments for 'stepInTargets' request.",
+			"properties": {
+				"frameId": {
+					"type": "integer",
+					"description": "The stack frame for which to retrieve the possible stepIn targets."
+				}
+			},
+			"required": [ "frameId" ]
+		},
+		"StepInTargetsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'stepInTargets' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"targets": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/StepInTarget"
+								},
+								"description": "The possible stepIn targets of the specified source location."
+							}
+						},
+						"required": [ "targets" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"GotoTargetsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "This request retrieves the possible goto targets for the specified source location.\nThese targets can be used in the 'goto' request.\nClients should only call this request if the capability 'supportsGotoTargetsRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "gotoTargets" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/GotoTargetsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"GotoTargetsArguments": {
+			"type": "object",
+			"description": "Arguments for 'gotoTargets' request.",
+			"properties": {
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "The source location for which the goto targets are determined."
+				},
+				"line": {
+					"type": "integer",
+					"description": "The line location for which the goto targets are determined."
+				},
+				"column": {
+					"type": "integer",
+					"description": "An optional column location for which the goto targets are determined."
+				}
+			},
+			"required": [ "source", "line" ]
+		},
+		"GotoTargetsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'gotoTargets' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"targets": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/GotoTarget"
+								},
+								"description": "The possible goto targets of the specified location."
+							}
+						},
+						"required": [ "targets" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"CompletionsRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Returns a list of possible completions for a given caret position and text.\nClients should only call this request if the capability 'supportsCompletionsRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "completions" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/CompletionsArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"CompletionsArguments": {
+			"type": "object",
+			"description": "Arguments for 'completions' request.",
+			"properties": {
+				"frameId": {
+					"type": "integer",
+					"description": "Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope."
+				},
+				"text": {
+					"type": "string",
+					"description": "One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion."
+				},
+				"column": {
+					"type": "integer",
+					"description": "The character position for which to determine the completion proposals."
+				},
+				"line": {
+					"type": "integer",
+					"description": "An optional line for which to determine the completion proposals. If missing the first line of the text is assumed."
+				}
+			},
+			"required": [ "text", "column" ]
+		},
+		"CompletionsResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'completions' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"targets": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/CompletionItem"
+								},
+								"description": "The possible completions for ."
+							}
+						},
+						"required": [ "targets" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"ExceptionInfoRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Retrieves the details of the exception that caused this event to be raised.\nClients should only call this request if the capability 'supportsExceptionInfoRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "exceptionInfo" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ExceptionInfoArguments"
+					}
+				},
+				"required": [ "command", "arguments"  ]
+			}]
+		},
+		"ExceptionInfoArguments": {
+			"type": "object",
+			"description": "Arguments for 'exceptionInfo' request.",
+			"properties": {
+				"threadId": {
+					"type": "integer",
+					"description": "Thread for which exception information should be retrieved."
+				}
+			},
+			"required": [ "threadId" ]
+		},
+		"ExceptionInfoResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'exceptionInfo' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"exceptionId": {
+								"type": "string",
+								"description": "ID of the exception that was thrown."
+							},
+							"description": {
+								"type": "string",
+								"description": "Descriptive text for the exception provided by the debug adapter."
+							},
+							"breakMode": {
+								"$ref": "#/definitions/ExceptionBreakMode",
+								"description": "Mode that caused the exception notification to be raised."
+							},
+							"details": {
+								"$ref": "#/definitions/ExceptionDetails",
+								"description": "Detailed information about the exception."
+							}
+						},
+						"required": [ "exceptionId", "breakMode" ]
+					}
+				},
+				"required": [ "body" ]
+			}]
+		},
+
+		"ReadMemoryRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Reads bytes from memory at the provided location.\nClients should only call this request if the capability 'supportsReadMemoryRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "readMemory" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/ReadMemoryArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"ReadMemoryArguments": {
+			"type": "object",
+			"description": "Arguments for 'readMemory' request.",
+			"properties": {
+				"memoryReference": {
+					"type": "string",
+					"description": "Memory reference to the base location from which data should be read."
+				},
+				"offset": {
+					"type": "integer",
+					"description": "Optional offset (in bytes) to be applied to the reference location before reading data. Can be negative."
+				},
+				"count": {
+					"type": "integer",
+					"description": "Number of bytes to read at the specified location and offset."
+				}
+			},
+			"required": [ "memoryReference", "count" ]
+		},
+		"ReadMemoryResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'readMemory' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"address": {
+								"type": "string",
+								"description": "The address of the first byte of data returned.\nTreated as a hex value if prefixed with '0x', or as a decimal value otherwise."
+							},
+							"unreadableBytes": {
+								"type": "integer",
+								"description": "The number of unreadable bytes encountered after the last successfully read byte.\nThis can be used to determine the number of bytes that must be skipped before a subsequent 'readMemory' request will succeed."
+							},
+							"data": {
+								"type": "string",
+								"description": "The bytes read from memory, encoded using base64."
+							}
+						},
+						"required": [ "address" ]
+					}
+				}
+			}]
+		},
+
+		"DisassembleRequest": {
+			"allOf": [ { "$ref": "#/definitions/Request" }, {
+				"type": "object",
+				"description": "Disassembles code stored at the provided location.\nClients should only call this request if the capability 'supportsDisassembleRequest' is true.",
+				"properties": {
+					"command": {
+						"type": "string",
+						"enum": [ "disassemble" ]
+					},
+					"arguments": {
+						"$ref": "#/definitions/DisassembleArguments"
+					}
+				},
+				"required": [ "command", "arguments" ]
+			}]
+		},
+		"DisassembleArguments": {
+			"type": "object",
+			"description": "Arguments for 'disassemble' request.",
+			"properties": {
+				"memoryReference": {
+					"type": "string",
+					"description": "Memory reference to the base location containing the instructions to disassemble."
+				},
+				"offset": {
+					"type": "integer",
+					"description": "Optional offset (in bytes) to be applied to the reference location before disassembling. Can be negative."
+				},
+				"instructionOffset": {
+					"type": "integer",
+					"description": "Optional offset (in instructions) to be applied after the byte offset (if any) before disassembling. Can be negative."
+				},
+				"instructionCount": {
+					"type": "integer",
+					"description": "Number of instructions to disassemble starting at the specified location and offset.\nAn adapter must return exactly this number of instructions - any unavailable instructions should be replaced with an implementation-defined 'invalid instruction' value."
+				},
+				"resolveSymbols": {
+					"type": "boolean",
+					"description": "If true, the adapter should attempt to resolve memory addresses and other values to symbolic names."
+				}
+			},
+			"required": [ "memoryReference", "instructionCount" ]
+		},
+		"DisassembleResponse": {
+			"allOf": [ { "$ref": "#/definitions/Response" }, {
+				"type": "object",
+				"description": "Response to 'disassemble' request.",
+				"properties": {
+					"body": {
+						"type": "object",
+						"properties": {
+							"instructions": {
+								"type": "array",
+								"items": {
+									"$ref": "#/definitions/DisassembledInstruction"
+								},
+								"description": "The list of disassembled instructions."
+							}
+						},
+						"required": [ "instructions" ]
+					}
+				}
+			}]
+		},
+
+		"Capabilities": {
+			"type": "object",
+			"title": "Types",
+			"description": "Information about the capabilities of a debug adapter.",
+			"properties": {
+				"supportsConfigurationDoneRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'configurationDone' request."
+				},
+				"supportsFunctionBreakpoints": {
+					"type": "boolean",
+					"description": "The debug adapter supports function breakpoints."
+				},
+				"supportsConditionalBreakpoints": {
+					"type": "boolean",
+					"description": "The debug adapter supports conditional breakpoints."
+				},
+				"supportsHitConditionalBreakpoints": {
+					"type": "boolean",
+					"description": "The debug adapter supports breakpoints that break execution after a specified number of hits."
+				},
+				"supportsEvaluateForHovers": {
+					"type": "boolean",
+					"description": "The debug adapter supports a (side effect free) evaluate request for data hovers."
+				},
+				"exceptionBreakpointFilters": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ExceptionBreakpointsFilter"
+					},
+					"description": "Available exception filter options for the 'setExceptionBreakpoints' request."
+				},
+				"supportsStepBack": {
+					"type": "boolean",
+					"description": "The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests."
+				},
+				"supportsSetVariable": {
+					"type": "boolean",
+					"description": "The debug adapter supports setting a variable to a value."
+				},
+				"supportsRestartFrame": {
+					"type": "boolean",
+					"description": "The debug adapter supports restarting a frame."
+				},
+				"supportsGotoTargetsRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'gotoTargets' request."
+				},
+				"supportsStepInTargetsRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'stepInTargets' request."
+				},
+				"supportsCompletionsRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'completions' request."
+				},
+				"completionTriggerCharacters": {
+					"type": "array",
+					"items": {
+						"type": "string"
+					},
+					"description": "The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character."
+				},
+				"supportsModulesRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'modules' request."
+				},
+				"additionalModuleColumns": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ColumnDescriptor"
+					},
+					"description": "The set of additional module information exposed by the debug adapter."
+				},
+				"supportedChecksumAlgorithms": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ChecksumAlgorithm"
+					},
+					"description": "Checksum algorithms supported by the debug adapter."
+				},
+				"supportsRestartRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'restart' request. In this case a client should not implement 'restart' by terminating and relaunching the adapter but by calling the RestartRequest."
+				},
+				"supportsExceptionOptions": {
+					"type": "boolean",
+					"description": "The debug adapter supports 'exceptionOptions' on the setExceptionBreakpoints request."
+				},
+				"supportsValueFormattingOptions": {
+					"type": "boolean",
+					"description": "The debug adapter supports a 'format' attribute on the stackTraceRequest, variablesRequest, and evaluateRequest."
+				},
+				"supportsExceptionInfoRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'exceptionInfo' request."
+				},
+				"supportTerminateDebuggee": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'terminateDebuggee' attribute on the 'disconnect' request."
+				},
+				"supportSuspendDebuggee": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'suspendDebuggee' attribute on the 'disconnect' request."
+				},
+				"supportsDelayedStackTraceLoading": {
+					"type": "boolean",
+					"description": "The debug adapter supports the delayed loading of parts of the stack, which requires that both the 'startFrame' and 'levels' arguments and an optional 'totalFrames' result of the 'StackTrace' request are supported."
+				},
+				"supportsLoadedSourcesRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'loadedSources' request."
+				},
+				"supportsLogPoints": {
+					"type": "boolean",
+					"description": "The debug adapter supports logpoints by interpreting the 'logMessage' attribute of the SourceBreakpoint."
+				},
+				"supportsTerminateThreadsRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'terminateThreads' request."
+				},
+				"supportsSetExpression": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'setExpression' request."
+				},
+				"supportsTerminateRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'terminate' request."
+				},
+				"supportsDataBreakpoints": {
+					"type": "boolean",
+					"description": "The debug adapter supports data breakpoints."
+				},
+				"supportsReadMemoryRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'readMemory' request."
+				},
+				"supportsDisassembleRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'disassemble' request."
+				},
+				"supportsCancelRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'cancel' request."
+				},
+				"supportsBreakpointLocationsRequest": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'breakpointLocations' request."
+				},
+				"supportsClipboardContext": {
+					"type": "boolean",
+					"description": "The debug adapter supports the 'clipboard' context value in the 'evaluate' request."
+				},
+				"supportsSteppingGranularity": {
+					"type": "boolean",
+					"description": "The debug adapter supports stepping granularities (argument 'granularity') for the stepping requests."
+				},
+				"supportsInstructionBreakpoints": {
+					"type": "boolean",
+					"description": "The debug adapter supports adding breakpoints based on instruction references."
+				},
+				"supportsExceptionFilterOptions": {
+					"type": "boolean",
+					"description": "The debug adapter supports 'filterOptions' as an argument on the 'setExceptionBreakpoints' request."
+				}
+			}
+		},
+
+		"ExceptionBreakpointsFilter": {
+			"type": "object",
+			"description": "An ExceptionBreakpointsFilter is shown in the UI as an filter option for configuring how exceptions are dealt with.",
+			"properties": {
+				"filter": {
+					"type": "string",
+					"description": "The internal ID of the filter option. This value is passed to the 'setExceptionBreakpoints' request."
+				},
+				"label": {
+					"type": "string",
+					"description": "The name of the filter option. This will be shown in the UI."
+				},
+				"description": {
+					"type": "string",
+					"description": "An optional help text providing additional information about the exception filter. This string is typically shown as a hover and must be translated."
+				},
+				"default": {
+					"type": "boolean",
+					"description": "Initial value of the filter option. If not specified a value 'false' is assumed."
+				},
+				"supportsCondition": {
+					"type": "boolean",
+					"description": "Controls whether a condition can be specified for this filter option. If false or missing, a condition can not be set."
+				},
+				"conditionDescription": {
+					"type": "string",
+					"description": "An optional help text providing information about the condition. This string is shown as the placeholder text for a text box and must be translated."
+				}
+			},
+			"required": [ "filter", "label" ]
+		},
+
+		"Message": {
+			"type": "object",
+			"description": "A structured message object. Used to return errors from requests.",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"description": "Unique identifier for the message."
+				},
+				"format": {
+					"type": "string",
+					"description": "A format string for the message. Embedded variables have the form '{name}'.\nIf variable name starts with an underscore character, the variable does not contain user data (PII) and can be safely used for telemetry purposes."
+				},
+				"variables": {
+					"type": "object",
+					"description": "An object used as a dictionary for looking up the variables in the format string.",
+					"additionalProperties": {
+						"type": "string",
+						"description": "Values must be strings."
+					}
+				},
+				"sendTelemetry": {
+					"type": "boolean",
+					"description": "If true send to telemetry."
+				},
+				"showUser": {
+					"type": "boolean",
+					"description": "If true show user."
+				},
+				"url": {
+					"type": "string",
+					"description": "An optional url where additional information about this message can be found."
+				},
+				"urlLabel": {
+					"type": "string",
+					"description": "An optional label that is presented to the user as the UI for opening the url."
+				}
+			},
+			"required": [ "id", "format" ]
+		},
+
+		"Module": {
+			"type": "object",
+			"description": "A Module object represents a row in the modules view.\nTwo attributes are mandatory: an id identifies a module in the modules view and is used in a ModuleEvent for identifying a module for adding, updating or deleting.\nThe name is used to minimally render the module in the UI.\n\nAdditional attributes can be added to the module. They will show up in the module View if they have a corresponding ColumnDescriptor.\n\nTo avoid an unnecessary proliferation of additional attributes with similar semantics but different names\nwe recommend to re-use attributes from the 'recommended' list below first, and only introduce new attributes if nothing appropriate could be found.",
+			"properties": {
+				"id": {
+					"type": ["integer", "string"],
+					"description": "Unique identifier for the module."
+				},
+				"name": {
+					"type": "string",
+					"description": "A name of the module."
+				},
+				"path": {
+					"type": "string",
+					"description": "optional but recommended attributes.\nalways try to use these first before introducing additional attributes.\n\nLogical full path to the module. The exact definition is implementation defined, but usually this would be a full path to the on-disk file for the module."
+				},
+				"isOptimized": {
+					"type": "boolean",
+					"description": "True if the module is optimized."
+				},
+				"isUserCode": {
+					"type": "boolean",
+					"description": "True if the module is considered 'user code' by a debugger that supports 'Just My Code'."
+				},
+				"version": {
+					"type": "string",
+					"description": "Version of Module."
+				},
+				"symbolStatus": {
+					"type": "string",
+					"description": "User understandable description of if symbols were found for the module (ex: 'Symbols Loaded', 'Symbols not found', etc."
+				},
+				"symbolFilePath": {
+					"type": "string",
+					"description": "Logical full path to the symbol file. The exact definition is implementation defined."
+				},
+				"dateTimeStamp": {
+					"type": "string",
+					"description": "Module created or modified."
+				},
+				"addressRange": {
+					"type": "string",
+					"description": "Address range covered by this module."
+				}
+			},
+			"required": [ "id", "name" ]
+		},
+
+		"ColumnDescriptor": {
+			"type": "object",
+			"description": "A ColumnDescriptor specifies what module attribute to show in a column of the ModulesView, how to format it,\nand what the column's label should be.\nIt is only used if the underlying UI actually supports this level of customization.",
+			"properties": {
+				"attributeName": {
+					"type": "string",
+					"description": "Name of the attribute rendered in this column."
+				},
+				"label": {
+					"type": "string",
+					"description": "Header UI label of column."
+				},
+				"format": {
+					"type": "string",
+					"description": "Format to use for the rendered values in this column. TBD how the format strings looks like."
+				},
+				"type": {
+					"type": "string",
+					"enum": [ "string", "number", "boolean", "unixTimestampUTC" ],
+					"description": "Datatype of values in this column.  Defaults to 'string' if not specified."
+				},
+				"width": {
+					"type": "integer",
+					"description": "Width of this column in characters (hint only)."
+				}
+			},
+			"required": [ "attributeName", "label"]
+		},
+
+		"ModulesViewDescriptor": {
+			"type": "object",
+			"description": "The ModulesViewDescriptor is the container for all declarative configuration options of a ModuleView.\nFor now it only specifies the columns to be shown in the modules view.",
+			"properties": {
+				"columns": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ColumnDescriptor"
+					}
+				}
+			},
+			"required": [ "columns" ]
+		},
+
+		"Thread": {
+			"type": "object",
+			"description": "A Thread",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"description": "Unique identifier for the thread."
+				},
+				"name": {
+					"type": "string",
+					"description": "A name of the thread."
+				}
+			},
+			"required": [ "id", "name" ]
+		},
+
+		"Source": {
+			"type": "object",
+			"description": "A Source is a descriptor for source code.\nIt is returned from the debug adapter as part of a StackFrame and it is used by clients when specifying breakpoints.",
+			"properties": {
+				"name": {
+					"type": "string",
+					"description": "The short name of the source. Every source returned from the debug adapter has a name.\nWhen sending a source to the debug adapter this name is optional."
+				},
+				"path": {
+					"type": "string",
+					"description": "The path of the source to be shown in the UI.\nIt is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0)."
+				},
+				"sourceReference": {
+					"type": "integer",
+					"description": "If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified).\nA sourceReference is only valid for a session, so it must not be used to persist a source.\nThe value should be less than or equal to 2147483647 (2^31-1)."
+				},
+				"presentationHint": {
+					"type": "string",
+					"description": "An optional hint for how to present the source in the UI.\nA value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping.",
+					"enum": [ "normal", "emphasize", "deemphasize" ]
+				},
+				"origin": {
+					"type": "string",
+					"description": "The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc."
+				},
+				"sources": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/Source"
+					},
+					"description": "An optional list of sources that are related to this source. These may be the source that generated this source."
+				},
+				"adapterData": {
+					"type": [ "array", "boolean", "integer", "null", "number", "object", "string" ],
+					"description": "Optional data that a debug adapter might want to loop through the client.\nThe client should leave the data intact and persist it across sessions. The client should not interpret the data."
+				},
+				"checksums": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/Checksum"
+					},
+					"description": "The checksums associated with this file."
+				}
+			}
+		},
+
+		"StackFrame": {
+			"type": "object",
+			"description": "A Stackframe contains the source location.",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"description": "An identifier for the stack frame. It must be unique across all threads.\nThis id can be used to retrieve the scopes of the frame with the 'scopesRequest' or to restart the execution of a stackframe."
+				},
+				"name": {
+					"type": "string",
+					"description": "The name of the stack frame, typically a method name."
+				},
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "The optional source of the frame."
+				},
+				"line": {
+					"type": "integer",
+					"description": "The line within the file of the frame. If source is null or doesn't exist, line is 0 and must be ignored."
+				},
+				"column": {
+					"type": "integer",
+					"description": "The column within the line. If source is null or doesn't exist, column is 0 and must be ignored."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "An optional end line of the range covered by the stack frame."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "An optional end column of the range covered by the stack frame."
+				},
+				"canRestart": {
+					"type": "boolean",
+					"description": "Indicates whether this frame can be restarted with the 'restart' request. Clients should only use this if the debug adapter supports the 'restart' request (capability 'supportsRestartRequest' is true)."
+				},
+				"instructionPointerReference": {
+					"type": "string",
+					"description": "Optional memory reference for the current instruction pointer in this frame."
+				},
+				"moduleId": {
+					"type": ["integer", "string"],
+					"description": "The module associated with this frame, if any."
+				},
+				"presentationHint": {
+					"type": "string",
+					"enum": [ "normal", "label", "subtle" ],
+					"description": "An optional hint for how to present this frame in the UI.\nA value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way."
+				}
+			},
+			"required": [ "id", "name", "line", "column" ]
+		},
+
+		"Scope": {
+			"type": "object",
+			"description": "A Scope is a named container for variables. Optionally a scope can map to a source or a range within a source.",
+			"properties": {
+				"name": {
+					"type": "string",
+					"description": "Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This string is shown in the UI as is and can be translated."
+				},
+				"presentationHint": {
+					"type": "string",
+					"description": "An optional hint for how to present this scope in the UI. If this attribute is missing, the scope is shown with a generic UI.",
+					"_enum": [ "arguments", "locals", "registers" ],
+					"enumDescriptions": [
+						"Scope contains method arguments.",
+						"Scope contains local variables.",
+						"Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request."
+					]
+				},
+				"variablesReference": {
+					"type": "integer",
+					"description": "The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest."
+				},
+				"namedVariables": {
+					"type": "integer",
+					"description": "The number of named variables in this scope.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks."
+				},
+				"indexedVariables": {
+					"type": "integer",
+					"description": "The number of indexed variables in this scope.\nThe client can use this optional information to present the variables in a paged UI and fetch them in chunks."
+				},
+				"expensive": {
+					"type": "boolean",
+					"description": "If true, the number of variables in this scope is large or expensive to retrieve."
+				},
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "Optional source for this scope."
+				},
+				"line": {
+					"type": "integer",
+					"description": "Optional start line of the range covered by this scope."
+				},
+				"column": {
+					"type": "integer",
+					"description": "Optional start column of the range covered by this scope."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "Optional end line of the range covered by this scope."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "Optional end column of the range covered by this scope."
+				}
+			},
+			"required": [ "name", "variablesReference", "expensive" ]
+		},
+
+		"Variable": {
+			"type": "object",
+			"description": "A Variable is a name/value pair.\nOptionally a variable can have a 'type' that is shown if space permits or when hovering over the variable's name.\nAn optional 'kind' is used to render additional properties of the variable, e.g. different icons can be used to indicate that a variable is public or private.\nIf the value is structured (has children), a handle is provided to retrieve the children with the VariablesRequest.\nIf the number of named or indexed children is large, the numbers should be returned via the optional 'namedVariables' and 'indexedVariables' attributes.\nThe client can use this optional information to present the children in a paged UI and fetch them in chunks.",
+			"properties": {
+				"name": {
+					"type": "string",
+					"description": "The variable's name."
+				},
+				"value": {
+					"type": "string",
+					"description": "The variable's value. This can be a multi-line text, e.g. for a function the body of a function."
+				},
+				"type": {
+					"type": "string",
+					"description": "The type of the variable's value. Typically shown in the UI when hovering over the value.\nThis attribute should only be returned by a debug adapter if the client has passed the value true for the 'supportsVariableType' capability of the 'initialize' request."
+				},
+				"presentationHint": {
+					"$ref": "#/definitions/VariablePresentationHint",
+					"description": "Properties of a variable that can be used to determine how to render the variable in the UI."
+				},
+				"evaluateName": {
+					"type": "string",
+					"description": "Optional evaluatable name of this variable which can be passed to the 'EvaluateRequest' to fetch the variable's value."
+				},
+				"variablesReference": {
+					"type": "integer",
+					"description": "If variablesReference is > 0, the variable is structured and its children can be retrieved by passing variablesReference to the VariablesRequest."
+				},
+				"namedVariables": {
+					"type": "integer",
+					"description": "The number of named child variables.\nThe client can use this optional information to present the children in a paged UI and fetch them in chunks."
+				},
+				"indexedVariables": {
+					"type": "integer",
+					"description": "The number of indexed child variables.\nThe client can use this optional information to present the children in a paged UI and fetch them in chunks."
+				},
+				"memoryReference": {
+					"type": "string",
+					"description": "Optional memory reference for the variable if the variable represents executable code, such as a function pointer.\nThis attribute is only required if the client has passed the value true for the 'supportsMemoryReferences' capability of the 'initialize' request."
+				}
+			},
+			"required": [ "name", "value", "variablesReference" ]
+		},
+
+		"VariablePresentationHint": {
+			"type": "object",
+			"description": "Optional properties of a variable that can be used to determine how to render the variable in the UI.",
+			"properties": {
+				"kind": {
+					"description": "The kind of variable. Before introducing additional values, try to use the listed values.",
+					"type": "string",
+					"_enum": [ "property", "method", "class", "data", "event", "baseClass", "innerClass", "interface", "mostDerivedClass", "virtual", "dataBreakpoint" ],
+					"enumDescriptions": [
+						"Indicates that the object is a property.",
+						"Indicates that the object is a method.",
+						"Indicates that the object is a class.",
+						"Indicates that the object is data.",
+						"Indicates that the object is an event.",
+						"Indicates that the object is a base class.",
+						"Indicates that the object is an inner class.",
+						"Indicates that the object is an interface.",
+						"Indicates that the object is the most derived class.",
+						"Indicates that the object is virtual, that means it is a synthetic object introducedby the\nadapter for rendering purposes, e.g. an index range for large arrays.",
+						"Deprecated: Indicates that a data breakpoint is registered for the object. The 'hasDataBreakpoint' attribute should generally be used instead."
+					]
+				},
+				"attributes": {
+					"description": "Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values.",
+					"type": "array",
+					"items": {
+						"type": "string",
+						"_enum": [ "static", "constant", "readOnly", "rawString", "hasObjectId", "canHaveObjectId", "hasSideEffects", "hasDataBreakpoint" ],
+						"enumDescriptions": [
+							"Indicates that the object is static.",
+							"Indicates that the object is a constant.",
+							"Indicates that the object is read only.",
+							"Indicates that the object is a raw string.",
+							"Indicates that the object can have an Object ID created for it.",
+							"Indicates that the object has an Object ID associated with it.",
+							"Indicates that the evaluation had side effects.",
+							"Indicates that the object has its value tracked by a data breakpoint."
+						]
+					}
+				},
+				"visibility": {
+					"description": "Visibility of variable. Before introducing additional values, try to use the listed values.",
+					"type": "string",
+					"_enum": [ "public", "private", "protected", "internal", "final" ]
+				}
+			}
+		},
+
+		"BreakpointLocation": {
+			"type": "object",
+			"description": "Properties of a breakpoint location returned from the 'breakpointLocations' request.",
+			"properties": {
+				"line": {
+					"type": "integer",
+					"description": "Start line of breakpoint location."
+				},
+				"column": {
+					"type": "integer",
+					"description": "Optional start column of breakpoint location."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "Optional end line of breakpoint location if the location covers a range."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "Optional end column of breakpoint location if the location covers a range."
+				}
+			},
+			"required": [ "line" ]
+		},
+
+		"SourceBreakpoint": {
+			"type": "object",
+			"description": "Properties of a breakpoint or logpoint passed to the setBreakpoints request.",
+			"properties": {
+				"line": {
+					"type": "integer",
+					"description": "The source line of the breakpoint or logpoint."
+				},
+				"column": {
+					"type": "integer",
+					"description": "An optional source column of the breakpoint."
+				},
+				"condition": {
+					"type": "string",
+					"description": "An optional expression for conditional breakpoints.\nIt is only honored by a debug adapter if the capability 'supportsConditionalBreakpoints' is true."
+				},
+				"hitCondition": {
+					"type": "string",
+					"description": "An optional expression that controls how many hits of the breakpoint are ignored.\nThe backend is expected to interpret the expression as needed.\nThe attribute is only honored by a debug adapter if the capability 'supportsHitConditionalBreakpoints' is true."
+				},
+				"logMessage": {
+					"type": "string",
+					"description": "If this attribute exists and is non-empty, the backend must not 'break' (stop)\nbut log the message instead. Expressions within {} are interpolated.\nThe attribute is only honored by a debug adapter if the capability 'supportsLogPoints' is true."
+				}
+			},
+			"required": [ "line" ]
+		},
+
+		"FunctionBreakpoint": {
+			"type": "object",
+			"description": "Properties of a breakpoint passed to the setFunctionBreakpoints request.",
+			"properties": {
+				"name": {
+					"type": "string",
+					"description": "The name of the function."
+				},
+				"condition": {
+					"type": "string",
+					"description": "An optional expression for conditional breakpoints.\nIt is only honored by a debug adapter if the capability 'supportsConditionalBreakpoints' is true."
+				},
+				"hitCondition": {
+					"type": "string",
+					"description": "An optional expression that controls how many hits of the breakpoint are ignored.\nThe backend is expected to interpret the expression as needed.\nThe attribute is only honored by a debug adapter if the capability 'supportsHitConditionalBreakpoints' is true."
+				}
+			},
+			"required": [ "name" ]
+		},
+
+		"DataBreakpointAccessType": {
+			"type": "string",
+			"description": "This enumeration defines all possible access types for data breakpoints.",
+			"enum": [ "read", "write", "readWrite" ]
+		},
+
+		"DataBreakpoint": {
+			"type": "object",
+			"description": "Properties of a data breakpoint passed to the setDataBreakpoints request.",
+			"properties": {
+				"dataId": {
+					"type": "string",
+					"description": "An id representing the data. This id is returned from the dataBreakpointInfo request."
+				},
+				"accessType": {
+					"$ref": "#/definitions/DataBreakpointAccessType",
+					"description": "The access type of the data."
+				},
+				"condition": {
+					"type": "string",
+					"description": "An optional expression for conditional breakpoints."
+				},
+				"hitCondition": {
+					"type": "string",
+					"description": "An optional expression that controls how many hits of the breakpoint are ignored.\nThe backend is expected to interpret the expression as needed."
+				}
+			},
+			"required": [ "dataId" ]
+		},
+
+		"InstructionBreakpoint": {
+			"type": "object",
+			"description": "Properties of a breakpoint passed to the setInstructionBreakpoints request",
+			"properties": {
+				"instructionReference": {
+					"type": "string",
+					"description": "The instruction reference of the breakpoint.\nThis should be a memory or instruction pointer reference from an EvaluateResponse, Variable, StackFrame, GotoTarget, or Breakpoint."
+				},
+				"offset": {
+					"type": "integer",
+					"description": "An optional offset from the instruction reference.\nThis can be negative."
+				},
+				"condition": {
+					"type": "string",
+					"description": "An optional expression for conditional breakpoints.\nIt is only honored by a debug adapter if the capability 'supportsConditionalBreakpoints' is true."
+				},
+				"hitCondition": {
+					"type": "string",
+					"description": "An optional expression that controls how many hits of the breakpoint are ignored.\nThe backend is expected to interpret the expression as needed.\nThe attribute is only honored by a debug adapter if the capability 'supportsHitConditionalBreakpoints' is true."
+				}
+			},
+			"required": [ "instructionReference" ]
+		},
+
+		"Breakpoint": {
+			"type": "object",
+			"description": "Information about a Breakpoint created in setBreakpoints, setFunctionBreakpoints, setInstructionBreakpoints, or setDataBreakpoints.",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"description": "An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints."
+				},
+				"verified": {
+					"type": "boolean",
+					"description": "If true breakpoint could be set (but not necessarily at the desired location)."
+				},
+				"message": {
+					"type": "string",
+					"description": "An optional message about the state of the breakpoint.\nThis is shown to the user and can be used to explain why a breakpoint could not be verified."
+				},
+				"source": {
+					"$ref": "#/definitions/Source",
+					"description": "The source where the breakpoint is located."
+				},
+				"line": {
+					"type": "integer",
+					"description": "The start line of the actual range covered by the breakpoint."
+				},
+				"column": {
+					"type": "integer",
+					"description": "An optional start column of the actual range covered by the breakpoint."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "An optional end line of the actual range covered by the breakpoint."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "An optional end column of the actual range covered by the breakpoint.\nIf no end line is given, then the end column is assumed to be in the start line."
+				},
+				"instructionReference": {
+					"type": "string",
+					"description": "An optional memory reference to where the breakpoint is set."
+				},
+				"offset": {
+					"type": "integer",
+					"description": "An optional offset from the instruction reference.\nThis can be negative."
+				}
+			},
+			"required": [ "verified" ]
+		},
+
+		"SteppingGranularity": {
+			"type": "string",
+			"description": "The granularity of one 'step' in the stepping requests 'next', 'stepIn', 'stepOut', and 'stepBack'.",
+			"enum": [ "statement", "line", "instruction" ],
+			"enumDescriptions": [
+				"The step should allow the program to run until the current statement has finished executing.\nThe meaning of a statement is determined by the adapter and it may be considered equivalent to a line.\nFor example 'for(int i = 0; i < 10; i++) could be considered to have 3 statements 'int i = 0', 'i < 10', and 'i++'.",
+				"The step should allow the program to run until the current source line has executed.",
+				"The step should allow one instruction to execute (e.g. one x86 instruction)."
+			]
+		},
+
+		"StepInTarget": {
+			"type": "object",
+			"description": "A StepInTarget can be used in the 'stepIn' request and determines into which single target the stepIn request should step.",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"description": "Unique identifier for a stepIn target."
+				},
+				"label": {
+					"type": "string",
+					"description": "The name of the stepIn target (shown in the UI)."
+				}
+			},
+			"required": [ "id", "label" ]
+		},
+
+		"GotoTarget": {
+			"type": "object",
+			"description": "A GotoTarget describes a code location that can be used as a target in the 'goto' request.\nThe possible goto targets can be determined via the 'gotoTargets' request.",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"description": "Unique identifier for a goto target. This is used in the goto request."
+				},
+				"label": {
+					"type": "string",
+					"description": "The name of the goto target (shown in the UI)."
+				},
+				"line": {
+					"type": "integer",
+					"description": "The line of the goto target."
+				},
+				"column": {
+					"type": "integer",
+					"description": "An optional column of the goto target."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "An optional end line of the range covered by the goto target."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "An optional end column of the range covered by the goto target."
+				},
+				"instructionPointerReference": {
+					"type": "string",
+					"description": "Optional memory reference for the instruction pointer value represented by this target."
+				}
+			},
+			"required": [ "id", "label", "line" ]
+		},
+
+		"CompletionItem": {
+			"type": "object",
+			"description": "CompletionItems are the suggestions returned from the CompletionsRequest.",
+			"properties": {
+				"label": {
+					"type": "string",
+					"description": "The label of this completion item. By default this is also the text that is inserted when selecting this completion."
+				},
+				"text": {
+					"type": "string",
+					"description": "If text is not falsy then it is inserted instead of the label."
+				},
+				"sortText": {
+					"type": "string",
+					"description": "A string that should be used when comparing this item with other items. When `falsy` the label is used."
+				},
+				"type": {
+					"$ref": "#/definitions/CompletionItemType",
+					"description": "The item's type. Typically the client uses this information to render the item in the UI with an icon."
+				},
+				"start": {
+					"type": "integer",
+					"description": "This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added.\nIf missing the text is added at the location specified by the CompletionsRequest's 'column' attribute."
+				},
+				"length": {
+					"type": "integer",
+					"description": "This value determines how many characters are overwritten by the completion text.\nIf missing the value 0 is assumed which results in the completion text being inserted."
+				},
+				"selectionStart": {
+					"type": "integer",
+					"description": "Determines the start of the new selection after the text has been inserted (or replaced).\nThe start position must in the range 0 and length of the completion text.\nIf omitted the selection starts at the end of the completion text."
+				},
+				"selectionLength": {
+					"type": "integer",
+					"description": "Determines the length of the new selection after the text has been inserted (or replaced).\nThe selection can not extend beyond the bounds of the completion text.\nIf omitted the length is assumed to be 0."
+				}
+			},
+			"required": [ "label" ]
+		},
+
+		"CompletionItemType": {
+			"type": "string",
+			"description": "Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them.",
+			"enum": [ "method", "function", "constructor", "field", "variable", "class", "interface", "module", "property", "unit", "value", "enum", "keyword", "snippet", "text", "color", "file", "reference", "customcolor" ]
+		},
+
+		"ChecksumAlgorithm": {
+			"type": "string",
+			"description": "Names of checksum algorithms that may be supported by a debug adapter.",
+			"enum": [ "MD5", "SHA1", "SHA256", "timestamp" ]
+		},
+
+		"Checksum": {
+			"type": "object",
+			"description": "The checksum of an item calculated by the specified algorithm.",
+			"properties": {
+				"algorithm": {
+					"$ref": "#/definitions/ChecksumAlgorithm",
+					"description": "The algorithm used to calculate this checksum."
+				},
+				"checksum": {
+					"type": "string",
+					"description": "Value of the checksum."
+				}
+			},
+			"required": [ "algorithm", "checksum" ]
+		},
+
+		"ValueFormat": {
+			"type": "object",
+			"description": "Provides formatting information for a value.",
+			"properties": {
+				"hex": {
+					"type": "boolean",
+					"description": "Display the value in hex."
+				}
+			}
+		},
+
+		"StackFrameFormat": {
+			"allOf": [ { "$ref": "#/definitions/ValueFormat" }, {
+				"type": "object",
+				"description": "Provides formatting information for a stack frame.",
+				"properties": {
+					"parameters": {
+						"type": "boolean",
+						"description": "Displays parameters for the stack frame."
+					},
+					"parameterTypes": {
+						"type": "boolean",
+						"description": "Displays the types of parameters for the stack frame."
+					},
+					"parameterNames": {
+						"type": "boolean",
+						"description": "Displays the names of parameters for the stack frame."
+					},
+					"parameterValues": {
+						"type": "boolean",
+						"description": "Displays the values of parameters for the stack frame."
+					},
+					"line": {
+						"type": "boolean",
+						"description": "Displays the line number of the stack frame."
+					},
+					"module": {
+						"type": "boolean",
+						"description": "Displays the module of the stack frame."
+					},
+					"includeAll": {
+						"type": "boolean",
+						"description": "Includes all stack frames, including those the debug adapter might otherwise hide."
+					}
+				}
+			}]
+		},
+
+		"ExceptionFilterOptions": {
+			"type": "object",
+			"description": "An ExceptionFilterOptions is used to specify an exception filter together with a condition for the setExceptionsFilter request.",
+			"properties": {
+				"filterId": {
+					"type": "string",
+					"description": "ID of an exception filter returned by the 'exceptionBreakpointFilters' capability."
+				},
+				"condition": {
+					"type": "string",
+					"description": "An optional expression for conditional exceptions.\nThe exception will break into the debugger if the result of the condition is true."
+				}
+			},
+			"required": [ "filterId" ]
+		},
+
+		"ExceptionOptions": {
+			"type": "object",
+			"description": "An ExceptionOptions assigns configuration options to a set of exceptions.",
+			"properties": {
+				"path": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ExceptionPathSegment"
+					},
+					"description": "A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected.\nBy convention the first segment of the path is a category that is used to group exceptions in the UI."
+				},
+				"breakMode": {
+					"$ref": "#/definitions/ExceptionBreakMode",
+					"description": "Condition when a thrown exception should result in a break."
+				}
+			},
+			"required": [ "breakMode" ]
+		},
+
+		"ExceptionBreakMode": {
+			"type": "string",
+			"description": "This enumeration defines all possible conditions when a thrown exception should result in a break.\nnever: never breaks,\nalways: always breaks,\nunhandled: breaks when exception unhandled,\nuserUnhandled: breaks if the exception is not handled by user code.",
+			"enum": [ "never", "always", "unhandled", "userUnhandled" ]
+		},
+
+		"ExceptionPathSegment": {
+			"type": "object",
+			"description": "An ExceptionPathSegment represents a segment in a path that is used to match leafs or nodes in a tree of exceptions.\nIf a segment consists of more than one name, it matches the names provided if 'negate' is false or missing or\nit matches anything except the names provided if 'negate' is true.",
+			"properties": {
+				"negate": {
+					"type": "boolean",
+					"description": "If false or missing this segment matches the names provided, otherwise it matches anything except the names provided."
+				},
+				"names": {
+					"type": "array",
+					"items": {
+						"type": "string"
+					},
+					"description": "Depending on the value of 'negate' the names that should match or not match."
+				}
+			},
+			"required": [ "names" ]
+		},
+
+		"ExceptionDetails": {
+			"type": "object",
+			"description": "Detailed information about an exception that has occurred.",
+			"properties": {
+				"message": {
+					"type": "string",
+					"description": "Message contained in the exception."
+				},
+				"typeName": {
+					"type": "string",
+					"description": "Short type name of the exception object."
+				},
+				"fullTypeName": {
+					"type": "string",
+					"description": "Fully-qualified type name of the exception object."
+				},
+				"evaluateName": {
+					"type": "string",
+					"description": "Optional expression that can be evaluated in the current scope to obtain the exception object."
+				},
+				"stackTrace": {
+					"type": "string",
+					"description": "Stack trace at the time the exception was thrown."
+				},
+				"innerException": {
+					"type": "array",
+					"items": {
+						"$ref": "#/definitions/ExceptionDetails"
+					},
+					"description": "Details of the exception contained by this exception, if any."
+				}
+			}
+		},
+
+		"DisassembledInstruction": {
+			"type": "object",
+			"description": "Represents a single disassembled instruction.",
+			"properties": {
+				"address": {
+					"type": "string",
+					"description": "The address of the instruction. Treated as a hex value if prefixed with '0x', or as a decimal value otherwise."
+				},
+				"instructionBytes": {
+					"type": "string",
+					"description": "Optional raw bytes representing the instruction and its operands, in an implementation-defined format."
+				},
+				"instruction": {
+					"type": "string",
+					"description": "Text representing the instruction and its operands, in an implementation-defined format."
+				},
+				"symbol": {
+					"type": "string",
+					"description": "Name of the symbol that corresponds with the location of this instruction, if any."
+				},
+				"location": {
+					"$ref": "#/definitions/Source",
+					"description": "Source location that corresponds to this instruction, if any.\nShould always be set (if available) on the first instruction returned,\nbut can be omitted afterwards if this instruction maps to the same source file as the previous instruction."
+				},
+				"line": {
+					"type": "integer",
+					"description": "The line within the source location that corresponds to this instruction, if any."
+				},
+				"column": {
+					"type": "integer",
+					"description": "The column within the line that corresponds to this instruction, if any."
+				},
+				"endLine": {
+					"type": "integer",
+					"description": "The end line of the range that corresponds to this instruction, if any."
+				},
+				"endColumn": {
+					"type": "integer",
+					"description": "The end column of the range that corresponds to this instruction, if any."
+				}
+			},
+			"required": [ "address", "instruction" ]
+		},
+
+		"InvalidatedAreas": {
+			"type": "string",
+			"description": "Logical areas that can be invalidated by the 'invalidated' event.",
+			"_enum": [ "all", "stacks", "threads", "variables" ],
+			"enumDescriptions": [
+				"All previously fetched data has become invalid and needs to be refetched.",
+				"Previously fetched stack related data has become invalid and needs to be refetched.",
+				"Previously fetched thread related data has become invalid and needs to be refetched.",
+				"Previously fetched variable data has become invalid and needs to be refetched."
+			]
+		}
+
+	}
+}
diff --git a/dds/tool/dap/external_dap_spec/debugAdapterProtocol.license.txt b/dds/tool/dap/external_dap_spec/debugAdapterProtocol.license.txt
new file mode 100644
index 0000000..8a02f14
--- /dev/null
+++ b/dds/tool/dap/external_dap_spec/debugAdapterProtocol.license.txt
@@ -0,0 +1,23 @@
+debugAdapterProtocol.json is an unmodified copy of the DAP Specification,
+downloaded from:
+
+  https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/gh-pages/debugAdapterProtocol.json
+
+The licence for this file is included below. This accompanying file is the
+version of the specification that was used to generate a portion of the Dart
+code used to support the protocol.
+
+To regenerate the generated code, run the script in "tool/dap/generate_all.dart"
+with no arguments. To download the latest version of the specification before
+regenerating the code, run the same script with the "--download" argument.
+
+---
+
+Copyright (c) Microsoft Corporation.

+ 

+All rights reserved. 

+

+Distributed under the following terms:

+

+1.	Documentation is licensed under the Creative Commons Attribution 3.0 United States License. Code is licensed under the MIT License.

+2.	This license does not grant you rights to use any trademarks or logos of Microsoft. For Microsoft’s general trademark guidelines, go to http://go.microsoft.com/fwlink/?LinkID=254653

diff --git a/dds/tool/dap/generate_all.dart b/dds/tool/dap/generate_all.dart
new file mode 100644
index 0000000..8fe4b60
--- /dev/null
+++ b/dds/tool/dap/generate_all.dart
@@ -0,0 +1,95 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:http/http.dart' as http;
+import 'package:path/path.dart' as path;
+
+import 'codegen.dart';
+import 'json_schema.dart';
+
+Future<void> main(List<String> arguments) async {
+  final args = argParser.parse(arguments);
+  if (args[argHelp]) {
+    print(argParser.usage);
+    return;
+  }
+
+  if (args[argDownload]) {
+    await downloadSpec();
+  }
+
+  final schemaContent = await File(specFile).readAsString();
+  final schemaJson = jsonDecode(schemaContent);
+  final schema = JsonSchema.fromJson(schemaJson);
+
+  final buffer = IndentableStringBuffer();
+  CodeGenerator().writeAll(buffer, schema);
+  final generatedCode = buffer.toString();
+  await File(generatedCodeFile)
+      .writeAsString('$codeFileHeader\n$generatedCode');
+
+  // Format the generated code.
+  await Process.run(Platform.resolvedExecutable, ['format', generatedCodeFile]);
+}
+
+const argDownload = 'download';
+const argHelp = 'help';
+const codeFileHeader = '''
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code was auto-generated by tool/dap/generate_all.dart - do not hand-edit!
+
+import 'package:dds/src/dap/protocol_common.dart';
+import 'package:dds/src/dap/protocol_special.dart';
+''';
+final argParser = ArgParser()
+  ..addFlag(argHelp, hide: true)
+  ..addFlag(argDownload,
+      negatable: false,
+      abbr: 'd',
+      help: 'Download latest version of the DAP spec before generating types');
+final generatedCodeFile =
+    path.join(toolFolder, '../../lib/src/dap/protocol_generated.dart');
+final licenseFile = path.join(specFolder, 'debugAdapterProtocol.license.txt');
+final specFile = path.join(specFolder, 'debugAdapterProtocol.json');
+final specFolder = path.join(toolFolder, 'external_dap_spec');
+final specLicenseUri = Uri.parse(
+    'https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/main/License.txt');
+final specUri = Uri.parse(
+    'https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/gh-pages/debugAdapterProtocol.json');
+final toolFolder = path.dirname(Platform.script.toFilePath());
+
+Future<void> downloadSpec() async {
+  final specResp = await http.get(specUri);
+  final licenseResp = await http.get(specLicenseUri);
+
+  assert(specResp.statusCode == 200);
+  assert(licenseResp.statusCode == 200);
+
+  final licenseHeader = '''
+debugAdapterProtocol.json is an unmodified copy of the DAP Specification,
+downloaded from:
+
+  $specUri
+
+The licence for this file is included below. This accompanying file is the
+version of the specification that was used to generate a portion of the Dart
+code used to support the protocol.
+
+To regenerate the generated code, run the script in "tool/dap/generate_all.dart"
+with no arguments. To download the latest version of the specification before
+regenerating the code, run the same script with the "--download" argument.
+
+---
+''';
+
+  await File(specFile).writeAsString(specResp.body);
+  await File(licenseFile).writeAsString('$licenseHeader\n${licenseResp.body}');
+}
diff --git a/dds/tool/dap/json_schema.dart b/dds/tool/dap/json_schema.dart
new file mode 100644
index 0000000..123882e
--- /dev/null
+++ b/dds/tool/dap/json_schema.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:dds/src/dap/protocol_special.dart';
+
+class JsonSchema {
+  late final Uri dollarSchema;
+  late final Map<String, JsonType> definitions;
+
+  JsonSchema.fromJson(Map<String, Object?> json) {
+    dollarSchema = Uri.parse(json[r'$schema'] as String);
+    definitions = (json['definitions'] as Map<String, Object?>).map((key,
+            value) =>
+        MapEntry(key, JsonType.fromJson(this, value as Map<String, Object?>)));
+  }
+}
+
+class JsonType {
+  final JsonSchema root;
+  final List<JsonType>? allOf;
+  final List<JsonType>? oneOf;
+  final String? description;
+  final String? dollarRef;
+  final List<String>? enumValues;
+  final JsonType? items;
+  final Map<String, JsonType>? properties;
+  final List<String>? required;
+  final String? title;
+  final Either2<String, List<String>>? type;
+
+  JsonType.empty(this.root)
+      : allOf = null,
+        oneOf = null,
+        description = null,
+        dollarRef = null,
+        enumValues = null,
+        items = null,
+        properties = null,
+        required = null,
+        title = null,
+        type = null;
+
+  JsonType.fromJson(this.root, Map<String, Object?> json)
+      : allOf = json['allOf'] == null
+            ? null
+            : (json['allOf'] as List<Object?>)
+                .cast<Map<String, Object?>>()
+                .map((item) => JsonType.fromJson(root, item))
+                .toList(),
+        description = json['description'] as String?,
+        dollarRef = json[r'$ref'] as String?,
+        enumValues = (json['enum'] as List<Object?>?)?.cast<String>(),
+        items = json['items'] == null
+            ? null
+            : JsonType.fromJson(root, json['items'] as Map<String, Object?>),
+        oneOf = json['oneOf'] == null
+            ? null
+            : (json['oneOf'] as List<Object?>)
+                .cast<Map<String, Object?>>()
+                .map((item) => JsonType.fromJson(root, item))
+                .toList(),
+        properties = json['properties'] == null
+            ? null
+            : (json['properties'] as Map<String, Object?>).map((key, value) =>
+                MapEntry(key,
+                    JsonType.fromJson(root, value as Map<String, Object?>))),
+        required = (json['required'] as List<Object?>?)?.cast<String>(),
+        title = json['title'] as String?,
+        type = json['type'] == null
+            ? null
+            : json['type'] is String
+                ? Either2<String, List<String>>.t1(json['type'] as String)
+                : Either2<String, List<String>>.t2(
+                    (json['type'] as List<Object?>).cast<String>());
+
+  /// Creates a dummy type to represent a type that exists outside of the
+  /// generated code (in 'lib/src/dap/protocol_common.dart').
+  JsonType.named(this.root, String name)
+      : allOf = null,
+        oneOf = null,
+        description = null,
+        dollarRef = '#/definitions/$name',
+        enumValues = null,
+        items = null,
+        properties = null,
+        required = null,
+        title = null,
+        type = null;
+}
diff --git a/dds/tool/dap/json_schema_extensions.dart b/dds/tool/dap/json_schema_extensions.dart
new file mode 100644
index 0000000..a84cafd
--- /dev/null
+++ b/dds/tool/dap/json_schema_extensions.dart
@@ -0,0 +1,155 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:collection/collection.dart';
+
+import 'json_schema.dart';
+
+String _toDartType(String type) {
+  if (type.startsWith('#/definitions/')) {
+    return type.replaceAll('#/definitions/', '');
+  }
+  switch (type) {
+    case 'object':
+      return 'Map<String, Object?>';
+    case 'integer':
+      return 'int';
+    case 'number':
+      return 'num';
+    case 'string':
+      return 'String';
+    case 'boolean':
+      return 'bool';
+    case 'null':
+      return 'Null';
+    default:
+      return type;
+  }
+}
+
+String _toDartUnionType(List<String> types) {
+  const allLiteralTypes = {
+    'array',
+    'boolean',
+    'integer',
+    'null',
+    'number',
+    'object',
+    'string'
+  };
+  if (types.length == 7 && allLiteralTypes.containsAll(types)) {
+    return 'Object';
+  }
+  return 'Either${types.length}<${types.map(_toDartType).join(', ')}>';
+}
+
+extension JsonSchemaExtensions on JsonSchema {
+  JsonType typeFor(JsonType type) => type.dollarRef != null
+      // TODO(dantup): Do we need to support more than just refs to definitions?
+      ? definitions[type.refName]!
+      : type;
+
+  Map<String, JsonType> propertiesFor(JsonType type,
+      {bool includeBase = true}) {
+    // Merge this types direct properties with anything from the included
+    // (allOf) types, but excluding those that come from the base class.
+    final baseType = type.baseType;
+    final includedBaseTypes =
+        (type.allOf ?? []).where((t) => includeBase || t != baseType);
+    final properties = {
+      for (final other in includedBaseTypes) ...propertiesFor(typeFor(other)),
+      ...?type.properties,
+    };
+
+    return properties;
+  }
+}
+
+extension JsonTypeExtensions on JsonType {
+  String asDartType({bool isOptional = false}) {
+    final dartType = dollarRef != null
+        ? _toDartType(dollarRef!)
+        : oneOf != null
+            ? _toDartUnionType(oneOf!.map((item) => item.asDartType()).toList())
+            : type!.valueEquals('array')
+                ? 'List<${items!.asDartType()}>'
+                : type!.map(_toDartType, _toDartUnionType);
+
+    return isOptional ? '$dartType?' : dartType;
+  }
+
+  /// Whether this type can have any type of value (Object/dynamic/any).
+  bool get isAny => asDartType() == 'Object';
+
+  /// Whether this type represents a List.
+  bool get isList => type?.valueEquals('array') ?? false;
+
+  /// Whether this type is a simple value that does not need any special handling.
+  bool get isSimple {
+    const _dartSimpleTypes = {
+      'bool',
+      'int',
+      'num',
+      'String',
+      'Map<String, Object?>',
+      'Null',
+    };
+    return _dartSimpleTypes.contains(asDartType());
+  }
+
+  /// Whether this type is a Union type using JSON schema's "oneOf" of where its
+  /// [type] is a list of types.
+  bool get isUnion =>
+      oneOf != null || type != null && type!.map((_) => false, (_) => true);
+
+  /// Whether this type is a reference to another spec type (using `dollarRef`).
+  bool get isSpecType => dollarRef != null;
+
+  /// Whether [propertyName] is a required for this type or its base types.
+  bool requiresField(String propertyName) {
+    if (required?.contains(propertyName) ?? false) {
+      return true;
+    }
+    if (allOf?.any((type) => root.typeFor(type).requiresField(propertyName)) ??
+        false) {
+      return true;
+    }
+
+    return false;
+  }
+
+  /// The name of the type that this one references.
+  String get refName => dollarRef!.replaceAll('#/definitions/', '');
+
+  /// The literal value of this type, if it can have only one.
+  ///
+  /// These are represented in the spec using an enum with only a single value.
+  String? get literalValue => enumValues?.singleOrNull;
+
+  /// The base type for this type. Base types are inferred by a type using
+  /// allOf and the first listed type being a reference (dollarRef) to another
+  /// spec type.
+  JsonType? get baseType {
+    final all = allOf;
+    if (all != null && all.length > 1 && all.first.dollarRef != null) {
+      return all.first;
+    }
+    return null;
+  }
+
+  /// The list of possible types allowed by this union.
+  ///
+  /// May be represented using `oneOf` or a list of types in `type`.
+  List<JsonType> get unionTypes {
+    final types = oneOf ??
+        // Fabricate a union for types where "type" is an array of literal types:
+        // ['a', 'b']
+        type!.map(
+          (_) => throw 'unexpected non-union in isUnion condition',
+          (types) =>
+              types.map((t) => JsonType.fromJson(root, {'type': t})).toList(),
+        )!;
+    return types;
+  }
+}
diff --git a/dds/tool/dap/run_server.dart b/dds/tool/dap/run_server.dart
new file mode 100644
index 0000000..69f90f1
--- /dev/null
+++ b/dds/tool/dap/run_server.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:dds/src/dap/server.dart';
+
+Future<void> main(List<String> arguments) async {
+  final args = argParser.parse(arguments);
+  if (args[argHelp]) {
+    print(argParser.usage);
+    return;
+  }
+
+  final port = int.parse(args[argPort]);
+  final host = args[argHost];
+
+  await DapServer.create(
+    host: host,
+    port: port,
+    logger: args[argVerbose] ? print : null,
+  );
+}
+
+const argHelp = 'help';
+const argHost = 'host';
+const argPort = 'port';
+const argVerbose = 'verbose';
+final argParser = ArgParser()
+  ..addFlag(argHelp, hide: true)
+  ..addOption(
+    argHost,
+    defaultsTo: 'localhost',
+    help: 'The hostname/IP to bind the server to',
+  )
+  ..addOption(
+    argPort,
+    abbr: 'p',
+    defaultsTo: DapServer.defaultPort.toString(),
+    help: 'The port to bind the server to',
+  )
+  ..addFlag(
+    argVerbose,
+    abbr: 'v',
+    help: 'Whether to print diagnostic output to stdout',
+  );
diff --git a/devtools_shared/BUILD.gn b/devtools_shared/BUILD.gn
new file mode 100644
index 0000000..aba8027
--- /dev/null
+++ b/devtools_shared/BUILD.gn
@@ -0,0 +1,26 @@
+# This file is generated by package_importer.py for devtools_shared-2.3.3
+
+import("//build/dart/dart_library.gni")
+
+dart_library("devtools_shared") {
+  package_name = "devtools_shared"
+
+  language_version = "2.12"
+
+  disable_analysis = true
+
+  deps = [
+    "//third_party/dart-pkg/pub/vm_service",
+  ]
+
+  sources = [
+    "devtools_shared.dart",
+    "src/devtools_api.dart",
+    "src/memory/adb_memory_info.dart",
+    "src/memory/class_heap_detail_stats.dart",
+    "src/memory/event_sample.dart",
+    "src/memory/heap_sample.dart",
+    "src/memory/heap_space.dart",
+    "src/memory/memory_json.dart",
+  ]
+}
diff --git a/devtools_shared/CHANGELOG.md b/devtools_shared/CHANGELOG.md
new file mode 100644
index 0000000..bf52664
--- /dev/null
+++ b/devtools_shared/CHANGELOG.md
@@ -0,0 +1,14 @@
+## 2.3.0
+- Migrate to null safety.
+## 0.2.3
+- Coordinated release with DevTools 0.2.3.
+## 0.2.2
+- Simplified devtools_api.dart.
+## 0.2.1
+- Added devtools_api.dart to devtools_shared.
+## 0.2.0
+- Added field to Memory JSON file "dartDevToolsScreen": "memory".
+## 0.1.0
+- Updated MemoryJson to expose header and footer parts of the JSON file. The data portion is still the persisted List<HeapSample>.  Exposed encodeHeapSample (to add a single HeapSample) and encodeAnotherHeapSample (when adding more than one to the list - comma is added).
+## 0.0.1
+- initial release
diff --git a/devtools_shared/LICENSE b/devtools_shared/LICENSE
new file mode 100644
index 0000000..687fb49
--- /dev/null
+++ b/devtools_shared/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2020 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/devtools_shared/README.md b/devtools_shared/README.md
new file mode 100644
index 0000000..0a440f2
--- /dev/null
+++ b/devtools_shared/README.md
@@ -0,0 +1,10 @@
+## What is this?
+
+This is a package shared between devtools_app, devtools_server, and external users accessing
+exported JSON files. This package contains structures describing the format of JSON files e.g.,
+HeapSample, memory structures collected from the Dart VM (HeapSpace) and collected memory info
+from Android's ADB (AdbMemoryInfo).
+
+## Terms and Privacy
+
+By using Dart DevTools, you agree to the [Google Terms of Service](https://policies.google.com/terms).
diff --git a/devtools_shared/devtools_shared.iml b/devtools_shared/devtools_shared.iml
new file mode 100644
index 0000000..ae9af97
--- /dev/null
+++ b/devtools_shared/devtools_shared.iml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
+      <excludeFolder url="file://$MODULE_DIR$/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Dart SDK" level="project" />
+    <orderEntry type="library" name="Dart Packages" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/devtools_shared/lib/devtools_shared.dart b/devtools_shared/lib/devtools_shared.dart
new file mode 100644
index 0000000..2a00d98
--- /dev/null
+++ b/devtools_shared/lib/devtools_shared.dart
@@ -0,0 +1,11 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'src/devtools_api.dart';
+export 'src/memory/adb_memory_info.dart';
+export 'src/memory/class_heap_detail_stats.dart';
+export 'src/memory/event_sample.dart';
+export 'src/memory/heap_sample.dart';
+export 'src/memory/heap_space.dart';
+export 'src/memory/memory_json.dart';
diff --git a/devtools_shared/lib/src/devtools_api.dart b/devtools_shared/lib/src/devtools_api.dart
new file mode 100644
index 0000000..1353e4c
--- /dev/null
+++ b/devtools_shared/lib/src/devtools_api.dart
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// All server APIs prefix:
+const String apiPrefix = 'api/';
+
+/// Flutter GA properties APIs:
+const String apiGetFlutterGAEnabled = '${apiPrefix}getFlutterGAEnabled';
+const String apiGetFlutterGAClientId = '${apiPrefix}getFlutterGAClientId';
+
+/// DevTools GA properties APIs:
+const String apiResetDevTools = '${apiPrefix}resetDevTools';
+const String apiGetDevToolsFirstRun = '${apiPrefix}getDevToolsFirstRun';
+const String apiGetDevToolsEnabled = '${apiPrefix}getDevToolsEnabled';
+const String apiSetDevToolsEnabled = '${apiPrefix}setDevToolsEnabled';
+
+/// Property name to apiSetDevToolsEnabled the DevToolsEnabled is the name used
+/// in queryParameter:
+const String devToolsEnabledPropertyName = 'enabled';
+
+/// Survey properties APIs:
+/// setActiveSurvey sets the survey property to fetch and save JSON values e.g., Q1-2020
+const String apiSetActiveSurvey = '${apiPrefix}setActiveSurvey';
+
+/// Survey name passed in apiSetActiveSurvey, the activeSurveyName is the property name
+/// passed as a queryParameter and is the property in ~/.devtools too.
+const String activeSurveyName = 'activeSurveyName';
+
+/// Returns the surveyActionTaken of the activeSurvey (apiSetActiveSurvey).
+const String apiGetSurveyActionTaken = '${apiPrefix}getSurveyActionTaken';
+
+/// Sets the surveyActionTaken of the of the activeSurvey (apiSetActiveSurvey).
+const String apiSetSurveyActionTaken = '${apiPrefix}setSurveyActionTaken';
+
+/// Property name to apiSetSurveyActionTaken the surveyActionTaken is the name
+/// passed in queryParameter:
+const String surveyActionTakenPropertyName = 'surveyActionTaken';
+
+/// Returns the surveyShownCount of the of the activeSurvey (apiSetActiveSurvey).
+const String apiGetSurveyShownCount = '${apiPrefix}getSurveyShownCount';
+
+/// Increments the surveyShownCount of the of the activeSurvey (apiSetActiveSurvey).
+const String apiIncrementSurveyShownCount =
+    '${apiPrefix}incrementSurveyShownCount';
+
+/// Returns the base app size file, if present.
+const String apiGetBaseAppSizeFile = '${apiPrefix}getBaseAppSizeFile';
+
+/// Returns the test app size file used for comparing two files in a diff, if
+/// present.
+const String apiGetTestAppSizeFile = '${apiPrefix}getTestAppSizeFile';
+
+const String baseAppSizeFilePropertyName = 'appSizeBase';
+
+const String testAppSizeFilePropertyName = 'appSizeTest';
diff --git a/devtools_shared/lib/src/memory/adb_memory_info.dart b/devtools_shared/lib/src/memory/adb_memory_info.dart
new file mode 100644
index 0000000..04e695c
--- /dev/null
+++ b/devtools_shared/lib/src/memory/adb_memory_info.dart
@@ -0,0 +1,143 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(terry): Need the iOS version of this data.
+/// Android ADB dumpsys meminfo data.
+class AdbMemoryInfo {
+  AdbMemoryInfo(
+    this.realtime,
+    this.javaHeap,
+    this.nativeHeap,
+    this.code,
+    this.stack,
+    this.graphics,
+    this.other,
+    this.system,
+    this.total,
+  );
+
+  /// All data inside of AdbMemoryInfo is in total bytes. When receiving ADB data
+  /// from the service extension (directly from ADB) then the data is in kilobytes.
+  /// See the factory constructor fromJsonInKB.
+  factory AdbMemoryInfo.fromJson(Map<String, dynamic> json) => AdbMemoryInfo(
+        json[realTimeKey] as int,
+        json[javaHeapKey] as int,
+        json[nativeHeapKey] as int,
+        json[codeKey] as int,
+        json[stackKey] as int,
+        json[graphicsKey] as int,
+        json[otherKey] as int,
+        json[systemKey] as int,
+        json[totalKey] as int,
+      );
+
+  /// Use when converting data received from the service extension, directly from
+  /// ADB. All data received from ADB dumpsys meminfo is in kilobytes must adjust to
+  /// total bytes for AdbMemoryInfo data.
+  factory AdbMemoryInfo.fromJsonInKB(
+    Map<String, dynamic> json,
+  ) {
+    final int realTime = json[realTimeKey];
+    int javaHeap = json[javaHeapKey];
+    int nativeHeap = json[nativeHeapKey];
+    int code = json[codeKey];
+    int stack = json[stackKey];
+    int graphics = json[graphicsKey];
+    int other = json[otherKey];
+    int system = json[systemKey];
+    int total = json[totalKey];
+
+    // Convert to total bytes.
+    javaHeap *= 1024;
+    nativeHeap *= 1024;
+    code *= 1024;
+    stack *= 1024;
+    graphics *= 1024;
+    other *= 1024;
+    system *= 1024;
+    total *= 1024;
+
+    return AdbMemoryInfo(
+      realTime,
+      javaHeap,
+      nativeHeap,
+      code,
+      stack,
+      graphics,
+      other,
+      system,
+      total,
+    );
+  }
+
+  /// JSON keys of data retrieved from ADB tool.
+  static const String realTimeKey = 'Realtime';
+  static const String javaHeapKey = 'Java Heap';
+  static const String nativeHeapKey = 'Native Heap';
+  static const String codeKey = 'Code';
+  static const String stackKey = 'Stack';
+  static const String graphicsKey = 'Graphics';
+  static const String otherKey = 'Private Other';
+  static const String systemKey = 'System';
+  static const String totalKey = 'Total';
+
+  Map<String, dynamic> toJson() => <String, dynamic>{
+        realTimeKey: realtime,
+        javaHeapKey: javaHeap,
+        nativeHeapKey: nativeHeap,
+        codeKey: code,
+        stackKey: stack,
+        graphicsKey: graphics,
+        otherKey: other,
+        systemKey: system,
+        totalKey: total,
+      };
+
+  /// Create an empty AdbMemoryInfo (all values are)
+  static AdbMemoryInfo empty() => AdbMemoryInfo(0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+  /// Milliseconds since the device was booted (value zero) including deep sleep.
+  ///
+  /// This clock is guaranteed to be monotonic, and continues to tick even
+  /// in power saving mode. The value zero is Unix Epoch UTC (Jan 1, 1970 00:00:00).
+  /// This DateTime, from USA PST, would be Dec 31, 1960 16:00:00 (UTC - 8 hours).
+  final int realtime;
+
+  /// All remaining values are received from ADB in kilobytes but converted to total
+  /// bytes using the AdbMemoryInfo.fromJsonInKilobytes factory.
+  final int javaHeap;
+
+  final int nativeHeap;
+
+  final int code;
+
+  final int stack;
+
+  final int graphics;
+
+  final int other;
+
+  final int system;
+
+  final int total;
+
+  DateTime get realtimeDT => DateTime.fromMillisecondsSinceEpoch(realtime);
+
+  /// Duration the device has been up since boot time.
+  Duration get bootDuration => Duration(milliseconds: realtime);
+
+  @override
+  String toString() => '[AdbMemoryInfo '
+      '$realTimeKey: $realtime, '
+      'realtimeDT: $realtimeDT, '
+      'durationBoot: $bootDuration, '
+      '$javaHeapKey: $javaHeap, '
+      '$nativeHeapKey: $nativeHeap, '
+      '$codeKey: $code, '
+      '$stackKey: $stack, '
+      '$graphicsKey: $graphics, '
+      '$otherKey: $other, '
+      '$systemKey: $system, '
+      '$totalKey: $total]';
+}
diff --git a/devtools_shared/lib/src/memory/class_heap_detail_stats.dart b/devtools_shared/lib/src/memory/class_heap_detail_stats.dart
new file mode 100644
index 0000000..4192cda
--- /dev/null
+++ b/devtools_shared/lib/src/memory/class_heap_detail_stats.dart
@@ -0,0 +1,63 @@
+// Copyright 2021 The Chromium Authors. 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:vm_service/vm_service.dart';
+
+/// Entries for each class statistics
+class ClassHeapDetailStats {
+  ClassHeapDetailStats(
+    this.classRef, {
+    required int bytes,
+    int deltaBytes = 0,
+    required int instances,
+    int deltaInstances = 0,
+    bool traceAllocations = false,
+  })  : bytesCurrent = bytes,
+        bytesDelta = deltaBytes,
+        instancesCurrent = instances,
+        instancesDelta = deltaInstances,
+        isStacktraced = traceAllocations;
+
+  factory ClassHeapDetailStats.fromJson(Map<String, dynamic> json) {
+    final classId = json['class']['id'];
+    final className = json['class']['name'];
+
+    return ClassHeapDetailStats(
+      ClassRef(id: classId, name: className, library: null),
+      bytes: json['bytesCurrent'] as int,
+      deltaBytes: json['bytesDelta'] as int,
+      instances: json['instancesCurrent'] as int,
+      deltaInstances: json['instancesDelta'] as int,
+      traceAllocations: json['isStackedTraced'] as bool,
+    );
+  }
+
+  Map<String, dynamic> toJson() => {
+        'class': {'id': classRef.id, 'name': classRef.name},
+        'bytesCurrent': bytesCurrent,
+        'bytesDelta': bytesDelta,
+        'instancesCurrent': instancesCurrent,
+        'instancesDelta': instancesDelta,
+        'isStackedTraced': isStacktraced,
+      };
+
+  /// Version of ClassHeapDetailsStats payload.
+  static const version = 1;
+
+  final ClassRef classRef;
+
+  final int instancesCurrent;
+
+  int instancesDelta;
+
+  final int bytesCurrent;
+
+  int bytesDelta;
+
+  bool isStacktraced;
+
+  @override
+  String toString() => '[ClassHeapDetailStats class: ${classRef.name}, '
+      'count: $instancesCurrent, bytes: $bytesCurrent]';
+}
diff --git a/devtools_shared/lib/src/memory/event_sample.dart b/devtools_shared/lib/src/memory/event_sample.dart
new file mode 100644
index 0000000..0f14a9b
--- /dev/null
+++ b/devtools_shared/lib/src/memory/event_sample.dart
@@ -0,0 +1,311 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+/// Monitor heap object allocations (in the VM).  The allocation monitor will
+/// cause 'start' event exist in the HeapSample. Immediately afterwards a
+/// 'continues' event is added on each subsequent timestamp tick (HeapSample)
+/// until another monitor start event.  A 'reset' event stops the 'continues'
+/// event for one timestamp tick with a 'reset' event. Immediately after the
+/// reset event a 'continues' event will again appear in the HeapSample's
+/// MemoryEventInfo - until a new monitor is started. One monitor exists per
+/// VM connection.
+class AllocationAccumulator {
+  AllocationAccumulator(this._start, this._continues, this._reset);
+
+  AllocationAccumulator.start()
+      : _start = true,
+        _continues = false,
+        _reset = false;
+  AllocationAccumulator.continues()
+      : _start = false,
+        _continues = true,
+        _reset = false;
+  AllocationAccumulator.reset()
+      : _start = false,
+        _continues = false,
+        _reset = true;
+
+  factory AllocationAccumulator.fromJson(Map<String, dynamic> json) =>
+      AllocationAccumulator(
+        json['start'] as bool,
+        json['continues'] as bool,
+        json['reset'] as bool,
+      );
+
+  Map<String, dynamic> toJson() => <String, dynamic>{
+        'start': _start,
+        'continues': _continues,
+        'reset': _reset,
+      };
+
+  static AllocationAccumulator empty() =>
+      AllocationAccumulator(false, false, false);
+
+  bool get isEmpty => !isStart && !isContinuesVisible && !isReset;
+
+  final bool _start;
+
+  final bool _continues;
+  bool continuesVisible = false;
+
+  final bool _reset;
+
+  bool get isStart => _start;
+
+  bool get isContinues => _continues;
+  bool get isContinuesVisible => isContinues && continuesVisible;
+
+  bool get isReset => _reset;
+
+  @override
+  String toString() => '[AllocationAccumulator '
+      '${const JsonEncoder.withIndent('  ').convert(toJson())}]';
+}
+
+class ExtensionEvent {
+  ExtensionEvent(this.timestamp, this.eventKind, this.data)
+      : customEventName = null;
+
+  ExtensionEvent.custom(
+    this.timestamp,
+    this.eventKind,
+    this.customEventName,
+    this.data,
+  );
+
+  factory ExtensionEvent.fromJson(Map<String, dynamic> json) =>
+      ExtensionEvent.custom(
+        json['timestamp'] as int?,
+        json['eventKind'] as String?,
+        json['customEventName'] as String?,
+        json['data'] as Map<String, Object>?,
+      );
+
+  Map<String, dynamic> toJson() => <String, dynamic>{
+        'timestamp': timestamp,
+        'eventKind': eventKind,
+        'data': data,
+        'customEventName': customEventName,
+      };
+
+  static ExtensionEvent empty() =>
+      ExtensionEvent.custom(null, null, null, null);
+
+  bool get isEmpty =>
+      timestamp == null &&
+      eventKind == null &&
+      data == null &&
+      customEventName == null;
+
+  final int? timestamp;
+
+  final String? eventKind;
+
+  final Map<String, Object>? data;
+
+  final String? customEventName;
+
+  @override
+  String toString() => '[ExtensionEvent '
+      '${const JsonEncoder.withIndent('  ').convert(toJson())}]';
+}
+
+class ExtensionEvents {
+  ExtensionEvents(List<ExtensionEvent> events) {
+    theEvents.addAll(events);
+  }
+
+  factory ExtensionEvents.fromJson(Map<String, Object> json) {
+    final List<ExtensionEvent> events = [];
+
+    json.forEach((key, value) {
+      final event = ExtensionEvent.fromJson(value as Map<String, dynamic>);
+      events.add(event);
+    });
+
+    return ExtensionEvents(events);
+  }
+
+  final theEvents = <ExtensionEvent>[];
+
+  bool get isEmpty => theEvents.isEmpty;
+
+  bool get isNotEmpty => theEvents.isNotEmpty;
+
+  void clear() => theEvents.clear();
+
+  Map<String, dynamic> toJson() {
+    final eventsAsJson = <String, dynamic>{};
+    var index = 0;
+    for (var event in theEvents) {
+      eventsAsJson['$index'] = event.toJson();
+      index++;
+    }
+
+    return eventsAsJson;
+  }
+
+  @override
+  String toString() => '[ExtensionEvents = '
+      '${const JsonEncoder.withIndent('  ').convert(toJson())}]';
+}
+
+class EventSample {
+  EventSample(
+    this.timestamp,
+    this.isEventGC,
+    this.isEventSnapshot,
+    this.isEventSnapshotAuto,
+    this.allocationAccumulator,
+    this.extensionEvents,
+  );
+
+  EventSample.gcEvent(this.timestamp, {ExtensionEvents? events})
+      : isEventGC = true,
+        isEventSnapshot = false,
+        isEventSnapshotAuto = false,
+        allocationAccumulator = null,
+        extensionEvents = events;
+
+  EventSample.snapshotEvent(
+    this.timestamp, {
+    snapshotAuto = false,
+    ExtensionEvents? events,
+  })  : isEventGC = false,
+        isEventSnapshot = !snapshotAuto,
+        isEventSnapshotAuto = snapshotAuto,
+        allocationAccumulator = null,
+        extensionEvents = events;
+
+  EventSample.accumulatorStart(
+    this.timestamp, {
+    ExtensionEvents? events,
+  })  : isEventGC = false,
+        isEventSnapshot = false,
+        isEventSnapshotAuto = false,
+        allocationAccumulator = AllocationAccumulator.start(),
+        extensionEvents = events;
+
+  EventSample.accumulatorContinues(
+    this.timestamp, {
+    ExtensionEvents? events,
+  })  : isEventGC = false,
+        isEventSnapshot = false,
+        isEventSnapshotAuto = false,
+        allocationAccumulator = AllocationAccumulator.continues(),
+        extensionEvents = events;
+
+  EventSample.accumulatorReset(
+    this.timestamp, {
+    ExtensionEvents? events,
+  })  : isEventGC = false,
+        isEventSnapshot = false,
+        isEventSnapshotAuto = false,
+        allocationAccumulator = AllocationAccumulator.reset(),
+        extensionEvents = events;
+
+  EventSample.extensionEvent(this.timestamp, this.extensionEvents)
+      : isEventGC = false,
+        isEventSnapshot = false,
+        isEventSnapshotAuto = false,
+        allocationAccumulator = null;
+
+  factory EventSample.fromJson(Map<String, dynamic> json) => EventSample(
+      json['timestamp'] as int,
+      (json['gcEvent'] as bool?) ?? false,
+      (json['snapshotEvent'] as bool?) ?? false,
+      (json['snapshotAutoEvent'] as bool?) ?? false,
+      json['allocationAccumulatorEvent'] != null
+          ? AllocationAccumulator.fromJson(json['allocationAccumulatorEvent'])
+          : null,
+      json['extensionEvents'] != null
+          ? ExtensionEvents.fromJson(json['extensionEvents'])
+          : null);
+
+  Map<String, dynamic> toJson() => <String, dynamic>{
+        'timestamp': timestamp,
+        'gcEvent': isEventGC,
+        'snapshotEvent': isEventSnapshot,
+        'snapshotAutoEvent': isEventSnapshotAuto,
+        'allocationAccumulatorEvent': allocationAccumulator?.toJson(),
+        'extensionEvents': extensionEvents?.toJson(),
+      };
+
+  EventSample clone(int timestamp, {ExtensionEvents? extensionEvents}) =>
+      EventSample(
+        timestamp,
+        isEventGC,
+        isEventSnapshot,
+        isEventSnapshotAuto,
+        allocationAccumulator,
+        extensionEvents,
+      );
+
+  /// Create an empty event (all values are nothing)
+  static EventSample empty() => EventSample(
+        -1,
+        false,
+        false,
+        false,
+        AllocationAccumulator.empty(),
+        null,
+      );
+
+  bool get isEmpty => timestamp == -1;
+
+  /// Version of EventSample JSON payload.
+  static const version = 1;
+
+  final int timestamp;
+
+  final bool isEventGC;
+
+  final bool isEventSnapshot;
+
+  final bool isEventSnapshotAuto;
+
+  bool get isEventAllocationAccumulator => allocationAccumulator != null;
+
+  bool get hasExtensionEvents => extensionEvents != null;
+
+  final AllocationAccumulator? allocationAccumulator;
+
+  final ExtensionEvents? extensionEvents;
+
+  @override
+  String toString() => '[EventSample timestamp: $timestamp = '
+      '${const JsonEncoder.withIndent('  ').convert(toJson())}]';
+}
+
+/// Engine's Raster Cache estimates.
+class RasterCache {
+  RasterCache._({required this.layerBytes, required this.pictureBytes});
+
+  factory RasterCache.fromJson(Map<String, dynamic> json) {
+    return RasterCache._(
+      layerBytes: json['layerBytes'],
+      pictureBytes: json['pictureBytes'],
+    );
+  }
+
+  static RasterCache empty() => RasterCache._(layerBytes: 0, pictureBytes: 0);
+
+  static RasterCache? parse(Map<String, dynamic>? json) =>
+      json == null ? null : RasterCache.fromJson(json);
+
+  int layerBytes;
+
+  int pictureBytes;
+
+  Map<String, dynamic> toJson() => {
+        'layerBytes': layerBytes,
+        'pictureBytes': pictureBytes,
+      };
+
+  @override
+  String toString() => '[RasterCache '
+      '${const JsonEncoder.withIndent('  ').convert(toJson())}]';
+}
diff --git a/devtools_shared/lib/src/memory/heap_sample.dart b/devtools_shared/lib/src/memory/heap_sample.dart
new file mode 100644
index 0000000..6000ab5
--- /dev/null
+++ b/devtools_shared/lib/src/memory/heap_sample.dart
@@ -0,0 +1,85 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'adb_memory_info.dart';
+import 'event_sample.dart';
+
+/// DevTools Plotted and JSON persisted memory information.
+class HeapSample {
+  HeapSample(
+    this.timestamp,
+    this.rss,
+    this.capacity,
+    this.used,
+    this.external,
+    this.isGC,
+    AdbMemoryInfo? adbMemoryInfo,
+    EventSample? memoryEventInfo,
+    RasterCache? rasterCache,
+  )   : adbMemoryInfo = adbMemoryInfo ?? AdbMemoryInfo.empty(),
+        memoryEventInfo = memoryEventInfo ?? EventSample.empty(),
+        rasterCache = rasterCache ?? RasterCache.empty();
+
+  factory HeapSample.fromJson(Map<String, dynamic> json) {
+    final adbMemoryInfo = json['adb_memoryInfo'];
+    final memoryEventInfo = json['memory_eventInfo'];
+    final rasterCache = json['raster_cache'];
+    return HeapSample(
+      json['timestamp'] as int,
+      json['rss'] as int,
+      json['capacity'] as int,
+      json['used'] as int,
+      json['external'] as int,
+      json['gc'] as bool,
+      adbMemoryInfo != null
+          ? AdbMemoryInfo.fromJson(adbMemoryInfo)
+          : AdbMemoryInfo.empty(),
+      memoryEventInfo != null
+          ? EventSample.fromJson(memoryEventInfo)
+          : EventSample.empty(),
+      rasterCache != null
+          ? RasterCache.fromJson(rasterCache)
+          : RasterCache.empty(),
+    );
+  }
+
+  Map<String, dynamic> toJson() => <String, dynamic>{
+        'timestamp': timestamp,
+        'rss': rss,
+        'capacity': capacity,
+        'used': used,
+        'external': external,
+        'gc': isGC,
+        'adb_memoryInfo': adbMemoryInfo.toJson(),
+        'memory_eventInfo': memoryEventInfo.toJson(),
+        'raster_cache': rasterCache.toJson(),
+      };
+
+  /// Version of HeapSample JSON payload.
+  static const version = 1;
+
+  final int timestamp;
+
+  final int rss;
+
+  final int capacity;
+
+  final int used;
+
+  final int external;
+
+  final bool isGC;
+
+  EventSample memoryEventInfo;
+
+  AdbMemoryInfo adbMemoryInfo;
+
+  RasterCache rasterCache;
+
+  @override
+  String toString() => '[HeapSample timestamp: $timestamp, '
+      '${const JsonEncoder.withIndent('  ').convert(toJson())}]';
+}
diff --git a/devtools_shared/lib/src/memory/heap_space.dart b/devtools_shared/lib/src/memory/heap_space.dart
new file mode 100644
index 0000000..7d8cde4
--- /dev/null
+++ b/devtools_shared/lib/src/memory/heap_space.dart
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// HeapSpace of Dart VM collected heap data.
+class HeapSpace {
+  HeapSpace._fromJson(this.json)
+      : avgCollectionPeriodMillis = json['avgCollectionPeriodMillis'],
+        capacity = json['capacity'],
+        collections = json['collections'],
+        external = json['external'],
+        name = json['name'],
+        time = json['time'],
+        used = json['used'];
+
+  static HeapSpace? parse(Map<String, dynamic>? json) =>
+      json == null ? null : HeapSpace._fromJson(json);
+
+  final Map<String, dynamic> json;
+
+  final double? avgCollectionPeriodMillis;
+
+  final int? capacity;
+
+  final int? collections;
+
+  final int? external;
+
+  final String? name;
+
+  final double? time;
+
+  final int? used;
+
+  Map<String, dynamic> toJson() {
+    final json = <String, dynamic>{};
+    json['type'] = 'HeapSpace';
+    json.addAll({
+      'avgCollectionPeriodMillis': avgCollectionPeriodMillis,
+      'capacity': capacity,
+      'collections': collections,
+      'external': external,
+      'name': name,
+      'time': time,
+      'used': used,
+    });
+    return json;
+  }
+
+  @override
+  String toString() => '[HeapSpace]';
+}
diff --git a/devtools_shared/lib/src/memory/memory_json.dart b/devtools_shared/lib/src/memory/memory_json.dart
new file mode 100644
index 0000000..965e4de
--- /dev/null
+++ b/devtools_shared/lib/src/memory/memory_json.dart
@@ -0,0 +1,284 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'class_heap_detail_stats.dart';
+import 'heap_sample.dart';
+
+abstract class DecodeEncode<T> {
+  int get version;
+
+  String encode(T sample);
+
+  /// More than one Encoded entry, add a comma and the Encoded entry.
+  String encodeAnother(T sample);
+
+  T fromJson(Map<String, dynamic> json);
+}
+
+abstract class MemoryJson<T> implements DecodeEncode<T> {
+  MemoryJson();
+
+  /// Given a JSON string representing an array of HeapSample, decode to a
+  /// List of HeapSample.
+  MemoryJson.decode(
+    String payloadName, {
+    required String argJsonString,
+    Map<String, dynamic>? argDecodedMap,
+  }) {
+    final Map<String, dynamic> decodedMap =
+        argDecodedMap == null ? jsonDecode(argJsonString) : argDecodedMap;
+    final Map<String, dynamic> samplesPayload = decodedMap['$payloadName'];
+
+    final payloadVersion = samplesPayload['$jsonVersionField'];
+    final payloadDevToolsScreen = samplesPayload['$jsonDevToolsScreenField'];
+
+    if (payloadVersion != version) {
+      // TODO(terry): Convert Payload TBD - only one version today.
+      // TODO(terry): Notify user the file is being converted.
+      // TODO(terry): Consider moving config_specific/logger/ into shared to
+      //              use logger instead of print.
+      print(
+          'WARNING: Unable to convert JSON memory file payload version=$payloadVersion.');
+      // TODO(terry): After conversion update payloadVersion to version;
+    }
+
+    _memoryPayload = payloadDevToolsScreen == devToolsScreenValueMemory;
+    _payloadVersion = payloadVersion;
+
+    // Any problem return (data is empty).
+    if (!isMatchedVersion || !isMemoryPayload) return;
+
+    final List dynamicList = samplesPayload['$jsonDataField'];
+    for (var index = 0; index < dynamicList.length; index++) {
+      final sample = fromJson(dynamicList[index]);
+      data.add(sample);
+    }
+  }
+
+  late final int _payloadVersion;
+
+  int get payloadVersion => _payloadVersion;
+
+  /// Imported JSON data loaded and converted, if necessary, to the latest version.
+  bool get isMatchedVersion => _payloadVersion == version;
+
+  late final bool _memoryPayload;
+
+  /// JSON payload field "dart<T>DevToolsScreen" has a value of "memory" e.g.,
+  ///   "dartDevToolsScreen": "memory"
+  bool get isMemoryPayload => _memoryPayload;
+
+  /// If data is empty check isMatchedVersion and isMemoryPayload to ensure the
+  /// JSON file loaded is a memory file.
+  final data = <T>[];
+
+  static const String jsonDevToolsScreenField = 'dartDevToolsScreen';
+  // TODO(terry): Expose Timeline.
+  // const String _devToolsScreenValueTimeline = 'timeline';
+  static const String devToolsScreenValueMemory = 'memory';
+  static const String jsonVersionField = 'version';
+  static const String jsonDataField = 'data';
+
+  /// Trailer portion:
+  static String get trailer => '\n]\n}}';
+}
+
+class SamplesMemoryJson extends MemoryJson<HeapSample> {
+  SamplesMemoryJson();
+
+  /// Given a JSON string representing an array of HeapSample, decode to a
+  /// List of HeapSample.
+  SamplesMemoryJson.decode({
+    required String argJsonString,
+    Map<String, dynamic>? argDecodedMap,
+  }) : super.decode(
+          _jsonMemoryPayloadField,
+          argJsonString: argJsonString,
+          argDecodedMap: argDecodedMap,
+        );
+
+  /// Exported JSON payload of collected memory statistics.
+  static const String _jsonMemoryPayloadField = 'samples';
+
+  /// Structure of the memory JSON file:
+  ///
+  /// {
+  ///   "samples": {
+  ///     "version": 1,
+  ///     "dartDevToolsScreen": "memory"
+  ///     "data": [
+  ///       Encoded Heap Sample see section below.
+  ///     ]
+  ///   }
+  /// }
+
+  /// Header portion (memoryJsonHeader) e.g.,
+  /// =======================================
+  /// {
+  ///   "samples": {
+  ///     "version": 1,
+  ///     "dartDevToolsScreen": "memory"
+  ///     "data": [
+  ///
+  /// Encoded Allocations entry (SamplesMemoryJson),
+  /// ==============================================================================
+  ///     {
+  ///       "timestamp":1581540967479,
+  ///       "rss":211419136,
+  ///       "capacity":50956576,
+  ///       "used":41384952,
+  ///       "external":166176,
+  ///       "gc":false,
+  ///       "adb_memoryInfo":{
+  ///         "Realtime":450147758,
+  ///         "Java Heap":7416,
+  ///         "Native Heap":41712,
+  ///         "Code":12644,
+  ///         "Stack":52,
+  ///         "Graphics":0,
+  ///         "Private Other":94420,
+  ///         "System":6178,
+  ///         "Total":162422
+  ///       }
+  ///     },
+  ///
+  /// Trailer portion (memoryJsonTrailer) e.g.,
+  /// =========================================
+  ///     ]
+  ///   }
+  /// }
+
+  @override
+  int get version => HeapSample.version;
+
+  /// Encoded Heap Sample
+  @override
+  String encode(HeapSample sample) => jsonEncode(sample);
+
+  /// More than one Encoded Heap Sample, add a comma and the Encoded Heap Sample.
+  @override
+  String encodeAnother(HeapSample sample) => ',\n${jsonEncode(sample)}';
+
+  @override
+  HeapSample fromJson(Map<String, dynamic> json) => HeapSample.fromJson(json);
+
+  /// Given a list of HeapSample, encode as a Json string.
+  static String encodeList(List<HeapSample> data) {
+    final samplesJson = SamplesMemoryJson();
+    final result = StringBuffer();
+
+    // Iterate over all HeapSamples collected.
+    data.map((f) {
+      final encodedValue = result.isNotEmpty
+          ? samplesJson.encodeAnother(f)
+          : samplesJson.encode(f);
+      result.write(encodedValue);
+    }).toList();
+
+    return '$header$result${MemoryJson.trailer}';
+  }
+
+  static String get header => '{"$_jsonMemoryPayloadField": {'
+      '"${MemoryJson.jsonVersionField}": ${HeapSample.version}, '
+      '"${MemoryJson.jsonDevToolsScreenField}": "${MemoryJson.devToolsScreenValueMemory}", '
+      '"${MemoryJson.jsonDataField}": [\n';
+}
+
+/// Structure of the memory JSON file:
+///
+/// {
+///   "allocations": {
+///     "version": 1,
+///     "dartDevToolsScreen": "memory"
+///     "data": [
+///       Encoded ClassHeapDetailStats see section below.
+///     ]
+///   }
+/// }
+
+/// Header portion (memoryJsonHeader) e.g.,
+/// =======================================
+/// {
+///   "allocations": {
+///     "version": 1,
+///     "dartDevToolsScreen": "memory"
+///     "data": [
+///
+/// Encoded Allocations entry (AllocationMemoryJson),
+/// ==============================================================================
+///     {
+///       "class" : {
+///          id: "classes/1"
+///          name: "AClassName"
+///        },
+///       "instancesCurrent": 100,
+///       "instancesDelta": 0,
+///       "bytesCurrent": 55,
+///       "bytesDelta": 5,
+///       "isStacktraced": false,
+///     },
+///
+/// Trailer portion (memoryJsonTrailer) e.g.,
+/// =========================================
+///     ]
+///   }
+/// }
+class AllocationMemoryJson extends MemoryJson<ClassHeapDetailStats> {
+  AllocationMemoryJson();
+
+  /// Given a JSON string representing an array of HeapSample, decode to a
+  /// List of HeapSample.
+  AllocationMemoryJson.decode({
+    required String argJsonString,
+    Map<String, dynamic>? argDecodedMap,
+  }) : super.decode(
+          _jsonAllocationPayloadField,
+          argJsonString: argJsonString,
+          argDecodedMap: argDecodedMap,
+        );
+
+  /// Exported JSON payload of collected memory statistics.
+  static const String _jsonAllocationPayloadField = 'allocations';
+
+  /// Encoded ClassHeapDetailStats
+  @override
+  String encode(ClassHeapDetailStats sample) => jsonEncode(sample);
+
+  /// More than one Encoded ClassHeapDetailStats, add a comma and the Encoded ClassHeapDetailStats entry.
+  @override
+  String encodeAnother(ClassHeapDetailStats sample) =>
+      ',\n${jsonEncode(sample)}';
+
+  @override
+  ClassHeapDetailStats fromJson(Map<String, dynamic> json) =>
+      ClassHeapDetailStats.fromJson(json);
+
+  @override
+  int get version => ClassHeapDetailStats.version;
+
+  /// Given a list of HeapSample, encode as a Json string.
+  static String encodeList(List<ClassHeapDetailStats> data) {
+    final allocationJson = AllocationMemoryJson();
+
+    final result = StringBuffer();
+
+    // Iterate over all ClassHeapDetailStats collected.
+    data.map((f) {
+      final encodedValue = result.isNotEmpty
+          ? allocationJson.encodeAnother(f)
+          : allocationJson.encode(f);
+      result.write(encodedValue);
+    }).toList();
+
+    return '$header$result${MemoryJson.trailer}';
+  }
+
+  /// Allocations Header portion:
+  static String get header => '{"$_jsonAllocationPayloadField": {'
+      '"${MemoryJson.jsonVersionField}": ${ClassHeapDetailStats.version}, '
+      '"${MemoryJson.jsonDevToolsScreenField}": "${MemoryJson.devToolsScreenValueMemory}", '
+      '"${MemoryJson.jsonDataField}": [\n';
+}
diff --git a/devtools_shared/pubspec.yaml b/devtools_shared/pubspec.yaml
new file mode 100644
index 0000000..a812faf
--- /dev/null
+++ b/devtools_shared/pubspec.yaml
@@ -0,0 +1,14 @@
+name: devtools_shared
+description: Package of shared structures between devtools_app and devtools_server.
+
+# Note: this version should only be updated by running tools/update_version.dart
+# that updates all versions of packages from packages/devtools.
+version: 2.3.3
+
+homepage: https://github.com/flutter/devtools
+
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+
+dependencies:
+  vm_service: ^7.1.0
diff --git a/dwds/BUILD.gn b/dwds/BUILD.gn
index c9dbcda..ddec014 100644
--- a/dwds/BUILD.gn
+++ b/dwds/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for dwds-11.0.2
+# This file is generated by package_importer.py for dwds-11.1.1
 
 import("//build/dart/dart_library.gni")
 
@@ -41,6 +41,8 @@
     "data/build_result.g.dart",
     "data/connect_request.dart",
     "data/connect_request.g.dart",
+    "data/debug_event.dart",
+    "data/debug_event.g.dart",
     "data/devtools_request.dart",
     "data/devtools_request.g.dart",
     "data/error_response.dart",
@@ -49,6 +51,8 @@
     "data/extension_request.g.dart",
     "data/isolate_events.dart",
     "data/isolate_events.g.dart",
+    "data/register_event.dart",
+    "data/register_event.g.dart",
     "data/run_request.dart",
     "data/run_request.g.dart",
     "data/serializers.dart",
diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md
index 5ecb69a..b2d5643 100644
--- a/dwds/CHANGELOG.md
+++ b/dwds/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 11.1.1
+
+- Update versions of `package:sse`, `package:vm_service`, `package:dds`.
+
+## 11.1.0
+
+- Add global functions to the injected client for `dart.developer.postEvent`
+  and `dart.developer.registerExtension`.
+- Register new service extension `ext.dwds.emitEvent` so clients can emit
+  events. This is intended to be used for analytics.
+
 ## 11.0.2
 
 - Implement `_flutter.listViews` extension method in dwds vm client.
diff --git a/dwds/debug_extension/.gitignore b/dwds/debug_extension/.gitignore
deleted file mode 100644
index 567609b..0000000
--- a/dwds/debug_extension/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-build/
diff --git a/dwds/debug_extension/CHANGELOG.md b/dwds/debug_extension/CHANGELOG.md
index 3ef07ea..b7fea7a 100644
--- a/dwds/debug_extension/CHANGELOG.md
+++ b/dwds/debug_extension/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.23
+
+- Depend on the latest `package:sse` to improve stability of the connection with many
+  concurrent requests. 
+
+
 ## 1.22
 
 - Detect Dart applications and update the icon accordingly.
diff --git a/dwds/debug_extension/pubspec.yaml b/dwds/debug_extension/pubspec.yaml
index 9292ed0..f2cc147 100644
--- a/dwds/debug_extension/pubspec.yaml
+++ b/dwds/debug_extension/pubspec.yaml
@@ -1,6 +1,6 @@
 name: extension
 publish_to: none
-version: 1.22.0
+version: 1.23.0
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/webdev
 description: >-
@@ -14,13 +14,16 @@
   js: ^0.6.1+1
   pedantic: ^1.5.0
   pub_semver: ^2.0.0
-  sse: ^3.8.1
+  sse: ^4.1.0
   web_socket_channel: ^2.0.0
 
 dev_dependencies:
-  webdev: ^2.0.0
+  build: ^2.0.0
   build_web_compilers: ">=1.2.0 <3.0.0"
   build_runner: ">=1.5.0 <2.0.0"
+  built_collection: ^5.0.0
+  dwds: ^11.0.0
+  webdev: ^2.0.0
 
 dependency_overrides:
   webdev:
diff --git a/dwds/debug_extension/web/background.dart b/dwds/debug_extension/web/background.dart
index f0151bb..23b1770 100644
--- a/dwds/debug_extension/web/background.dart
+++ b/dwds/debug_extension/web/background.dart
@@ -10,7 +10,6 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:html';
-import 'dart:js';
 
 import 'package:built_collection/built_collection.dart';
 import 'package:dwds/data/devtools_request.dart';
diff --git a/dwds/debug_extension/web/manifest.json b/dwds/debug_extension/web/manifest.json
index beb8a2e..a59e69d 100644
--- a/dwds/debug_extension/web/manifest.json
+++ b/dwds/debug_extension/web/manifest.json
@@ -1,6 +1,6 @@
 {
     "name": "Dart Debug Extension",
-    "version": "1.22",
+    "version": "1.23",
     "minimum_chrome_version": "10.0",
     "devtools_page": "devtools.html",
     "manifest_version": 2,
diff --git a/dwds/lib/data/build_result.g.dart b/dwds/lib/data/build_result.g.dart
index 9fbb4fa..dabb7e8 100644
--- a/dwds/lib/data/build_result.g.dart
+++ b/dwds/lib/data/build_result.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'build_result.dart';
 
@@ -79,7 +78,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'status':
           result.status = serializers.deserialize(value,
@@ -100,9 +99,7 @@
       (new BuildResultBuilder()..update(updates)).build();
 
   _$BuildResult._({this.status}) : super._() {
-    if (status == null) {
-      throw new BuiltValueNullFieldError('BuildResult', 'status');
-    }
+    BuiltValueNullFieldError.checkNotNull(status, 'BuildResult', 'status');
   }
 
   @override
@@ -140,8 +137,9 @@
   BuildResultBuilder();
 
   BuildResultBuilder get _$this {
-    if (_$v != null) {
-      _status = _$v.status;
+    final $v = _$v;
+    if ($v != null) {
+      _status = $v.status;
       _$v = null;
     }
     return this;
@@ -149,9 +147,7 @@
 
   @override
   void replace(BuildResult other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$BuildResult;
   }
 
@@ -162,7 +158,10 @@
 
   @override
   _$BuildResult build() {
-    final _$result = _$v ?? new _$BuildResult._(status: status);
+    final _$result = _$v ??
+        new _$BuildResult._(
+            status: BuiltValueNullFieldError.checkNotNull(
+                status, 'BuildResult', 'status'));
     replace(_$result);
     return _$result;
   }
diff --git a/dwds/lib/data/connect_request.g.dart b/dwds/lib/data/connect_request.g.dart
index 9e59946..3c685bb 100644
--- a/dwds/lib/data/connect_request.g.dart
+++ b/dwds/lib/data/connect_request.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'connect_request.dart';
 
@@ -46,7 +45,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'appId':
           result.appId = serializers.deserialize(value,
@@ -80,15 +79,11 @@
 
   _$ConnectRequest._({this.appId, this.instanceId, this.entrypointPath})
       : super._() {
-    if (appId == null) {
-      throw new BuiltValueNullFieldError('ConnectRequest', 'appId');
-    }
-    if (instanceId == null) {
-      throw new BuiltValueNullFieldError('ConnectRequest', 'instanceId');
-    }
-    if (entrypointPath == null) {
-      throw new BuiltValueNullFieldError('ConnectRequest', 'entrypointPath');
-    }
+    BuiltValueNullFieldError.checkNotNull(appId, 'ConnectRequest', 'appId');
+    BuiltValueNullFieldError.checkNotNull(
+        instanceId, 'ConnectRequest', 'instanceId');
+    BuiltValueNullFieldError.checkNotNull(
+        entrypointPath, 'ConnectRequest', 'entrypointPath');
   }
 
   @override
@@ -144,10 +139,11 @@
   ConnectRequestBuilder();
 
   ConnectRequestBuilder get _$this {
-    if (_$v != null) {
-      _appId = _$v.appId;
-      _instanceId = _$v.instanceId;
-      _entrypointPath = _$v.entrypointPath;
+    final $v = _$v;
+    if ($v != null) {
+      _appId = $v.appId;
+      _instanceId = $v.instanceId;
+      _entrypointPath = $v.entrypointPath;
       _$v = null;
     }
     return this;
@@ -155,9 +151,7 @@
 
   @override
   void replace(ConnectRequest other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$ConnectRequest;
   }
 
@@ -170,9 +164,12 @@
   _$ConnectRequest build() {
     final _$result = _$v ??
         new _$ConnectRequest._(
-            appId: appId,
-            instanceId: instanceId,
-            entrypointPath: entrypointPath);
+            appId: BuiltValueNullFieldError.checkNotNull(
+                appId, 'ConnectRequest', 'appId'),
+            instanceId: BuiltValueNullFieldError.checkNotNull(
+                instanceId, 'ConnectRequest', 'instanceId'),
+            entrypointPath: BuiltValueNullFieldError.checkNotNull(
+                entrypointPath, 'ConnectRequest', 'entrypointPath'));
     replace(_$result);
     return _$result;
   }
diff --git a/dwds/lib/data/debug_event.dart b/dwds/lib/data/debug_event.dart
new file mode 100644
index 0000000..f956cd3
--- /dev/null
+++ b/dwds/lib/data/debug_event.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'package:built_value/built_value.dart';
+import 'package:built_value/serializer.dart';
+
+part 'debug_event.g.dart';
+
+abstract class DebugEvent implements Built<DebugEvent, DebugEventBuilder> {
+  static Serializer<DebugEvent> get serializer => _$debugEventSerializer;
+
+  factory DebugEvent([Function(DebugEventBuilder) updates]) = _$DebugEvent;
+
+  DebugEvent._();
+
+  String get kind;
+
+  String get eventData;
+
+  int get timestamp;
+}
diff --git a/dwds/lib/data/debug_event.g.dart b/dwds/lib/data/debug_event.g.dart
new file mode 100644
index 0000000..a3a0d60
--- /dev/null
+++ b/dwds/lib/data/debug_event.g.dart
@@ -0,0 +1,168 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// @dart=2.9
+
+part of 'debug_event.dart';
+
+// **************************************************************************
+// BuiltValueGenerator
+// **************************************************************************
+
+Serializer<DebugEvent> _$debugEventSerializer = new _$DebugEventSerializer();
+
+class _$DebugEventSerializer implements StructuredSerializer<DebugEvent> {
+  @override
+  final Iterable<Type> types = const [DebugEvent, _$DebugEvent];
+  @override
+  final String wireName = 'DebugEvent';
+
+  @override
+  Iterable<Object> serialize(Serializers serializers, DebugEvent object,
+      {FullType specifiedType = FullType.unspecified}) {
+    final result = <Object>[
+      'kind',
+      serializers.serialize(object.kind, specifiedType: const FullType(String)),
+      'eventData',
+      serializers.serialize(object.eventData,
+          specifiedType: const FullType(String)),
+      'timestamp',
+      serializers.serialize(object.timestamp,
+          specifiedType: const FullType(int)),
+    ];
+
+    return result;
+  }
+
+  @override
+  DebugEvent deserialize(Serializers serializers, Iterable<Object> serialized,
+      {FullType specifiedType = FullType.unspecified}) {
+    final result = new DebugEventBuilder();
+
+    final iterator = serialized.iterator;
+    while (iterator.moveNext()) {
+      final key = iterator.current as String;
+      iterator.moveNext();
+      final Object value = iterator.current;
+      switch (key) {
+        case 'kind':
+          result.kind = serializers.deserialize(value,
+              specifiedType: const FullType(String)) as String;
+          break;
+        case 'eventData':
+          result.eventData = serializers.deserialize(value,
+              specifiedType: const FullType(String)) as String;
+          break;
+        case 'timestamp':
+          result.timestamp = serializers.deserialize(value,
+              specifiedType: const FullType(int)) as int;
+          break;
+      }
+    }
+
+    return result.build();
+  }
+}
+
+class _$DebugEvent extends DebugEvent {
+  @override
+  final String kind;
+  @override
+  final String eventData;
+  @override
+  final int timestamp;
+
+  factory _$DebugEvent([void Function(DebugEventBuilder) updates]) =>
+      (new DebugEventBuilder()..update(updates)).build();
+
+  _$DebugEvent._({this.kind, this.eventData, this.timestamp}) : super._() {
+    BuiltValueNullFieldError.checkNotNull(kind, 'DebugEvent', 'kind');
+    BuiltValueNullFieldError.checkNotNull(eventData, 'DebugEvent', 'eventData');
+    BuiltValueNullFieldError.checkNotNull(timestamp, 'DebugEvent', 'timestamp');
+  }
+
+  @override
+  DebugEvent rebuild(void Function(DebugEventBuilder) updates) =>
+      (toBuilder()..update(updates)).build();
+
+  @override
+  DebugEventBuilder toBuilder() => new DebugEventBuilder()..replace(this);
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(other, this)) return true;
+    return other is DebugEvent &&
+        kind == other.kind &&
+        eventData == other.eventData &&
+        timestamp == other.timestamp;
+  }
+
+  @override
+  int get hashCode {
+    return $jf($jc(
+        $jc($jc(0, kind.hashCode), eventData.hashCode), timestamp.hashCode));
+  }
+
+  @override
+  String toString() {
+    return (newBuiltValueToStringHelper('DebugEvent')
+          ..add('kind', kind)
+          ..add('eventData', eventData)
+          ..add('timestamp', timestamp))
+        .toString();
+  }
+}
+
+class DebugEventBuilder implements Builder<DebugEvent, DebugEventBuilder> {
+  _$DebugEvent _$v;
+
+  String _kind;
+  String get kind => _$this._kind;
+  set kind(String kind) => _$this._kind = kind;
+
+  String _eventData;
+  String get eventData => _$this._eventData;
+  set eventData(String eventData) => _$this._eventData = eventData;
+
+  int _timestamp;
+  int get timestamp => _$this._timestamp;
+  set timestamp(int timestamp) => _$this._timestamp = timestamp;
+
+  DebugEventBuilder();
+
+  DebugEventBuilder get _$this {
+    final $v = _$v;
+    if ($v != null) {
+      _kind = $v.kind;
+      _eventData = $v.eventData;
+      _timestamp = $v.timestamp;
+      _$v = null;
+    }
+    return this;
+  }
+
+  @override
+  void replace(DebugEvent other) {
+    ArgumentError.checkNotNull(other, 'other');
+    _$v = other as _$DebugEvent;
+  }
+
+  @override
+  void update(void Function(DebugEventBuilder) updates) {
+    if (updates != null) updates(this);
+  }
+
+  @override
+  _$DebugEvent build() {
+    final _$result = _$v ??
+        new _$DebugEvent._(
+            kind: BuiltValueNullFieldError.checkNotNull(
+                kind, 'DebugEvent', 'kind'),
+            eventData: BuiltValueNullFieldError.checkNotNull(
+                eventData, 'DebugEvent', 'eventData'),
+            timestamp: BuiltValueNullFieldError.checkNotNull(
+                timestamp, 'DebugEvent', 'timestamp'));
+    replace(_$result);
+    return _$result;
+  }
+}
+
+// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
diff --git a/dwds/lib/data/devtools_request.g.dart b/dwds/lib/data/devtools_request.g.dart
index 15bd06d..1170911 100644
--- a/dwds/lib/data/devtools_request.g.dart
+++ b/dwds/lib/data/devtools_request.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'devtools_request.dart';
 
@@ -31,16 +30,18 @@
       serializers.serialize(object.instanceId,
           specifiedType: const FullType(String)),
     ];
-    if (object.contextId != null) {
+    Object value;
+    value = object.contextId;
+    if (value != null) {
       result
         ..add('contextId')
-        ..add(serializers.serialize(object.contextId,
-            specifiedType: const FullType(int)));
+        ..add(serializers.serialize(value, specifiedType: const FullType(int)));
     }
-    if (object.tabUrl != null) {
+    value = object.tabUrl;
+    if (value != null) {
       result
         ..add('tabUrl')
-        ..add(serializers.serialize(object.tabUrl,
+        ..add(serializers.serialize(value,
             specifiedType: const FullType(String)));
     }
     return result;
@@ -56,7 +57,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'appId':
           result.appId = serializers.deserialize(value,
@@ -99,10 +100,12 @@
       serializers.serialize(object.promptExtension,
           specifiedType: const FullType(bool)),
     ];
-    if (object.error != null) {
+    Object value;
+    value = object.error;
+    if (value != null) {
       result
         ..add('error')
-        ..add(serializers.serialize(object.error,
+        ..add(serializers.serialize(value,
             specifiedType: const FullType(String)));
     }
     return result;
@@ -118,7 +121,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'success':
           result.success = serializers.deserialize(value,
@@ -155,12 +158,9 @@
   _$DevToolsRequest._(
       {this.appId, this.instanceId, this.contextId, this.tabUrl})
       : super._() {
-    if (appId == null) {
-      throw new BuiltValueNullFieldError('DevToolsRequest', 'appId');
-    }
-    if (instanceId == null) {
-      throw new BuiltValueNullFieldError('DevToolsRequest', 'instanceId');
-    }
+    BuiltValueNullFieldError.checkNotNull(appId, 'DevToolsRequest', 'appId');
+    BuiltValueNullFieldError.checkNotNull(
+        instanceId, 'DevToolsRequest', 'instanceId');
   }
 
   @override
@@ -223,11 +223,12 @@
   DevToolsRequestBuilder();
 
   DevToolsRequestBuilder get _$this {
-    if (_$v != null) {
-      _appId = _$v.appId;
-      _instanceId = _$v.instanceId;
-      _contextId = _$v.contextId;
-      _tabUrl = _$v.tabUrl;
+    final $v = _$v;
+    if ($v != null) {
+      _appId = $v.appId;
+      _instanceId = $v.instanceId;
+      _contextId = $v.contextId;
+      _tabUrl = $v.tabUrl;
       _$v = null;
     }
     return this;
@@ -235,9 +236,7 @@
 
   @override
   void replace(DevToolsRequest other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$DevToolsRequest;
   }
 
@@ -250,8 +249,10 @@
   _$DevToolsRequest build() {
     final _$result = _$v ??
         new _$DevToolsRequest._(
-            appId: appId,
-            instanceId: instanceId,
+            appId: BuiltValueNullFieldError.checkNotNull(
+                appId, 'DevToolsRequest', 'appId'),
+            instanceId: BuiltValueNullFieldError.checkNotNull(
+                instanceId, 'DevToolsRequest', 'instanceId'),
             contextId: contextId,
             tabUrl: tabUrl);
     replace(_$result);
@@ -273,12 +274,10 @@
 
   _$DevToolsResponse._({this.success, this.promptExtension, this.error})
       : super._() {
-    if (success == null) {
-      throw new BuiltValueNullFieldError('DevToolsResponse', 'success');
-    }
-    if (promptExtension == null) {
-      throw new BuiltValueNullFieldError('DevToolsResponse', 'promptExtension');
-    }
+    BuiltValueNullFieldError.checkNotNull(
+        success, 'DevToolsResponse', 'success');
+    BuiltValueNullFieldError.checkNotNull(
+        promptExtension, 'DevToolsResponse', 'promptExtension');
   }
 
   @override
@@ -334,10 +333,11 @@
   DevToolsResponseBuilder();
 
   DevToolsResponseBuilder get _$this {
-    if (_$v != null) {
-      _success = _$v.success;
-      _promptExtension = _$v.promptExtension;
-      _error = _$v.error;
+    final $v = _$v;
+    if ($v != null) {
+      _success = $v.success;
+      _promptExtension = $v.promptExtension;
+      _error = $v.error;
       _$v = null;
     }
     return this;
@@ -345,9 +345,7 @@
 
   @override
   void replace(DevToolsResponse other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$DevToolsResponse;
   }
 
@@ -360,7 +358,11 @@
   _$DevToolsResponse build() {
     final _$result = _$v ??
         new _$DevToolsResponse._(
-            success: success, promptExtension: promptExtension, error: error);
+            success: BuiltValueNullFieldError.checkNotNull(
+                success, 'DevToolsResponse', 'success'),
+            promptExtension: BuiltValueNullFieldError.checkNotNull(
+                promptExtension, 'DevToolsResponse', 'promptExtension'),
+            error: error);
     replace(_$result);
     return _$result;
   }
diff --git a/dwds/lib/data/error_response.g.dart b/dwds/lib/data/error_response.g.dart
index 0001c85..7cbd01e 100644
--- a/dwds/lib/data/error_response.g.dart
+++ b/dwds/lib/data/error_response.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'error_response.dart';
 
@@ -42,7 +41,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'error':
           result.error = serializers.deserialize(value,
@@ -69,12 +68,9 @@
       (new ErrorResponseBuilder()..update(updates)).build();
 
   _$ErrorResponse._({this.error, this.stackTrace}) : super._() {
-    if (error == null) {
-      throw new BuiltValueNullFieldError('ErrorResponse', 'error');
-    }
-    if (stackTrace == null) {
-      throw new BuiltValueNullFieldError('ErrorResponse', 'stackTrace');
-    }
+    BuiltValueNullFieldError.checkNotNull(error, 'ErrorResponse', 'error');
+    BuiltValueNullFieldError.checkNotNull(
+        stackTrace, 'ErrorResponse', 'stackTrace');
   }
 
   @override
@@ -121,9 +117,10 @@
   ErrorResponseBuilder();
 
   ErrorResponseBuilder get _$this {
-    if (_$v != null) {
-      _error = _$v.error;
-      _stackTrace = _$v.stackTrace;
+    final $v = _$v;
+    if ($v != null) {
+      _error = $v.error;
+      _stackTrace = $v.stackTrace;
       _$v = null;
     }
     return this;
@@ -131,9 +128,7 @@
 
   @override
   void replace(ErrorResponse other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$ErrorResponse;
   }
 
@@ -144,8 +139,12 @@
 
   @override
   _$ErrorResponse build() {
-    final _$result =
-        _$v ?? new _$ErrorResponse._(error: error, stackTrace: stackTrace);
+    final _$result = _$v ??
+        new _$ErrorResponse._(
+            error: BuiltValueNullFieldError.checkNotNull(
+                error, 'ErrorResponse', 'error'),
+            stackTrace: BuiltValueNullFieldError.checkNotNull(
+                stackTrace, 'ErrorResponse', 'stackTrace'));
     replace(_$result);
     return _$result;
   }
diff --git a/dwds/lib/data/extension_request.g.dart b/dwds/lib/data/extension_request.g.dart
index bdd88fe..b84160e 100644
--- a/dwds/lib/data/extension_request.g.dart
+++ b/dwds/lib/data/extension_request.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'extension_request.dart';
 
@@ -34,10 +33,12 @@
       serializers.serialize(object.command,
           specifiedType: const FullType(String)),
     ];
-    if (object.commandParams != null) {
+    Object value;
+    value = object.commandParams;
+    if (value != null) {
       result
         ..add('commandParams')
-        ..add(serializers.serialize(object.commandParams,
+        ..add(serializers.serialize(value,
             specifiedType: const FullType(String)));
     }
     return result;
@@ -53,7 +54,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'id':
           result.id = serializers.deserialize(value,
@@ -94,10 +95,12 @@
       serializers.serialize(object.result,
           specifiedType: const FullType(String)),
     ];
-    if (object.error != null) {
+    Object value;
+    value = object.error;
+    if (value != null) {
       result
         ..add('error')
-        ..add(serializers.serialize(object.error,
+        ..add(serializers.serialize(value,
             specifiedType: const FullType(String)));
     }
     return result;
@@ -113,7 +116,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'id':
           result.id = serializers.deserialize(value,
@@ -170,7 +173,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'params':
           result.params = serializers.deserialize(value,
@@ -216,7 +219,7 @@
     while (iterator.moveNext()) {
       final key = iterator.current as String;
       iterator.moveNext();
-      final dynamic value = iterator.current;
+      final Object value = iterator.current;
       switch (key) {
         case 'events':
           result.events.replace(serializers.deserialize(value,
@@ -245,12 +248,9 @@
 
   _$ExtensionRequest._({this.id, this.command, this.commandParams})
       : super._() {
-    if (id == null) {
-      throw new BuiltValueNullFieldError('ExtensionRequest', 'id');
-    }
-    if (command == null) {
-      throw new BuiltValueNullFieldError('ExtensionRequest', 'command');
-    }
+    BuiltValueNullFieldError.checkNotNull(id, 'ExtensionRequest', 'id');
+    BuiltValueNullFieldError.checkNotNull(
+        command, 'ExtensionRequest', 'command');
   }
 
   @override
@@ -306,10 +306,11 @@
   ExtensionRequestBuilder();
 
   ExtensionRequestBuilder get _$this {
-    if (_$v != null) {
-      _id = _$v.id;
-      _command = _$v.command;
-      _commandParams = _$v.commandParams;
+    final $v = _$v;
+    if ($v != null) {
+      _id = $v.id;
+      _command = $v.command;
+      _commandParams = $v.commandParams;
       _$v = null;
     }
     return this;
@@ -317,9 +318,7 @@
 
   @override
   void replace(ExtensionRequest other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$ExtensionRequest;
   }
 
@@ -332,7 +331,11 @@
   _$ExtensionRequest build() {
     final _$result = _$v ??
         new _$ExtensionRequest._(
-            id: id, command: command, commandParams: commandParams);
+            id: BuiltValueNullFieldError.checkNotNull(
+                id, 'ExtensionRequest', 'id'),
+            command: BuiltValueNullFieldError.checkNotNull(
+                command, 'ExtensionRequest', 'command'),
+            commandParams: commandParams);
     replace(_$result);
     return _$result;
   }
@@ -354,15 +357,11 @@
 
   _$ExtensionResponse._({this.id, this.success, this.result, this.error})
       : super._() {
-    if (id == null) {
-      throw new BuiltValueNullFieldError('ExtensionResponse', 'id');
-    }
-    if (success == null) {
-      throw new BuiltValueNullFieldError('ExtensionResponse', 'success');
-    }
-    if (result == null) {
-      throw new BuiltValueNullFieldError('ExtensionResponse', 'result');
-    }
+    BuiltValueNullFieldError.checkNotNull(id, 'ExtensionResponse', 'id');
+    BuiltValueNullFieldError.checkNotNull(
+        success, 'ExtensionResponse', 'success');
+    BuiltValueNullFieldError.checkNotNull(
+        result, 'ExtensionResponse', 'result');
   }
 
   @override
@@ -424,11 +423,12 @@
   ExtensionResponseBuilder();
 
   ExtensionResponseBuilder get _$this {
-    if (_$v != null) {
-      _id = _$v.id;
-      _success = _$v.success;
-      _result = _$v.result;
-      _error = _$v.error;
+    final $v = _$v;
+    if ($v != null) {
+      _id = $v.id;
+      _success = $v.success;
+      _result = $v.result;
+      _error = $v.error;
       _$v = null;
     }
     return this;
@@ -436,9 +436,7 @@
 
   @override
   void replace(ExtensionResponse other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$ExtensionResponse;
   }
 
@@ -451,7 +449,13 @@
   _$ExtensionResponse build() {
     final _$result = _$v ??
         new _$ExtensionResponse._(
-            id: id, success: success, result: result, error: error);
+            id: BuiltValueNullFieldError.checkNotNull(
+                id, 'ExtensionResponse', 'id'),
+            success: BuiltValueNullFieldError.checkNotNull(
+                success, 'ExtensionResponse', 'success'),
+            result: BuiltValueNullFieldError.checkNotNull(
+                result, 'ExtensionResponse', 'result'),
+            error: error);
     replace(_$result);
     return _$result;
   }
@@ -467,12 +471,8 @@
       (new ExtensionEventBuilder()..update(updates)).build();
 
   _$ExtensionEvent._({this.params, this.method}) : super._() {
-    if (params == null) {
-      throw new BuiltValueNullFieldError('ExtensionEvent', 'params');
-    }
-    if (method == null) {
-      throw new BuiltValueNullFieldError('ExtensionEvent', 'method');
-    }
+    BuiltValueNullFieldError.checkNotNull(params, 'ExtensionEvent', 'params');
+    BuiltValueNullFieldError.checkNotNull(method, 'ExtensionEvent', 'method');
   }
 
   @override
@@ -520,9 +520,10 @@
   ExtensionEventBuilder();
 
   ExtensionEventBuilder get _$this {
-    if (_$v != null) {
-      _params = _$v.params;
-      _method = _$v.method;
+    final $v = _$v;
+    if ($v != null) {
+      _params = $v.params;
+      _method = $v.method;
       _$v = null;
     }
     return this;
@@ -530,9 +531,7 @@
 
   @override
   void replace(ExtensionEvent other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$ExtensionEvent;
   }
 
@@ -543,8 +542,12 @@
 
   @override
   _$ExtensionEvent build() {
-    final _$result =
-        _$v ?? new _$ExtensionEvent._(params: params, method: method);
+    final _$result = _$v ??
+        new _$ExtensionEvent._(
+            params: BuiltValueNullFieldError.checkNotNull(
+                params, 'ExtensionEvent', 'params'),
+            method: BuiltValueNullFieldError.checkNotNull(
+                method, 'ExtensionEvent', 'method'));
     replace(_$result);
     return _$result;
   }
@@ -558,9 +561,7 @@
       (new BatchedEventsBuilder()..update(updates)).build();
 
   _$BatchedEvents._({this.events}) : super._() {
-    if (events == null) {
-      throw new BuiltValueNullFieldError('BatchedEvents', 'events');
-    }
+    BuiltValueNullFieldError.checkNotNull(events, 'BatchedEvents', 'events');
   }
 
   @override
@@ -600,8 +601,9 @@
   BatchedEventsBuilder();
 
   BatchedEventsBuilder get _$this {
-    if (_$v != null) {
-      _events = _$v.events?.toBuilder();
+    final $v = _$v;
+    if ($v != null) {
+      _events = $v.events.toBuilder();
       _$v = null;
     }
     return this;
@@ -609,9 +611,7 @@
 
   @override
   void replace(BatchedEvents other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$BatchedEvents;
   }
 
diff --git a/dwds/lib/data/isolate_events.g.dart b/dwds/lib/data/isolate_events.g.dart
index c00f5c1..cf84186 100644
--- a/dwds/lib/data/isolate_events.g.dart
+++ b/dwds/lib/data/isolate_events.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'isolate_events.dart';
 
@@ -87,9 +86,7 @@
 
   @override
   void replace(IsolateExit other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$IsolateExit;
   }
 
@@ -144,9 +141,7 @@
 
   @override
   void replace(IsolateStart other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$IsolateStart;
   }
 
diff --git a/dwds/lib/data/register_event.dart b/dwds/lib/data/register_event.dart
new file mode 100644
index 0000000..eb88aa2
--- /dev/null
+++ b/dwds/lib/data/register_event.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'package:built_value/built_value.dart';
+import 'package:built_value/serializer.dart';
+
+part 'register_event.g.dart';
+
+abstract class RegisterEvent
+    implements Built<RegisterEvent, RegisterEventBuilder> {
+  static Serializer<RegisterEvent> get serializer => _$registerEventSerializer;
+
+  factory RegisterEvent([Function(RegisterEventBuilder) updates]) =
+      _$RegisterEvent;
+
+  RegisterEvent._();
+
+  String get eventData;
+
+  int get timestamp;
+}
diff --git a/dwds/lib/data/register_event.g.dart b/dwds/lib/data/register_event.g.dart
new file mode 100644
index 0000000..7151a0b
--- /dev/null
+++ b/dwds/lib/data/register_event.g.dart
@@ -0,0 +1,154 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// @dart=2.9
+
+part of 'register_event.dart';
+
+// **************************************************************************
+// BuiltValueGenerator
+// **************************************************************************
+
+Serializer<RegisterEvent> _$registerEventSerializer =
+    new _$RegisterEventSerializer();
+
+class _$RegisterEventSerializer implements StructuredSerializer<RegisterEvent> {
+  @override
+  final Iterable<Type> types = const [RegisterEvent, _$RegisterEvent];
+  @override
+  final String wireName = 'RegisterEvent';
+
+  @override
+  Iterable<Object> serialize(Serializers serializers, RegisterEvent object,
+      {FullType specifiedType = FullType.unspecified}) {
+    final result = <Object>[
+      'eventData',
+      serializers.serialize(object.eventData,
+          specifiedType: const FullType(String)),
+      'timestamp',
+      serializers.serialize(object.timestamp,
+          specifiedType: const FullType(int)),
+    ];
+
+    return result;
+  }
+
+  @override
+  RegisterEvent deserialize(
+      Serializers serializers, Iterable<Object> serialized,
+      {FullType specifiedType = FullType.unspecified}) {
+    final result = new RegisterEventBuilder();
+
+    final iterator = serialized.iterator;
+    while (iterator.moveNext()) {
+      final key = iterator.current as String;
+      iterator.moveNext();
+      final Object value = iterator.current;
+      switch (key) {
+        case 'eventData':
+          result.eventData = serializers.deserialize(value,
+              specifiedType: const FullType(String)) as String;
+          break;
+        case 'timestamp':
+          result.timestamp = serializers.deserialize(value,
+              specifiedType: const FullType(int)) as int;
+          break;
+      }
+    }
+
+    return result.build();
+  }
+}
+
+class _$RegisterEvent extends RegisterEvent {
+  @override
+  final String eventData;
+  @override
+  final int timestamp;
+
+  factory _$RegisterEvent([void Function(RegisterEventBuilder) updates]) =>
+      (new RegisterEventBuilder()..update(updates)).build();
+
+  _$RegisterEvent._({this.eventData, this.timestamp}) : super._() {
+    BuiltValueNullFieldError.checkNotNull(
+        eventData, 'RegisterEvent', 'eventData');
+    BuiltValueNullFieldError.checkNotNull(
+        timestamp, 'RegisterEvent', 'timestamp');
+  }
+
+  @override
+  RegisterEvent rebuild(void Function(RegisterEventBuilder) updates) =>
+      (toBuilder()..update(updates)).build();
+
+  @override
+  RegisterEventBuilder toBuilder() => new RegisterEventBuilder()..replace(this);
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(other, this)) return true;
+    return other is RegisterEvent &&
+        eventData == other.eventData &&
+        timestamp == other.timestamp;
+  }
+
+  @override
+  int get hashCode {
+    return $jf($jc($jc(0, eventData.hashCode), timestamp.hashCode));
+  }
+
+  @override
+  String toString() {
+    return (newBuiltValueToStringHelper('RegisterEvent')
+          ..add('eventData', eventData)
+          ..add('timestamp', timestamp))
+        .toString();
+  }
+}
+
+class RegisterEventBuilder
+    implements Builder<RegisterEvent, RegisterEventBuilder> {
+  _$RegisterEvent _$v;
+
+  String _eventData;
+  String get eventData => _$this._eventData;
+  set eventData(String eventData) => _$this._eventData = eventData;
+
+  int _timestamp;
+  int get timestamp => _$this._timestamp;
+  set timestamp(int timestamp) => _$this._timestamp = timestamp;
+
+  RegisterEventBuilder();
+
+  RegisterEventBuilder get _$this {
+    final $v = _$v;
+    if ($v != null) {
+      _eventData = $v.eventData;
+      _timestamp = $v.timestamp;
+      _$v = null;
+    }
+    return this;
+  }
+
+  @override
+  void replace(RegisterEvent other) {
+    ArgumentError.checkNotNull(other, 'other');
+    _$v = other as _$RegisterEvent;
+  }
+
+  @override
+  void update(void Function(RegisterEventBuilder) updates) {
+    if (updates != null) updates(this);
+  }
+
+  @override
+  _$RegisterEvent build() {
+    final _$result = _$v ??
+        new _$RegisterEvent._(
+            eventData: BuiltValueNullFieldError.checkNotNull(
+                eventData, 'RegisterEvent', 'eventData'),
+            timestamp: BuiltValueNullFieldError.checkNotNull(
+                timestamp, 'RegisterEvent', 'timestamp'));
+    replace(_$result);
+    return _$result;
+  }
+}
+
+// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
diff --git a/dwds/lib/data/run_request.g.dart b/dwds/lib/data/run_request.g.dart
index 493aa7f..dd2e066 100644
--- a/dwds/lib/data/run_request.g.dart
+++ b/dwds/lib/data/run_request.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'run_request.dart';
 
@@ -66,9 +65,7 @@
 
   @override
   void replace(RunRequest other) {
-    if (other == null) {
-      throw new ArgumentError.notNull('other');
-    }
+    ArgumentError.checkNotNull(other, 'other');
     _$v = other as _$RunRequest;
   }
 
diff --git a/dwds/lib/data/serializers.dart b/dwds/lib/data/serializers.dart
index a53c5d7..2303e21 100644
--- a/dwds/lib/data/serializers.dart
+++ b/dwds/lib/data/serializers.dart
@@ -9,27 +9,31 @@
 
 import 'build_result.dart';
 import 'connect_request.dart';
+import 'debug_event.dart';
 import 'devtools_request.dart';
 import 'error_response.dart';
 import 'extension_request.dart';
 import 'isolate_events.dart';
+import 'register_event.dart';
 import 'run_request.dart';
 
 part 'serializers.g.dart';
 
 /// Serializers for all the types used in DWDS communication.
 @SerializersFor([
+  BatchedEvents,
   BuildResult,
+  ConnectRequest,
+  DebugEvent,
   DevToolsRequest,
   DevToolsResponse,
-  ConnectRequest,
-  RunRequest,
   IsolateExit,
   IsolateStart,
   ExtensionRequest,
   ExtensionResponse,
   ExtensionEvent,
-  BatchedEvents,
   ErrorResponse,
+  RegisterEvent,
+  RunRequest,
 ])
 final Serializers serializers = _$serializers;
diff --git a/dwds/lib/data/serializers.g.dart b/dwds/lib/data/serializers.g.dart
index eb6c63f..6cef393 100644
--- a/dwds/lib/data/serializers.g.dart
+++ b/dwds/lib/data/serializers.g.dart
@@ -1,6 +1,5 @@
 // GENERATED CODE - DO NOT MODIFY BY HAND
-
-// @dart = 2.9
+// @dart=2.9
 
 part of 'serializers.dart';
 
@@ -13,6 +12,7 @@
       ..add(BuildResult.serializer)
       ..add(BuildStatus.serializer)
       ..add(ConnectRequest.serializer)
+      ..add(DebugEvent.serializer)
       ..add(DevToolsRequest.serializer)
       ..add(DevToolsResponse.serializer)
       ..add(ErrorResponse.serializer)
@@ -21,6 +21,7 @@
       ..add(ExtensionResponse.serializer)
       ..add(IsolateExit.serializer)
       ..add(IsolateStart.serializer)
+      ..add(RegisterEvent.serializer)
       ..add(RunRequest.serializer)
       ..addBuilderFactory(
           const FullType(BuiltList, const [const FullType(ExtensionEvent)]),
diff --git a/dwds/lib/src/debugging/classes.dart b/dwds/lib/src/debugging/classes.dart
index c8ee5cb..ba7f1d2 100644
--- a/dwds/lib/src/debugging/classes.dart
+++ b/dwds/lib/src/debugging/classes.dart
@@ -24,8 +24,10 @@
 final classRefForUnknown = classRefFor('dart:core', 'Unknown');
 
 /// Returns a [ClassRef] for the provided library ID and class name.
-ClassRef classRefFor(String libraryId, String name) =>
-    ClassRef(id: 'classes|$libraryId|$name', name: name);
+ClassRef classRefFor(String libraryId, String name) => ClassRef(
+    id: 'classes|$libraryId|$name',
+    name: name,
+    library: LibraryRef(id: libraryId, name: libraryId, uri: libraryId));
 
 /// Keeps track of Dart classes available in the running application.
 class ClassHelper extends Domain {
@@ -91,12 +93,24 @@
           'name': clazz.name,
           'dartName': sdkUtils.typeName(clazz)
         };
-      // TODO(jakemac): static methods once ddc supports them
-      var methods = sdkUtils.getMethods(clazz);
-      var methodNames = methods ? Object.keys(methods) : [];
+
+      // TODO(grouma) - we display all inherited methods since we don't provide
+      // the superClass information. This is technically not correct.
+      var proto = clazz.prototype;
+      var methodNames = [];
+      for (; proto != null; proto = Object.getPrototypeOf(proto)) {
+        var methods = Object.getOwnPropertyNames(proto);
+        for (var i = 0; i < methods.length; i++) {
+          if (methodNames.indexOf(methods[i]) == -1
+              && methods[i] != 'constructor') {
+              methodNames.push(methods[i]);
+          }
+        }
+        if (proto.constructor.name == 'Object') break;
+      }
+
       descriptor['methods'] = {};
       for (var name of methodNames) {
-        var method = methods[name];
         descriptor['methods'][name] = {
           // TODO(jakemac): how can we get actual const info?
           "isConst": false,
diff --git a/dwds/lib/src/debugging/dart_scope.dart b/dwds/lib/src/debugging/dart_scope.dart
index 0b6164c..0c6bc4d 100644
--- a/dwds/lib/src/debugging/dart_scope.dart
+++ b/dwds/lib/src/debugging/dart_scope.dart
@@ -11,7 +11,7 @@
 
 // TODO(sdk/issues/44262) - use an alternative way to identify synthetic
 // variables.
-final ddcTemporaryVariableRegExp = RegExp(r'^t([0-9])+(\$)?([0-9])*$');
+final ddcTemporaryVariableRegExp = RegExp(r'^(t[0-9]+\$?[0-9]*|__t\$\w*)$');
 
 /// Find the visible Dart properties from a JS Scope Chain, coming from the
 /// scopeChain attribute of a Chrome CallFrame corresponding to [frame].
diff --git a/dwds/lib/src/debugging/debugger.dart b/dwds/lib/src/debugging/debugger.dart
index 7c5cd43..fdbc583 100644
--- a/dwds/lib/src/debugging/debugger.dart
+++ b/dwds/lib/src/debugging/debugger.dart
@@ -701,12 +701,12 @@
   Breakpoint _dartBreakpoint(
       ScriptRef dartScript, Location location, String id) {
     var breakpoint = Breakpoint(
-        id: id,
-        breakpointNumber: int.parse(createId()),
-        resolved: true,
-        location:
-            SourceLocation(script: dartScript, tokenPos: location.tokenPos))
-      ..id = id;
+      id: id,
+      breakpointNumber: int.parse(createId()),
+      resolved: true,
+      location: SourceLocation(script: dartScript, tokenPos: location.tokenPos),
+      enabled: true,
+    )..id = id;
     return breakpoint;
   }
 
diff --git a/dwds/lib/src/dwds_vm_client.dart b/dwds/lib/src/dwds_vm_client.dart
index 4a27d8d..6e93fc6 100644
--- a/dwds/lib/src/dwds_vm_client.dart
+++ b/dwds/lib/src/dwds_vm_client.dart
@@ -11,6 +11,7 @@
 import 'package:vm_service/vm_service.dart';
 import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
 
+import 'events.dart';
 import 'services/chrome_proxy_service.dart' show ChromeProxyService;
 import 'services/debug_service.dart';
 
@@ -153,6 +154,13 @@
     });
     await client.registerService('ext.dwds.screenshot', 'DWDS');
 
+    client.registerServiceCallback('ext.dwds.emitEvent', (event) async {
+      emitEvent(DwdsEvent(
+          event['type'] as String, event['payload'] as Map<String, dynamic>));
+      return {'result': Success().toJson()};
+    });
+    await client.registerService('ext.dwds.emitEvent', 'DWDS');
+
     client.registerServiceCallback('_yieldControlToDDS', (request) async {
       final ddsUri = request['uri'] as String;
       if (ddsUri == null) {
diff --git a/dwds/lib/src/handlers/dev_handler.dart b/dwds/lib/src/handlers/dev_handler.dart
index 4695b78..664f931 100644
--- a/dwds/lib/src/handlers/dev_handler.dart
+++ b/dwds/lib/src/handlers/dev_handler.dart
@@ -16,25 +16,21 @@
 
 import '../../data/build_result.dart';
 import '../../data/connect_request.dart';
+import '../../data/debug_event.dart';
 import '../../data/devtools_request.dart';
 import '../../data/error_response.dart';
 import '../../data/isolate_events.dart';
+import '../../data/register_event.dart';
 import '../../data/serializers.dart';
 import '../../dwds.dart';
-import '../connections/app_connection.dart';
-import '../connections/debug_connection.dart';
 import '../debugging/execution_context.dart';
 import '../debugging/remote_debugger.dart';
 import '../debugging/webkit_debugger.dart';
 import '../dwds_vm_client.dart';
 import '../events.dart';
-import '../handlers/socket_connections.dart';
-import '../readers/asset_reader.dart';
-import '../servers/devtools.dart';
 import '../servers/extension_backend.dart';
 import '../services/app_debug_services.dart';
 import '../services/debug_service.dart';
-import '../services/expression_compiler.dart';
 import 'injector.dart';
 
 /// When enabled, this logs VM service protocol and Chrome debug protocol
@@ -264,6 +260,14 @@
             await _handleIsolateExit(appConnection);
           } else if (message is IsolateStart) {
             await _handleIsolateStart(appConnection, injectedConnection);
+          } else if (message is DebugEvent) {
+            await _servicesByAppId[appConnection.request.appId]
+                ?.chromeProxyService
+                ?.parseDebugEvent(message);
+          } else if (message is RegisterEvent) {
+            await _servicesByAppId[appConnection.request.appId]
+                ?.chromeProxyService
+                ?.parseRegisterEvent(message);
           }
         }
       } catch (e, s) {
diff --git a/dwds/lib/src/loaders/build_runner_require.dart b/dwds/lib/src/loaders/build_runner_require.dart
index f70bbda..d0ad0ca 100644
--- a/dwds/lib/src/loaders/build_runner_require.dart
+++ b/dwds/lib/src/loaders/build_runner_require.dart
@@ -11,9 +11,7 @@
 import 'package:shelf/shelf.dart';
 
 import '../../dwds.dart';
-import '../debugging/metadata/provider.dart';
 import 'require.dart';
-import 'strategy.dart';
 
 /// Provides a [RequireStrategy] suitable for use with `package:build_runner`.
 class BuildRunnerRequireStrategyProvider {
diff --git a/dwds/lib/src/loaders/frontend_server_require.dart b/dwds/lib/src/loaders/frontend_server_require.dart
index f755b35..ad5871c 100644
--- a/dwds/lib/src/loaders/frontend_server_require.dart
+++ b/dwds/lib/src/loaders/frontend_server_require.dart
@@ -8,7 +8,6 @@
 
 import '../../dwds.dart';
 import 'require.dart';
-import 'strategy.dart';
 
 /// Provides a [RequireStrategy] suitable for use with Frontend Server.
 class FrontendServerRequireStrategyProvider {
diff --git a/dwds/lib/src/loaders/legacy.dart b/dwds/lib/src/loaders/legacy.dart
index 98e87a6..e712ab5 100644
--- a/dwds/lib/src/loaders/legacy.dart
+++ b/dwds/lib/src/loaders/legacy.dart
@@ -7,7 +7,6 @@
 import 'package:shelf/shelf.dart';
 
 import '../../dwds.dart';
-import 'strategy.dart';
 
 /// A load strategy for the legacy module system.
 class LegacyStrategy extends LoadStrategy {
diff --git a/dwds/lib/src/loaders/require.dart b/dwds/lib/src/loaders/require.dart
index ecc6f3e..56868e2 100644
--- a/dwds/lib/src/loaders/require.dart
+++ b/dwds/lib/src/loaders/require.dart
@@ -10,7 +10,6 @@
 import 'package:shelf/shelf.dart';
 
 import '../../dwds.dart';
-import 'strategy.dart';
 
 String relativizePath(String path) =>
     path.startsWith('/') ? path.substring(1) : path;
diff --git a/dwds/lib/src/services/chrome_proxy_service.dart b/dwds/lib/src/services/chrome_proxy_service.dart
index 92e36ab..2e384e2 100644
--- a/dwds/lib/src/services/chrome_proxy_service.dart
+++ b/dwds/lib/src/services/chrome_proxy_service.dart
@@ -14,8 +14,9 @@
 import 'package:vm_service/vm_service.dart';
 import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
 
+import '../../data/debug_event.dart';
+import '../../data/register_event.dart';
 import '../../dwds.dart';
-import '../connections/app_connection.dart';
 import '../debugging/debugger.dart';
 import '../debugging/execution_context.dart';
 import '../debugging/inspector.dart';
@@ -26,10 +27,8 @@
 import '../debugging/skip_list.dart';
 import '../events.dart';
 import '../loaders/strategy.dart';
-import '../readers/asset_reader.dart';
 import '../utilities/dart_uri.dart';
 import '../utilities/shared.dart';
-import 'expression_compiler.dart';
 import 'expression_evaluator.dart';
 
 /// Adds [event] to the stream with [streamId] if there is anybody listening
@@ -832,6 +831,44 @@
     return controller;
   }
 
+  /// Parses the [DebugEvent] and emits a corresponding Dart VM Service
+  /// protocol [Event].
+  Future<void> parseDebugEvent(DebugEvent debugEvent) async {
+    if (terminatingIsolates) return;
+
+    var isolate = _inspector?.isolate;
+    if (isolate == null) return;
+
+    _streamNotify(
+        EventStreams.kExtension,
+        Event(
+            kind: EventKind.kExtension,
+            timestamp: DateTime.now().millisecondsSinceEpoch,
+            isolate: isolate)
+          ..extensionKind = debugEvent.kind
+          ..extensionData = ExtensionData.parse(
+              jsonDecode(debugEvent.eventData) as Map<String, dynamic>));
+  }
+
+  /// Parses the [RegisterEvent] and emits a corresponding Dart VM Service
+  /// protocol [Event].
+  Future<void> parseRegisterEvent(RegisterEvent registerEvent) async {
+    if (terminatingIsolates) return;
+
+    var isolate = _inspector?.isolate;
+    if (isolate == null) return;
+
+    var service = registerEvent.eventData;
+    isolate.extensionRPCs.add(service);
+    _streamNotify(
+        EventStreams.kIsolate,
+        Event(
+            kind: EventKind.kServiceExtensionAdded,
+            timestamp: DateTime.now().millisecondsSinceEpoch,
+            isolate: isolate)
+          ..extensionRPC = service);
+  }
+
   /// Listens for chrome console events and handles the ones we care about.
   void _setUpChromeConsoleListeners(IsolateRef isolateRef) {
     _consoleSubscription =
@@ -844,6 +881,8 @@
       if (isolateRef.id != isolate.id) return;
 
       var firstArgValue = event.args[0].value as String;
+      // TODO(grouma) - Remove when the min SDK has updated to migrate users
+      // over to the injected client communication approach.
       switch (firstArgValue) {
         case 'dart.developer.registerExtension':
           var service = event.args[1].value as String;
@@ -1010,6 +1049,11 @@
   Future<Success> setTraceClassAllocation(
           String isolateId, String classId, bool enable) =>
       throw UnimplementedError();
+
+  @override
+  Future<Breakpoint> setBreakpointState(
+          String isolateId, String breakpointId, bool enable) =>
+      throw UnimplementedError();
 }
 
 /// The `type`s of [ConsoleAPIEvent]s that are treated as `stderr` logs.
diff --git a/dwds/lib/src/services/debug_service.dart b/dwds/lib/src/services/debug_service.dart
index 133525f..066afe3 100644
--- a/dwds/lib/src/services/debug_service.dart
+++ b/dwds/lib/src/services/debug_service.dart
@@ -24,10 +24,8 @@
 import '../../dwds.dart';
 import '../debugging/execution_context.dart';
 import '../debugging/remote_debugger.dart';
-import '../readers/asset_reader.dart';
 import '../utilities/shared.dart';
 import 'chrome_proxy_service.dart';
-import 'expression_compiler.dart';
 
 bool _acceptNewConnections = true;
 int _clientsConnected = 0;
diff --git a/dwds/lib/src/version.dart b/dwds/lib/src/version.dart
index fddd0d6..b441fe3 100644
--- a/dwds/lib/src/version.dart
+++ b/dwds/lib/src/version.dart
@@ -1,2 +1,2 @@
 // Generated code. Do not modify.
-const packageVersion = '11.0.2';
+const packageVersion = '11.1.1';
diff --git a/dwds/pubspec.yaml b/dwds/pubspec.yaml
index 70fb229..2f741d1 100644
--- a/dwds/pubspec.yaml
+++ b/dwds/pubspec.yaml
@@ -1,6 +1,6 @@
 name: dwds
 # Every time this changes you need to run `pub run build_runner build`.
-version: 11.0.2
+version: 11.1.1
 homepage: https://github.com/dart-lang/webdev/tree/master/dwds
 description: >-
   A service that proxies between the Chrome debug protocol and the Dart VM
@@ -11,10 +11,10 @@
 
 dependencies:
   async: ^2.3.0
-  built_collection: ^4.2.0
+  built_collection: ^5.0.0
   built_value: '>=6.7.0 <9.0.0'
   crypto: '>=2.0.6 < 4.0.0'
-  dds: ^1.4.1
+  dds: ^2.0.0
   http: '>=0.12.0 < 0.14.0'
   http_multi_server: ^3.0.0
   logging: '>=0.11.3 < 2.0.0'
@@ -30,9 +30,9 @@
   shelf_static: ^1.0.0
   shelf_web_socket: ^1.0.0
   source_maps: ^0.10.0
-  sse: ^3.7.0
+  sse: ^4.1.0
   # We pin the version because we implement the interface.
-  vm_service: 6.2.0
+  vm_service: 7.1.0
   web_socket_channel: ^2.0.0
   webkit_inspection_protocol: ^1.0.0
 
@@ -43,6 +43,7 @@
   build_runner: ^1.6.2
   build_version: ^2.0.0
   build_web_compilers: ^2.12.0
+  built_value_generator: ^8.0.0
   graphs: ^1.0.0
   frontend_server_common:
     path: ../frontend_server_common
diff --git a/dwds/web/client.dart b/dwds/web/client.dart
index 3b6e75f..4ddbafd 100644
--- a/dwds/web/client.dart
+++ b/dwds/web/client.dart
@@ -13,8 +13,10 @@
 
 import 'package:dwds/data/build_result.dart';
 import 'package:dwds/data/connect_request.dart';
+import 'package:dwds/data/debug_event.dart';
 import 'package:dwds/data/devtools_request.dart';
 import 'package:dwds/data/error_response.dart';
+import 'package:dwds/data/register_event.dart';
 import 'package:dwds/data/run_request.dart';
 import 'package:dwds/data/serializers.dart';
 import 'package:dwds/src/sockets.dart';
@@ -59,6 +61,19 @@
       return toPromise(manager.hotRestart());
     });
 
+    emitDebugEvent = allowInterop((String kind, String eventData) {
+      client.sink.add(jsonEncode(serializers.serialize(DebugEvent((b) => b
+        ..timestamp = (DateTime.now().millisecondsSinceEpoch)
+        ..kind = kind
+        ..eventData = eventData))));
+    });
+
+    emitRegisterEvent = allowInterop((String eventData) {
+      client.sink.add(jsonEncode(serializers.serialize(RegisterEvent((b) => b
+        ..timestamp = (DateTime.now().millisecondsSinceEpoch)
+        ..eventData = eventData))));
+    });
+
     launchDevToolsJs = allowInterop(() {
       if (!_isChromium) {
         window.alert(
@@ -201,4 +216,10 @@
 @JS('window.top.document.dispatchEvent')
 external void dispatchEvent(CustomEvent event);
 
+@JS(r'$emitDebugEvent')
+external set emitDebugEvent(void Function(String, String) func);
+
+@JS(r'$emitRegisterEvent')
+external set emitRegisterEvent(void Function(String) func);
+
 bool get _isChromium => window.navigator.userAgent.contains('Chrome');
diff --git a/edit_distance/BUILD.gn b/edit_distance/BUILD.gn
index 311c809..f0ec5af 100644
--- a/edit_distance/BUILD.gn
+++ b/edit_distance/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for edit_distance-0.4.1
+# This file is generated by package_importer.py for edit_distance-0.4.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/fake_async/BUILD.gn b/fake_async/BUILD.gn
index 1a35581..a8f78a7 100644
--- a/fake_async/BUILD.gn
+++ b/fake_async/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for fake_async-1.2.0
+# This file is generated by package_importer.py for fake_async-1.2.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/ffi/BUILD.gn b/ffi/BUILD.gn
index 8893966..53e45f5 100644
--- a/ffi/BUILD.gn
+++ b/ffi/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for ffi-1.1.2
+# This file is generated by package_importer.py for ffi-1.1.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/file/BUILD.gn b/file/BUILD.gn
index 28a1c4b..f6a833b 100644
--- a/file/BUILD.gn
+++ b/file/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for file-6.1.1
+# This file is generated by package_importer.py for file-6.1.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/file/CHANGELOG.md b/file/CHANGELOG.md
index 69175de..61e6ae2 100644
--- a/file/CHANGELOG.md
+++ b/file/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 6.1.2
+
+* `MemoryFileSystem` now provides `opHandle`s for exists operations.
+
 #### 6.1.1
 
 * `MemoryFile` now provides `opHandle`s for copy and open operations.
diff --git a/file/lib/src/backends/memory/memory_directory.dart b/file/lib/src/backends/memory/memory_directory.dart
index c5c590e..9288f7e 100644
--- a/file/lib/src/backends/memory/memory_directory.dart
+++ b/file/lib/src/backends/memory/memory_directory.dart
@@ -38,7 +38,10 @@
   }
 
   @override
-  bool existsSync() => backingOrNull?.stat.type == expectedType;
+  bool existsSync() {
+    fileSystem.opHandle.call(path, FileSystemOp.exists);
+    return backingOrNull?.stat.type == expectedType;
+  }
 
   @override
   Future<Directory> create({bool recursive = false}) async {
diff --git a/file/lib/src/backends/memory/memory_file.dart b/file/lib/src/backends/memory/memory_file.dart
index 988ffd2..be3cadc 100644
--- a/file/lib/src/backends/memory/memory_file.dart
+++ b/file/lib/src/backends/memory/memory_file.dart
@@ -42,7 +42,10 @@
   io.FileSystemEntityType get expectedType => io.FileSystemEntityType.file;
 
   @override
-  bool existsSync() => backingOrNull?.stat.type == expectedType;
+  bool existsSync() {
+    fileSystem.opHandle.call(path, FileSystemOp.exists);
+    return backingOrNull?.stat.type == expectedType;
+  }
 
   @override
   Future<File> create({bool recursive = false}) async {
diff --git a/file/lib/src/backends/memory/memory_link.dart b/file/lib/src/backends/memory/memory_link.dart
index a3cba7c..7d5afb4 100644
--- a/file/lib/src/backends/memory/memory_link.dart
+++ b/file/lib/src/backends/memory/memory_link.dart
@@ -22,7 +22,10 @@
   io.FileSystemEntityType get expectedType => io.FileSystemEntityType.link;
 
   @override
-  bool existsSync() => backingOrNull?.type == expectedType;
+  bool existsSync() {
+    fileSystem.opHandle.call(path, FileSystemOp.exists);
+    return backingOrNull?.type == expectedType;
+  }
 
   @override
   Future<Link> rename(String newPath) async => renameSync(newPath);
diff --git a/file/lib/src/backends/memory/operations.dart b/file/lib/src/backends/memory/operations.dart
index e0bf55f..9fc7462 100644
--- a/file/lib/src/backends/memory/operations.dart
+++ b/file/lib/src/backends/memory/operations.dart
@@ -56,6 +56,12 @@
   /// * [File.copySync]
   static const FileSystemOp copy = FileSystemOp._(5);
 
+  /// A file system operation used for all exists methods.
+  ///
+  /// * [FileSystemEntity.exists]
+  /// * [FileSystemEntity.existsSync]
+  static const FileSystemOp exists = FileSystemOp._(6);
+
   @override
   String toString() {
     switch (_value) {
@@ -71,6 +77,8 @@
         return 'FileSystemOp.open';
       case 5:
         return 'FileSystemOp.copy';
+      case 6:
+        return 'FileSystemOp.exists';
       default:
         throw StateError('Invalid FileSytemOp type: $this');
     }
diff --git a/file/pubspec.yaml b/file/pubspec.yaml
index 14ac96a..3037f3a 100644
--- a/file/pubspec.yaml
+++ b/file/pubspec.yaml
@@ -1,5 +1,5 @@
 name: file
-version: 6.1.1
+version: 6.1.2
 description:
   A pluggable, mockable file system abstraction for Dart. Supports local file
   system access, as well as in-memory file systems, record-replay file systems,
@@ -7,7 +7,7 @@
 homepage: https://github.com/google/file.dart
 
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   meta: ^1.3.0
diff --git a/fixnum/.github/workflows/test-package.yml b/fixnum/.github/workflows/test-package.yml
new file mode 100644
index 0000000..e55702c
--- /dev/null
+++ b/fixnum/.github/workflows/test-package.yml
@@ -0,0 +1,64 @@
+name: Dart CI
+
+on:
+  # Run on PRs and pushes to the default branch.
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+  schedule:
+    - cron: "0 0 * * 0"
+
+env:
+  PUB_ENVIRONMENT: bot.github
+
+jobs:
+  # Check code formatting and static analysis on a single OS (linux)
+  # against Dart dev.
+  analyze:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        sdk: [dev]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dart-lang/setup-dart@v0.1
+        with:
+          channel: ${{ matrix.sdk }}
+      - id: install
+        name: Install dependencies
+        run: dart pub get
+      - name: Check formatting
+        run: dart format --output=none --set-exit-if-changed .
+        if: always() && steps.install.outcome == 'success'
+      - name: Analyze code
+        run: dart analyze --fatal-infos
+        if: always() && steps.install.outcome == 'success'
+
+  # Run tests on a matrix consisting of two dimensions:
+  # 1. OS: ubuntu-latest, (macos-latest, windows-latest)
+  # 2. release channel: dev
+  test:
+    needs: analyze
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        # Add macos-latest and/or windows-latest if relevant for this package.
+        os: [ubuntu-latest]
+        sdk: [dev]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dart-lang/setup-dart@v0.1
+        with:
+          channel: ${{ matrix.sdk }}
+      - id: install
+        name: Install dependencies
+        run: dart pub get
+      - name: Run VM tests
+        run: dart test --platform vm
+        if: always() && steps.install.outcome == 'success'
+      - name: Run Chrome tests
+        run: dart test --platform chrome
+        if: always() && steps.install.outcome == 'success'
diff --git a/fixnum/.travis.yml b/fixnum/.travis.yml
deleted file mode 100644
index 88939f9..0000000
--- a/fixnum/.travis.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-language: dart
-
-dart:
- - 2.1.1
- - dev
-
-dart_task:
- - test: --platform vm
-   xvfb: false
- - test: --platform chrome
- - dartanalyzer: --fatal-warnings --fatal-infos .
-
-matrix:
-  include:
-  # Only validate formatting using the dev release
-  - dart: dev
-    dart_task: dartfmt
-
-# Only building master means that we don't run two builds for each pull request.
-branches:
-  only: [master]
-
-cache:
-  directories:
-    - $HOME/.pub-cache
diff --git a/fixnum/BUILD.gn b/fixnum/BUILD.gn
index 34321f9..6e152d5 100644
--- a/fixnum/BUILD.gn
+++ b/fixnum/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for fixnum-0.10.11
+# This file is generated by package_importer.py for fixnum-1.0.0
 
 import("//build/dart/dart_library.gni")
 
 dart_library("fixnum") {
   package_name = "fixnum"
 
-  language_version = "2.1"
+  language_version = "2.12"
 
   disable_analysis = true
 
diff --git a/fixnum/CHANGELOG.md b/fixnum/CHANGELOG.md
index 3359d0a..5ee9ebb 100644
--- a/fixnum/CHANGELOG.md
+++ b/fixnum/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 1.0.0
+
+* Stable null safety release.
+
+## 1.0.0-nullsafety.0
+
+* Migrate to null safety.
+  * This is meant to be mostly non-breaking, for opted in users runtime errors
+    will be promoted to static errors. For non-opted in users the runtime
+    errors are still present in their original form.
+
 ## 0.10.11
 
 * Update minimum SDK constraint to version 2.1.1.
diff --git a/fixnum/README.md b/fixnum/README.md
index cfb3164..2f79b60 100644
--- a/fixnum/README.md
+++ b/fixnum/README.md
@@ -1,14 +1,9 @@
 A fixed-width 32- and 64- bit integer library for Dart.
 
-[![Build Status](https://travis-ci.org/dart-lang/fixnum.svg?branch=master)](https://travis-ci.org/dart-lang/fixnum)
+[![Pub](https://img.shields.io/pub/v/fixnum.svg)](https://pub.dev/packages/fixnum)
+[![Dart CI](https://github.com/dart-lang/fixnum/workflows/Dart%20CI/badge.svg)](https://github.com/dart-lang/fixnum/actions?query=workflow%3A%22Dart+CI%22+branch%3Amaster)
 [![Coverage Status](https://img.shields.io/coveralls/dart-lang/fixnum.svg)](https://coveralls.io/r/dart-lang/fixnum)
 
-## Documentation
-
-The fixnum package provides data types for signed 32- and 64-bit integers.
+Provides data types for signed 32- and 64-bit integers.
 The integer implementations in this library are designed to work identically
 whether executed on the Dart VM or compiled to JavaScript.
-
-For more information, see the
-[fixnum package](https://pub.dev/packages/fixnum) on
-[pub.dev](https://pub.dev/).
diff --git a/fixnum/analysis_options.yaml b/fixnum/analysis_options.yaml
index 6ddb107..b459a9b 100644
--- a/fixnum/analysis_options.yaml
+++ b/fixnum/analysis_options.yaml
@@ -2,13 +2,10 @@
 analyzer:
   strong-mode:
     implicit-casts: false
+
 linter:
   rules:
-    #- annotate_overrides
     - avoid_function_literals_in_foreach_calls
-    - avoid_init_to_null
-    - avoid_null_checks_in_equality_operators
-    - avoid_relative_lib_imports
     - avoid_returning_null
     - avoid_unused_constructor_parameters
     - await_only_futures
@@ -18,43 +15,27 @@
     #- constant_identifier_names
     - control_flow_in_finally
     - directives_ordering
-    - empty_catches
-    - empty_constructor_bodies
     - empty_statements
     - hash_and_equals
     - implementation_imports
     - invariant_booleans
     - iterable_contains_unrelated_type
-    - library_names
-    - library_prefixes
     - list_remove_unrelated_type
     - no_adjacent_strings_in_list
     - non_constant_identifier_names
-    #- omit_local_variable_types
     - only_throw_errors
     - overridden_fields
     - package_api_docs
     - package_names
     - package_prefixed_library_names
-    - prefer_adjacent_string_concatenation
-    - prefer_collection_literals
-    - prefer_conditional_assignment
     - prefer_const_constructors
-    - prefer_final_fields
-    - prefer_generic_function_type_aliases
     - prefer_initializing_formals
     - prefer_interpolation_to_compose_strings
-    #- prefer_single_quotes
     - prefer_typing_uninitialized_variables
-    - slash_for_doc_comments
     - test_types_in_equals
     - throw_in_finally
-    - type_init_formals
     - unnecessary_brace_in_string_interps
-    - unnecessary_const
     - unnecessary_getters_setters
     - unnecessary_lambdas
-    - unnecessary_new
     - unnecessary_null_aware_assignments
     - unnecessary_statements
-    #- unnecessary_this
diff --git a/fixnum/lib/src/int32.dart b/fixnum/lib/src/int32.dart
index 3a9d017..664fc17 100644
--- a/fixnum/lib/src/int32.dart
+++ b/fixnum/lib/src/int32.dart
@@ -54,12 +54,12 @@
   // TODO(rice) - Make this faster by converting several digits at once.
   static Int32 parseRadix(String s, int radix) {
     _validateRadix(radix);
-    Int32 x = ZERO;
-    for (int i = 0; i < s.length; i++) {
-      int c = s.codeUnitAt(i);
-      int digit = _decodeDigit(c);
+    var x = ZERO;
+    for (var i = 0; i < s.length; i++) {
+      var c = s.codeUnitAt(i);
+      var digit = _decodeDigit(c);
       if (digit < 0 || digit >= radix) {
-        throw FormatException("Non-radix code unit: $c");
+        throw FormatException('Non-radix code unit: $c');
       }
       x = ((x * radix) + digit) as Int32;
     }
@@ -122,7 +122,7 @@
 
   // Returns the [int] representation of the specified value. Throws
   // [ArgumentError] for non-integer arguments.
-  int _toInt(val) {
+  int _toInt(Object val) {
     if (val is Int32) {
       return val._i;
     } else if (val is int) {
@@ -145,76 +145,88 @@
   // Int32 % Int32 => Int32
   // Int32 % Int64 => Int32
 
-  IntX operator +(other) {
+  @override
+  IntX operator +(Object other) {
     if (other is Int64) {
-      return this.toInt64() + other;
+      return toInt64() + other;
     }
     return Int32(_i + _toInt(other));
   }
 
-  IntX operator -(other) {
+  @override
+  IntX operator -(Object other) {
     if (other is Int64) {
-      return this.toInt64() - other;
+      return toInt64() - other;
     }
     return Int32(_i - _toInt(other));
   }
 
+  @override
   Int32 operator -() => Int32(-_i);
 
-  IntX operator *(other) {
+  @override
+  IntX operator *(Object other) {
     if (other is Int64) {
-      return this.toInt64() * other;
+      return toInt64() * other;
     }
     // TODO(rice) - optimize
-    return (this.toInt64() * other).toInt32();
+    return (toInt64() * other).toInt32();
   }
 
-  Int32 operator %(other) {
+  @override
+  Int32 operator %(Object other) {
     if (other is Int64) {
       // Result will be Int32
-      return (this.toInt64() % other).toInt32();
+      return (toInt64() % other).toInt32();
     }
     return Int32(_i % _toInt(other));
   }
 
-  Int32 operator ~/(other) {
+  @override
+  Int32 operator ~/(Object other) {
     if (other is Int64) {
-      return (this.toInt64() ~/ other).toInt32();
+      return (toInt64() ~/ other).toInt32();
     }
     return Int32(_i ~/ _toInt(other));
   }
 
-  Int32 remainder(other) {
+  @override
+  Int32 remainder(Object other) {
     if (other is Int64) {
-      Int64 t = this.toInt64();
+      var t = toInt64();
       return (t - (t ~/ other) * other).toInt32();
     }
     return (this - (this ~/ other) * other) as Int32;
   }
 
-  Int32 operator &(other) {
+  @override
+  Int32 operator &(Object other) {
     if (other is Int64) {
-      return (this.toInt64() & other).toInt32();
+      return (toInt64() & other).toInt32();
     }
     return Int32(_i & _toInt(other));
   }
 
-  Int32 operator |(other) {
+  @override
+  Int32 operator |(Object other) {
     if (other is Int64) {
-      return (this.toInt64() | other).toInt32();
+      return (toInt64() | other).toInt32();
     }
     return Int32(_i | _toInt(other));
   }
 
-  Int32 operator ^(other) {
+  @override
+  Int32 operator ^(Object other) {
     if (other is Int64) {
-      return (this.toInt64() ^ other).toInt32();
+      return (toInt64() ^ other).toInt32();
     }
     return Int32(_i ^ _toInt(other));
   }
 
+  @override
   Int32 operator ~() => Int32(~_i);
 
+  @override
   Int32 operator <<(int n) {
     if (n < 0) {
       throw ArgumentError(n);
@@ -225,6 +237,7 @@
     return Int32(_i << n);
   }
 
+  @override
   Int32 operator >>(int n) {
     if (n < 0) {
       throw ArgumentError(n);
@@ -241,6 +254,7 @@
     return Int32(value);
   }
 
+  @override
   Int32 shiftRightUnsigned(int n) {
     if (n < 0) {
       throw ArgumentError(n);
@@ -259,65 +273,87 @@
 
   /// Returns [:true:] if this [Int32] has the same numeric value as the
   /// given object.  The argument may be an [int] or an [IntX].
-  bool operator ==(other) {
+  @override
+  bool operator ==(Object other) {
     if (other is Int32) {
       return _i == other._i;
     } else if (other is Int64) {
-      return this.toInt64() == other;
+      return toInt64() == other;
     } else if (other is int) {
       return _i == other;
     }
     return false;
   }
 
-  int compareTo(other) {
+  @override
+  int compareTo(Object other) {
     if (other is Int64) {
-      return this.toInt64().compareTo(other);
+      return toInt64().compareTo(other);
     }
     return _i.compareTo(_toInt(other));
   }
 
-  bool operator <(other) {
+  @override
+  bool operator <(Object other) {
     if (other is Int64) {
-      return this.toInt64() < other;
+      return toInt64() < other;
     }
     return _i < _toInt(other);
   }
 
-  bool operator <=(other) {
+  @override
+  bool operator <=(Object other) {
     if (other is Int64) {
-      return this.toInt64() <= other;
+      return toInt64() <= other;
     }
     return _i <= _toInt(other);
   }
 
-  bool operator >(other) {
+  @override
+  bool operator >(Object other) {
     if (other is Int64) {
-      return this.toInt64() > other;
+      return toInt64() > other;
     }
     return _i > _toInt(other);
   }
 
-  bool operator >=(other) {
+  @override
+  bool operator >=(Object other) {
     if (other is Int64) {
-      return this.toInt64() >= other;
+      return toInt64() >= other;
     }
     return _i >= _toInt(other);
   }
 
+  @override
   bool get isEven => (_i & 0x1) == 0;
+
+  @override
   bool get isMaxValue => _i == 2147483647;
+
+  @override
   bool get isMinValue => _i == -2147483648;
+
+  @override
   bool get isNegative => _i < 0;
+
+  @override
   bool get isOdd => (_i & 0x1) == 1;
+
+  @override
   bool get isZero => _i == 0;
+
+  @override
   int get bitLength => _i.bitLength;
 
+  @override
   int get hashCode => _i;
 
+  @override
   Int32 abs() => _i < 0 ? Int32(-_i) : this;
 
-  Int32 clamp(lowerLimit, upperLimit) {
+  @override
+  Int32 clamp(Object lowerLimit, Object upperLimit) {
     if (this < lowerLimit) {
       if (lowerLimit is IntX) return lowerLimit.toInt32();
       if (lowerLimit is int) return Int32(lowerLimit);
@@ -330,21 +366,27 @@
     return this;
   }
 
+  @override
   int numberOfLeadingZeros() => _numberOfLeadingZeros(_i);
+
+  @override
   int numberOfTrailingZeros() => _numberOfTrailingZeros(_i);
 
+  @override
   Int32 toSigned(int width) {
     if (width < 1 || width > 32) throw RangeError.range(width, 1, 32);
     return Int32(_i.toSigned(width));
   }
 
+  @override
   Int32 toUnsigned(int width) {
     if (width < 0 || width > 32) throw RangeError.range(width, 0, 32);
     return Int32(_i.toUnsigned(width));
   }
 
+  @override
   List<int> toBytes() {
-    List<int> result = List<int>(4);
+    var result = List<int>.filled(4, 0);
     result[0] = _i & 0xff;
     result[1] = (_i >> 8) & 0xff;
     result[2] = (_i >> 16) & 0xff;
@@ -352,12 +394,24 @@
     return result;
   }
 
+  @override
   double toDouble() => _i.toDouble();
+
+  @override
   int toInt() => _i;
+
+  @override
   Int32 toInt32() => this;
+
+  @override
   Int64 toInt64() => Int64(_i);
 
+  @override
   String toString() => _i.toString();
+
+  @override
   String toHexString() => _i.toRadixString(16);
+
+  @override
   String toRadixString(int radix) => _i.toRadixString(radix);
 }
diff --git a/fixnum/lib/src/int64.dart b/fixnum/lib/src/int64.dart
index 3453a09..24de9d1 100644
--- a/fixnum/lib/src/int64.dart
+++ b/fixnum/lib/src/int64.dart
@@ -2,6 +2,11 @@
 // 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.
 
+// Many locals are declared as `int` or `double`. We keep local variable types
+// because the types are critical to the efficiency of many operations.
+//
+// ignore_for_file: omit_local_variable_types
+
 part of fixnum;
 
 /// An immutable 64-bit signed integer, in the range [-2^63, 2^63 - 1].
@@ -75,7 +80,7 @@
       int c = s.codeUnitAt(i);
       int digit = Int32._decodeDigit(c);
       if (digit < 0 || digit >= radix) {
-        throw FormatException("Non-radix char code: $c");
+        throw FormatException('Non-radix char code: $c');
       }
 
       // [radix] and [digit] are at most 6 bits, component is 22, so we can
@@ -192,7 +197,8 @@
     throw ArgumentError.value(value);
   }
 
-  Int64 operator +(other) {
+  @override
+  Int64 operator +(Object other) {
     Int64 o = _promote(other);
     int sum0 = _l + o._l;
     int sum1 = _m + o._m + (sum0 >> _BITS);
@@ -200,14 +206,17 @@
     return Int64._masked(sum0, sum1, sum2);
   }
 
-  Int64 operator -(other) {
+  @override
+  Int64 operator -(Object other) {
     Int64 o = _promote(other);
     return _sub(_l, _m, _h, o._l, o._m, o._h);
   }
 
+  @override
   Int64 operator -() => _negate(_l, _m, _h);
 
-  Int64 operator *(other) {
+  @override
+  Int64 operator *(Object other) {
     Int64 o = _promote(other);
 
     // Grab 13-bit chunks.
@@ -288,13 +297,17 @@
     return Int64._masked(c0, c1, c2);
   }
 
-  Int64 operator %(other) => _divide(this, other, _RETURN_MOD);
+  @override
+  Int64 operator %(Object other) => _divide(this, other, _RETURN_MOD);
 
-  Int64 operator ~/(other) => _divide(this, other, _RETURN_DIV);
+  @override
+  Int64 operator ~/(Object other) => _divide(this, other, _RETURN_DIV);
 
-  Int64 remainder(other) => _divide(this, other, _RETURN_REM);
+  @override
+  Int64 remainder(Object other) => _divide(this, other, _RETURN_REM);
 
-  Int64 operator &(other) {
+  @override
+  Int64 operator &(Object other) {
     Int64 o = _promote(other);
     int a0 = _l & o._l;
     int a1 = _m & o._m;
@@ -302,7 +315,8 @@
     return Int64._masked(a0, a1, a2);
   }
 
-  Int64 operator |(other) {
+  @override
+  Int64 operator |(Object other) {
     Int64 o = _promote(other);
     int a0 = _l | o._l;
     int a1 = _m | o._m;
@@ -310,7 +324,8 @@
     return Int64._masked(a0, a1, a2);
   }
 
-  Int64 operator ^(other) {
+  @override
+  Int64 operator ^(Object other) {
     Int64 o = _promote(other);
     int a0 = _l ^ o._l;
     int a1 = _m ^ o._m;
@@ -318,10 +333,12 @@
     return Int64._masked(a0, a1, a2);
   }
 
+  @override
   Int64 operator ~() {
     return Int64._masked(~_l, ~_m, ~_h);
   }
 
+  @override
   Int64 operator <<(int n) {
     if (n < 0) {
       throw ArgumentError.value(n);
@@ -348,6 +365,7 @@
     return Int64._masked(res0, res1, res2);
   }
 
+  @override
   Int64 operator >>(int n) {
     if (n < 0) {
       throw ArgumentError.value(n);
@@ -393,6 +411,7 @@
     return Int64._masked(res0, res1, res2);
   }
 
+  @override
   Int64 shiftRightUnsigned(int n) {
     if (n < 0) {
       throw ArgumentError.value(n);
@@ -422,8 +441,9 @@
 
   /// Returns [:true:] if this [Int64] has the same numeric value as the
   /// given object.  The argument may be an [int] or an [IntX].
-  bool operator ==(other) {
-    Int64 o;
+  @override
+  bool operator ==(Object other) {
+    Int64? o;
     if (other is Int64) {
       o = other;
     } else if (other is int) {
@@ -441,9 +461,10 @@
     return false;
   }
 
-  int compareTo(other) => _compareTo(other);
+  @override
+  int compareTo(Object other) => _compareTo(other);
 
-  int _compareTo(other) {
+  int _compareTo(Object other) {
     Int64 o = _promote(other);
     int signa = _h >> (_BITS2 - 1);
     int signb = o._h >> (_BITS2 - 1);
@@ -468,18 +489,37 @@
     return 0;
   }
 
-  bool operator <(other) => _compareTo(other) < 0;
-  bool operator <=(other) => _compareTo(other) <= 0;
-  bool operator >(other) => this._compareTo(other) > 0;
-  bool operator >=(other) => _compareTo(other) >= 0;
+  @override
+  bool operator <(Object other) => _compareTo(other) < 0;
 
+  @override
+  bool operator <=(Object other) => _compareTo(other) <= 0;
+
+  @override
+  bool operator >(Object other) => _compareTo(other) > 0;
+
+  @override
+  bool operator >=(Object other) => _compareTo(other) >= 0;
+
+  @override
   bool get isEven => (_l & 0x1) == 0;
+
+  @override
   bool get isMaxValue => (_h == _MASK2 >> 1) && _m == _MASK && _l == _MASK;
+
+  @override
   bool get isMinValue => _h == _SIGN_BIT_MASK && _m == 0 && _l == 0;
+
+  @override
   bool get isNegative => (_h & _SIGN_BIT_MASK) != 0;
+
+  @override
   bool get isOdd => (_l & 0x1) == 1;
+
+  @override
   bool get isZero => _h == 0 && _m == 0 && _l == 0;
 
+  @override
   int get bitLength {
     if (isZero) return 0;
     int a0 = _l, a1 = _m, a2 = _h;
@@ -494,6 +534,7 @@
   }
 
   /// Returns a hash code based on all the bits of this [Int64].
+  @override
   int get hashCode {
     // TODO(sra): Should we ensure that hashCode values match corresponding int?
     // i.e. should `new Int64(x).hashCode == x.hashCode`?
@@ -502,11 +543,13 @@
     return bottom ^ top;
   }
 
+  @override
   Int64 abs() {
-    return this.isNegative ? -this : this;
+    return isNegative ? -this : this;
   }
 
-  Int64 clamp(lowerLimit, upperLimit) {
+  @override
+  Int64 clamp(Object lowerLimit, Object upperLimit) {
     Int64 lower = _promote(lowerLimit);
     Int64 upper = _promote(upperLimit);
     if (this < lower) return lower;
@@ -516,6 +559,7 @@
 
   /// Returns the number of leading zeros in this [Int64] as an [int]
   /// between 0 and 64.
+  @override
   int numberOfLeadingZeros() {
     int b2 = Int32._numberOfLeadingZeros(_h);
     if (b2 == 32) {
@@ -532,6 +576,7 @@
 
   /// Returns the number of trailing zeros in this [Int64] as an [int]
   /// between 0 and 64.
+  @override
   int numberOfTrailingZeros() {
     int zeros = Int32._numberOfTrailingZeros(_l);
     if (zeros < 32) {
@@ -551,6 +596,7 @@
     return 64;
   }
 
+  @override
   Int64 toSigned(int width) {
     if (width < 1 || width > 64) throw RangeError.range(width, 1, 64);
     if (width > _BITS01) {
@@ -568,6 +614,7 @@
     }
   }
 
+  @override
   Int64 toUnsigned(int width) {
     if (width < 0 || width > 64) throw RangeError.range(width, 0, 64);
     if (width > _BITS01) {
@@ -582,8 +629,9 @@
     }
   }
 
+  @override
   List<int> toBytes() {
-    List<int> result = List<int>(8);
+    var result = List<int>.filled(8, 0);
     result[0] = _l & 0xff;
     result[1] = (_l >> 8) & 0xff;
     result[2] = ((_m << 6) & 0xfc) | ((_l >> 16) & 0x3f);
@@ -595,8 +643,10 @@
     return result;
   }
 
+  @override
   double toDouble() => toInt().toDouble();
 
+  @override
   int toInt() {
     int l = _l;
     int m = _m;
@@ -614,24 +664,28 @@
   }
 
   /// Returns an [Int32] containing the low 32 bits of this [Int64].
+  @override
   Int32 toInt32() {
     return Int32(((_m & 0x3ff) << _BITS) | _l);
   }
 
   /// Returns `this`.
+  @override
   Int64 toInt64() => this;
 
   /// Returns the value of this [Int64] as a decimal [String].
+  @override
   String toString() => _toRadixString(10);
 
   // TODO(rice) - Make this faster by avoiding arithmetic.
+  @override
   String toHexString() {
-    if (isZero) return "0";
+    if (isZero) return '0';
     Int64 x = this;
-    String hexStr = "";
+    String hexStr = '';
     while (!x.isZero) {
       int digit = x._l & 0xf;
-      hexStr = "${_hexDigit(digit)}$hexStr";
+      hexStr = '${_hexDigit(digit)}$hexStr';
       x = x.shiftRightUnsigned(4);
     }
     return hexStr;
@@ -648,6 +702,7 @@
     return _toRadixStringUnsigned(Int32._validateRadix(radix), _l, _m, _h, '');
   }
 
+  @override
   String toRadixString(int radix) {
     return _toRadixString(Int32._validateRadix(radix));
   }
@@ -712,7 +767,7 @@
     // need only two chunks, but radix values 17-19 and 33-36 generate only 15
     // or 16 bits per iteration, so sometimes the third chunk is needed.
 
-    String chunk1 = "", chunk2 = "", chunk3 = "";
+    String chunk1 = '', chunk2 = '', chunk3 = '';
 
     while (!(d4 == 0 && d3 == 0)) {
       int q = d4 ~/ fatRadix;
@@ -739,7 +794,7 @@
       r = d0 - q * fatRadix;
       d0 = q;
 
-      assert(chunk3 == "");
+      assert(chunk3 == '');
       chunk3 = chunk2;
       chunk2 = chunk1;
       // Adding [fatRadix] Forces an extra digit which we discard to get a fixed
@@ -814,7 +869,7 @@
   ];
 
   String toDebugString() {
-    return "Int64[_l=$_l, _m=$_m, _h=$_h]";
+    return 'Int64[_l=$_l, _m=$_m, _h=$_h]';
   }
 
   static Int64 _masked(int a0, int a1, int a2) =>
@@ -831,7 +886,7 @@
     return _sub(0, 0, 0, b0, b1, b2);
   }
 
-  String _hexDigit(int digit) => "0123456789ABCDEF"[digit];
+  String _hexDigit(int digit) => '0123456789ABCDEF'[digit];
 
   // Work around dart2js bugs with negative arguments to '>>' operator.
   static int _shiftRight(int x, int n) {
diff --git a/fixnum/lib/src/intx.dart b/fixnum/lib/src/intx.dart
index cdc4b05..4c0dd61 100644
--- a/fixnum/lib/src/intx.dart
+++ b/fixnum/lib/src/intx.dart
@@ -5,12 +5,12 @@
 part of fixnum;
 
 /// A fixed-precision integer.
-abstract class IntX implements Comparable<dynamic> {
+abstract class IntX implements Comparable<Object> {
   /// Addition operator.
-  IntX operator +(other);
+  IntX operator +(Object other);
 
   /// Subtraction operator.
-  IntX operator -(other);
+  IntX operator -(Object other);
 
   /// Negate operator.
   ///
@@ -18,30 +18,30 @@
   IntX operator -();
 
   /// Multiplication operator.
-  IntX operator *(other);
+  IntX operator *(Object other);
 
   /// Euclidean modulo operator.
   ///
   /// Returns the remainder of the euclidean division. The euclidean division
   /// of two integers `a` and `b` yields two integers `q` and `r` such that
   /// `a == b * q + r` and `0 <= r < a.abs()`.
-  IntX operator %(other);
+  IntX operator %(Object other);
 
   /// Truncating division operator.
-  IntX operator ~/(other);
+  IntX operator ~/(Object other);
 
   /// Returns the remainder of the truncating division of this integer by
   /// [other].
-  IntX remainder(other);
+  IntX remainder(Object other);
 
   /// Bitwise and operator.
-  IntX operator &(other);
+  IntX operator &(Object other);
 
   /// Bitwise or operator.
-  IntX operator |(other);
+  IntX operator |(Object other);
 
   /// Bitwise xor operator.
-  IntX operator ^(other);
+  IntX operator ^(Object other);
 
   /// Bitwise negate operator.
   IntX operator ~();
@@ -65,23 +65,25 @@
   /// bits to the right. High-order bits are filled with zeros.
   IntX shiftRightUnsigned(int shiftAmount);
 
-  int compareTo(other);
+  @override
+  int compareTo(Object other);
 
   /// Returns `true` if and only if [other] is an int or IntX equal in
   /// value to this integer.
-  bool operator ==(other);
+  @override
+  bool operator ==(Object other);
 
   /// Relational less than operator.
-  bool operator <(other);
+  bool operator <(Object other);
 
   /// Relational less than or equal to operator.
-  bool operator <=(other);
+  bool operator <=(Object other);
 
   /// Relational greater than operator.
-  bool operator >(other);
+  bool operator >(Object other);
 
   /// Relational greater than or equal to operator.
-  bool operator >=(other);
+  bool operator >=(Object other);
 
   /// Returns `true` if and only if this integer is even.
   bool get isEven;
@@ -103,13 +105,14 @@
   /// Returns `true` if and only if this integer is zero.
   bool get isZero;
 
+  @override
   int get hashCode;
 
   /// Returns the absolute value of this integer.
   IntX abs();
 
   /// Clamps this integer to be in the range [lowerLimit] - [upperLimit].
-  IntX clamp(lowerLimit, upperLimit);
+  IntX clamp(Object lowerLimit, Object upperLimit);
 
   /// Returns the minimum number of bits required to store this integer.
   ///
@@ -179,6 +182,7 @@
 
   /// Returns a string representing the value of this integer in decimal
   /// notation; example: `'13'`.
+  @override
   String toString();
 
   /// Returns a string representing the value of this integer in hexadecimal
diff --git a/fixnum/pubspec.yaml b/fixnum/pubspec.yaml
index a08ee33..7dc7da0 100644
--- a/fixnum/pubspec.yaml
+++ b/fixnum/pubspec.yaml
@@ -1,13 +1,14 @@
 name: fixnum
-version: 0.10.11
+version: 1.0.0
 
-description: Library for 32- and 64-bit signed fixed-width integers.
-author: Dart Team <misc@dartlang.org>
+description: >-
+  Library for 32- and 64-bit signed fixed-width integers with consistent
+  behavior between native and JS runtimes.
 homepage: https://github.com/dart-lang/fixnum
 
 environment:
-  sdk: '>=2.1.1 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dev_dependencies:
-  pedantic: ^1.8.0
-  test: ^1.2.0
+  pedantic: ^1.10.0
+  test: ^1.16.0-nullsafety
diff --git a/flutter_flux/BUILD.gn b/flutter_flux/BUILD.gn
index 2fac41d..cd78583 100644
--- a/flutter_flux/BUILD.gn
+++ b/flutter_flux/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for flutter_flux-4.1.3
+# This file is generated by package_importer.py for flutter_flux-4.1.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/flutter_image/BUILD.gn b/flutter_image/BUILD.gn
index 95af7bb..7e30636 100644
--- a/flutter_image/BUILD.gn
+++ b/flutter_image/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for flutter_image-3.0.0
+# This file is generated by package_importer.py for flutter_image-3.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/flutter_mobx/BUILD.gn b/flutter_mobx/BUILD.gn
index 428c13d..b06f499 100644
--- a/flutter_mobx/BUILD.gn
+++ b/flutter_mobx/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for flutter_mobx-2.0.0
+# This file is generated by package_importer.py for flutter_mobx-2.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/flutter_staggered_grid_view/BUILD.gn b/flutter_staggered_grid_view/BUILD.gn
index 870eacb..a676ddd 100644
--- a/flutter_staggered_grid_view/BUILD.gn
+++ b/flutter_staggered_grid_view/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for flutter_staggered_grid_view-0.2.7
+# This file is generated by package_importer.py for flutter_staggered_grid_view-0.2.7
 
 import("//build/dart/dart_library.gni")
 
diff --git a/flutter_svg/BUILD.gn b/flutter_svg/BUILD.gn
index 87819e0..6a69afa 100644
--- a/flutter_svg/BUILD.gn
+++ b/flutter_svg/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for flutter_svg-0.20.0-nullsafety.3
+# This file is generated by package_importer.py for flutter_svg-0.20.0-nullsafety.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/flutter_template_images/BUILD.gn b/flutter_template_images/BUILD.gn
index d6b3ce4..2a6e221 100644
--- a/flutter_template_images/BUILD.gn
+++ b/flutter_template_images/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for flutter_template_images-3.0.1
+# This file is generated by package_importer.py for flutter_template_images-3.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/frontend_server_client/BUILD.gn b/frontend_server_client/BUILD.gn
index 2ab9d89..4c52627 100644
--- a/frontend_server_client/BUILD.gn
+++ b/frontend_server_client/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for frontend_server_client-2.1.0
+# This file is generated by package_importer.py for frontend_server_client-2.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/glob/BUILD.gn b/glob/BUILD.gn
index 04f3764..9b58ea7 100644
--- a/glob/BUILD.gn
+++ b/glob/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for glob-2.0.1
+# This file is generated by package_importer.py for glob-2.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/html/BUILD.gn b/html/BUILD.gn
index c993cdb..996e41c 100644
--- a/html/BUILD.gn
+++ b/html/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for html-0.15.0
+# This file is generated by package_importer.py for html-0.15.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/html_unescape/BUILD.gn b/html_unescape/BUILD.gn
index 0acb9d5..94f3f85 100644
--- a/html_unescape/BUILD.gn
+++ b/html_unescape/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for html_unescape-1.0.2
+# This file is generated by package_importer.py for html_unescape-1.0.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/http/BUILD.gn b/http/BUILD.gn
index 1bebecc..6a6df8c 100644
--- a/http/BUILD.gn
+++ b/http/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for http-0.13.3
+# This file is generated by package_importer.py for http-0.13.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/http_multi_server/BUILD.gn b/http_multi_server/BUILD.gn
index 6f1f131..f0b63e6 100644
--- a/http_multi_server/BUILD.gn
+++ b/http_multi_server/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for http_multi_server-3.0.1
+# This file is generated by package_importer.py for http_multi_server-3.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/http_parser/BUILD.gn b/http_parser/BUILD.gn
index 532710f..082a658 100644
--- a/http_parser/BUILD.gn
+++ b/http_parser/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for http_parser-4.0.0
+# This file is generated by package_importer.py for http_parser-4.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/image/BUILD.gn b/image/BUILD.gn
index a692967..7f63779 100644
--- a/image/BUILD.gn
+++ b/image/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for image-3.0.2
+# This file is generated by package_importer.py for image-3.0.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/intl/BUILD.gn b/intl/BUILD.gn
index 981f91b..4019c11 100644
--- a/intl/BUILD.gn
+++ b/intl/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for intl-0.17.0
+# This file is generated by package_importer.py for intl-0.17.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/io/BUILD.gn b/io/BUILD.gn
index f11eb89..d59b900 100644
--- a/io/BUILD.gn
+++ b/io/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for io-1.0.0
+# This file is generated by package_importer.py for io-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/js/BUILD.gn b/js/BUILD.gn
index 56a7b62..b73c4ed 100644
--- a/js/BUILD.gn
+++ b/js/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for js-0.6.3
+# This file is generated by package_importer.py for js-0.6.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/json_annotation/BUILD.gn b/json_annotation/BUILD.gn
index 018419e..97b1161 100644
--- a/json_annotation/BUILD.gn
+++ b/json_annotation/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for json_annotation-3.1.1
+# This file is generated by package_importer.py for json_annotation-3.1.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/json_rpc_2/.github/workflows/test-package.yml b/json_rpc_2/.github/workflows/test-package.yml
new file mode 100644
index 0000000..e47bf66
--- /dev/null
+++ b/json_rpc_2/.github/workflows/test-package.yml
@@ -0,0 +1,61 @@
+name: Dart CI
+
+on:
+  # Run on PRs and pushes to the default branch.
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+  schedule:
+    - cron: "0 0 * * 0"
+
+env:
+  PUB_ENVIRONMENT: bot.github
+
+jobs:
+  # Check code formatting and static analysis on a single OS (linux)
+  # against Dart dev.
+  analyze:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        sdk: [dev]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dart-lang/setup-dart@v1.0
+        with:
+          sdk: ${{ matrix.sdk }}
+      - id: install
+        name: Install dependencies
+        run: dart pub get
+      - name: Check formatting
+        run: dart format --output=none --set-exit-if-changed .
+        if: always() && steps.install.outcome == 'success'
+      - name: Analyze code
+        run: dart analyze --fatal-infos
+        if: always() && steps.install.outcome == 'success'
+
+  # Run tests on a matrix consisting of two dimensions:
+  # 1. OS: ubuntu-latest, (macos-latest, windows-latest)
+  # 2. release channel: dev
+  test:
+    needs: analyze
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        # Add macos-latest and/or windows-latest if relevant for this package.
+        os: [ubuntu-latest]
+        sdk: [2.12.0, dev]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dart-lang/setup-dart@v1.0
+        with:
+          sdk: ${{ matrix.sdk }}
+      - id: install
+        name: Install dependencies
+        run: dart pub get
+      - name: Run VM tests
+        run: dart test --platform vm
+        if: always() && steps.install.outcome == 'success'
diff --git a/json_rpc_2/.travis.yml b/json_rpc_2/.travis.yml
deleted file mode 100644
index 3537f65..0000000
--- a/json_rpc_2/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: dart
-
-dart:
-  - dev
-  - 2.2.0
-
-dart_task:
-  - test
-  - dartanalyzer: --fatal-infos --fatal-warnings .
-
-matrix:
-  include:
-    - dart: dev
-      dart_task: dartfmt
-
-# Only building master means that we don't run two builds for each pull request.
-branches:
-  only: [master]
-
-cache:
- directories:
-   - $HOME/.pub-cache
diff --git a/json_rpc_2/BUILD.gn b/json_rpc_2/BUILD.gn
index 6b6dcd0..319e04b 100644
--- a/json_rpc_2/BUILD.gn
+++ b/json_rpc_2/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for json_rpc_2-2.2.2
+# This file is generated by package_importer.py for json_rpc_2-3.0.1
 
 import("//build/dart/dart_library.gni")
 
 dart_library("json_rpc_2") {
   package_name = "json_rpc_2"
 
-  language_version = "2.2"
+  language_version = "2.12"
 
   disable_analysis = true
 
diff --git a/json_rpc_2/CHANGELOG.md b/json_rpc_2/CHANGELOG.md
index c7f1652..d28d50f 100644
--- a/json_rpc_2/CHANGELOG.md
+++ b/json_rpc_2/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 3.0.1
+
+* Fix a bug where a `null` result to a request caused an exception.
+
+## 3.0.0
+
+* Migrate to null safety.
+* Accept responses even if the server converts the ID to a String.
+
 ## 2.2.2
 
 * Fix `Peer.close()` throwing `Bad state: Future already completed`.
diff --git a/json_rpc_2/LICENSE b/json_rpc_2/LICENSE
index 5c60afe..000cd7b 100644
--- a/json_rpc_2/LICENSE
+++ b/json_rpc_2/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2014, the Dart project authors. All rights reserved.
+Copyright 2014, the Dart project authors. 
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/json_rpc_2/README.md b/json_rpc_2/README.md
index 64562ac..fef7008 100644
--- a/json_rpc_2/README.md
+++ b/json_rpc_2/README.md
@@ -1,6 +1,6 @@
 A library that implements the [JSON-RPC 2.0 spec][spec].
 
-[spec]: http://www.jsonrpc.org/specification
+[spec]: https://www.jsonrpc.org/specification
 
 ## Server
 
diff --git a/json_rpc_2/lib/error_code.dart b/json_rpc_2/lib/error_code.dart
index 4bb0808..f4ca462 100644
--- a/json_rpc_2/lib/error_code.dart
+++ b/json_rpc_2/lib/error_code.dart
@@ -37,7 +37,7 @@
 /// JSON-RPC 2.0 spec.
 ///
 /// If [errorCode] isn't defined in the JSON-RPC 2.0 spec, returns null.
-String name(int errorCode) {
+String? name(int errorCode) {
   switch (errorCode) {
     case PARSE_ERROR:
       return 'parse error';
diff --git a/json_rpc_2/lib/src/client.dart b/json_rpc_2/lib/src/client.dart
index 33a529c..7e040cc 100644
--- a/json_rpc_2/lib/src/client.dart
+++ b/json_rpc_2/lib/src/client.dart
@@ -24,7 +24,7 @@
   /// The current batch of requests to be sent together.
   ///
   /// Each element is a JSON RPC spec compliant message.
-  List<Map<String, dynamic>> _batch;
+  List<Map<String, dynamic>>? _batch;
 
   /// The map of request ids to pending requests.
   final _pendingRequests = <int, _Request>{};
@@ -64,10 +64,8 @@
   Client.withoutJson(this._channel) {
     done.whenComplete(() {
       for (var request in _pendingRequests.values) {
-        request.completer.completeError(
-            StateError(
-                'The client closed with pending request "${request.method}".'),
-            StackTrace.current);
+        request.completer.completeError(StateError(
+            'The client closed with pending request "${request.method}".'));
       }
       _pendingRequests.clear();
     }).catchError((_) {
@@ -143,7 +141,7 @@
   ///
   /// Sends a request to invoke [method] with [parameters]. If [id] is given,
   /// the request uses that id.
-  void _send(String method, parameters, [int id]) {
+  void _send(String method, parameters, [int? id]) {
     if (parameters is Iterable) parameters = parameters.toList();
     if (parameters is! Map && parameters is! List && parameters != null) {
       throw ArgumentError('Only maps and lists may be used as JSON-RPC '
@@ -156,7 +154,7 @@
     if (parameters != null) message['params'] = parameters;
 
     if (_batch != null) {
-      _batch.add(message);
+      _batch!.add(message);
     } else {
       _channel.sink.add(message);
     }
@@ -197,7 +195,9 @@
   /// resolved.
   void _handleSingleResponse(response) {
     if (!_isResponseValid(response)) return;
-    var request = _pendingRequests.remove(response['id']);
+    var id = response['id'];
+    id = (id is String) ? int.parse(id) : id;
+    var request = _pendingRequests.remove(id)!;
     if (response.containsKey('result')) {
       request.completer.complete(response['result']);
     } else {
@@ -212,7 +212,9 @@
   bool _isResponseValid(response) {
     if (response is! Map) return false;
     if (response['jsonrpc'] != '2.0') return false;
-    if (!_pendingRequests.containsKey(response['id'])) return false;
+    var id = response['id'];
+    id = (id is String) ? int.parse(id) : id;
+    if (!_pendingRequests.containsKey(id)) return false;
     if (response.containsKey('result')) return true;
 
     if (!response.containsKey('error')) return false;
diff --git a/json_rpc_2/lib/src/peer.dart b/json_rpc_2/lib/src/peer.dart
index 4cf6aae..a11cff2 100644
--- a/json_rpc_2/lib/src/peer.dart
+++ b/json_rpc_2/lib/src/peer.dart
@@ -21,11 +21,11 @@
 
   /// The underlying client that handles request-sending and response-receiving
   /// logic.
-  Client _client;
+  late final Client _client;
 
   /// The underlying server that handles request-receiving and response-sending
   /// logic.
-  Server _server;
+  late final Server _server;
 
   /// A stream controller that forwards incoming messages to [_server] if
   /// they're requests.
@@ -35,14 +35,14 @@
   /// they're responses.
   final _clientIncomingForwarder = StreamController(sync: true);
 
-  Future<void> _done;
   @override
-  Future get done => _done ??= Future.wait([_client.done, _server.done]);
+  late final Future done = Future.wait([_client.done, _server.done]);
+
   @override
   bool get isClosed => _client.isClosed || _server.isClosed;
 
   @override
-  ErrorCallback get onUnhandledError => _server?.onUnhandledError;
+  ErrorCallback? get onUnhandledError => _server.onUnhandledError;
 
   @override
   bool get strictProtocolChecks => _server.strictProtocolChecks;
@@ -60,7 +60,7 @@
   /// specification. In particular, requests missing the `jsonrpc` parameter
   /// will be accepted.
   Peer(StreamChannel<String> channel,
-      {ErrorCallback onUnhandledError, bool strictProtocolChecks = true})
+      {ErrorCallback? onUnhandledError, bool strictProtocolChecks = true})
       : this.withoutJson(
             jsonDocument.bind(channel).transform(respondToFormatExceptions),
             onUnhandledError: onUnhandledError,
@@ -82,7 +82,7 @@
   /// specification. In particular, requests missing the `jsonrpc` parameter
   /// will be accepted.
   Peer.withoutJson(this._channel,
-      {ErrorCallback onUnhandledError, bool strictProtocolChecks = true}) {
+      {ErrorCallback? onUnhandledError, bool strictProtocolChecks = true}) {
     _server = Server.withoutJson(
         StreamChannel(_serverIncomingForwarder.stream, _channel.sink),
         onUnhandledError: onUnhandledError,
diff --git a/json_rpc_2/lib/src/server.dart b/json_rpc_2/lib/src/server.dart
index 553b67b..ac34edf 100644
--- a/json_rpc_2/lib/src/server.dart
+++ b/json_rpc_2/lib/src/server.dart
@@ -60,7 +60,7 @@
   /// In the case where a user provided callback results in an exception that
   /// cannot be properly routed back to the client, this handler will be
   /// invoked. If it is not set, the exception will be swallowed.
-  final ErrorCallback onUnhandledError;
+  final ErrorCallback? onUnhandledError;
 
   /// Whether to strictly enforce the JSON-RPC 2.0 specification for received
   /// messages.
@@ -82,7 +82,7 @@
   /// requests which are not conformant with the JSON-RPC 2.0 specification. In
   /// particular, requests missing the `jsonrpc` parameter will be accepted.
   Server(StreamChannel<String> channel,
-      {ErrorCallback onUnhandledError, bool strictProtocolChecks = true})
+      {ErrorCallback? onUnhandledError, bool strictProtocolChecks = true})
       : this.withoutJson(
             jsonDocument.bind(channel).transform(respondToFormatExceptions),
             onUnhandledError: onUnhandledError,
@@ -201,7 +201,7 @@
       var method = _methods[name];
       method ??= _tryFallbacks;
 
-      Object result;
+      Object? result;
       if (method is ZeroArgumentFunction) {
         if (request.containsKey('params')) {
           throw RpcException.invalidParams('No parameters are allowed for '
diff --git a/json_rpc_2/lib/src/utils.dart b/json_rpc_2/lib/src/utils.dart
index 3f86f01..37523f0 100644
--- a/json_rpc_2/lib/src/utils.dart
+++ b/json_rpc_2/lib/src/utils.dart
@@ -40,25 +40,25 @@
     whenComplete();
     return result;
   } else {
-    return result.whenComplete(whenComplete);
+    result.whenComplete(whenComplete);
   }
 }
 
 /// A transformer that silently drops [FormatException]s.
-final ignoreFormatExceptions = StreamTransformer<Object, Object>.fromHandlers(
+final ignoreFormatExceptions = StreamTransformer<Object?, Object?>.fromHandlers(
     handleError: (error, stackTrace, sink) {
   if (error is FormatException) return;
   sink.addError(error, stackTrace);
 });
 
 /// A transformer that sends error responses on [FormatException]s.
-final StreamChannelTransformer<Object, Object> respondToFormatExceptions =
+final StreamChannelTransformer<Object?, Object?> respondToFormatExceptions =
     _RespondToFormatExceptionsTransformer();
 
 class _RespondToFormatExceptionsTransformer
-    implements StreamChannelTransformer<Object, Object> {
+    implements StreamChannelTransformer<Object?, Object?> {
   @override
-  StreamChannel<Object> bind(StreamChannel<Object> channel) {
+  StreamChannel<Object?> bind(StreamChannel<Object?> channel) {
     return channel.changeStream((stream) {
       return stream.handleError((dynamic error) {
         final formatException = error as FormatException;
diff --git a/json_rpc_2/pubspec.yaml b/json_rpc_2/pubspec.yaml
index 5515221..f7217f6 100644
--- a/json_rpc_2/pubspec.yaml
+++ b/json_rpc_2/pubspec.yaml
@@ -1,17 +1,17 @@
 name: json_rpc_2
-version: 2.2.2
+version: 3.0.1
 description: >-
   Utilities to write a client or server using the JSON-RPC 2.0 spec.
 homepage: https://github.com/dart-lang/json_rpc_2
 
 environment:
-  sdk: ">=2.2.0 <3.0.0"
+  sdk: ">=2.12.0 <3.0.0"
 
 dependencies:
-  stack_trace: ^1.0.0
-  stream_channel: ">=1.1.0 <3.0.0"
+  stack_trace: ^1.10.0
+  stream_channel: ^2.1.0
 
 dev_dependencies:
-  pedantic: ^1.8.0
-  test: ^1.0.0
-  web_socket_channel: ^1.1.0
+  pedantic: ^1.11.0
+  test: ^1.16.0
+  web_socket_channel: ^2.0.0
diff --git a/lists/BUILD.gn b/lists/BUILD.gn
index d6a6316..9dcaa1d 100644
--- a/lists/BUILD.gn
+++ b/lists/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for lists-0.1.6
+# This file is generated by package_importer.py for lists-0.1.6
 
 import("//build/dart/dart_library.gni")
 
diff --git a/logging/BUILD.gn b/logging/BUILD.gn
index d0d5816..395672c 100644
--- a/logging/BUILD.gn
+++ b/logging/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for logging-1.0.1
+# This file is generated by package_importer.py for logging-1.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/markdown/BUILD.gn b/markdown/BUILD.gn
index d122990..0edf617 100644
--- a/markdown/BUILD.gn
+++ b/markdown/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for markdown-4.0.0
+# This file is generated by package_importer.py for markdown-4.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/matcher/BUILD.gn b/matcher/BUILD.gn
index 7960e66..1ce81c7 100644
--- a/matcher/BUILD.gn
+++ b/matcher/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for matcher-0.12.10
+# This file is generated by package_importer.py for matcher-0.12.10
 
 import("//build/dart/dart_library.gni")
 
diff --git a/meta/BUILD.gn b/meta/BUILD.gn
index 931045d..96f4075 100644
--- a/meta/BUILD.gn
+++ b/meta/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for meta-1.4.0
+# This file is generated by package_importer.py for meta-1.4.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/mime/BUILD.gn b/mime/BUILD.gn
index 50c26c4..2bcef9a 100644
--- a/mime/BUILD.gn
+++ b/mime/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for mime-1.0.0
+# This file is generated by package_importer.py for mime-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/mobx/BUILD.gn b/mobx/BUILD.gn
index 5f757cf..8485c23 100644
--- a/mobx/BUILD.gn
+++ b/mobx/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for mobx-2.0.1
+# This file is generated by package_importer.py for mobx-2.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/mockito/BUILD.gn b/mockito/BUILD.gn
index 38dd68d..e74c303 100644
--- a/mockito/BUILD.gn
+++ b/mockito/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for mockito-5.0.7
+# This file is generated by package_importer.py for mockito-5.0.10
 
 import("//build/dart/dart_library.gni")
 
diff --git a/mockito/CHANGELOG.md b/mockito/CHANGELOG.md
index 57fb6dc..2975682 100644
--- a/mockito/CHANGELOG.md
+++ b/mockito/CHANGELOG.md
@@ -1,3 +1,41 @@
+## 5.0.10
+
+* Generate a proper mock class when the mocked class overrides `toString`,
+  `hashCode`, or `operator==`.
+  [#420](https://github.com/dart-lang/mockito/issues/420)
+* Override `toString` implementation on generated Fakes in order to match the
+  signature of an overriding method which adds optional parameters.
+  [#371](https://github.com/dart-lang/mockito/issues/371)
+  Properly type methods in a generated mock class which comes from a "custom
+  mock" annotation referencing an implicit type. Given a method which references
+  type variables defined on their enclosing class (for example, `T` in
+  `class Foo<T>`), mockito will now correctly reference `T` in generated code.
+  [#422](https://github.com/dart-lang/mockito/issues/422)
+
+## 5.0.9
+
+* Mock classes now implement a type's nested type arguments properly.
+  [#410](https://github.com/dart-lang/mockito/issues/410)
+* Mock classes now implement API from a class's interface(s) (in addition to
+  superclasses and mix ins). Thanks @markgravity.
+  [#404](https://github.com/dart-lang/mockito/pull/404)
+* A MockSpec passed into a `@GenerateMocks` annotation's `customMocks` list can
+  now specify "fallback generators." These are functions which can be used to
+  generate fake responses that mockito's code generation needs in order to
+  return a value for a method with a generic return type. See
+  [NULL_SAFETY_README][] for details.
+
+[NULL_SAFETY_README]: https://github.com/dart-lang/mockito/blob/master/NULL_SAFETY_README.md
+
+## 5.0.8
+
+* Migrate Mockito codegen to null safety.
+* Support mocking methods with typed_data List return types.
+* Support mocking methods with return types declared in private SDK libraries
+  (such as HttpClient and WebSocket, declared in `dart:_http`).
+* Do not generate a fake for a class which is only used as a nullable type in a
+  Future. [#409](https://github.com/dart-lang/mockito/issues/409)
+
 ## 5.0.7
 
 * Properly refer to type parameter bounds with import prefixes.
diff --git a/mockito/NULL_SAFETY_README.md b/mockito/NULL_SAFETY_README.md
index a1402c9..e427a70 100644
--- a/mockito/NULL_SAFETY_README.md
+++ b/mockito/NULL_SAFETY_README.md
@@ -156,6 +156,50 @@
 @GenerateMocks([], customMocks: [MockSpec<Foo>(returnNullOnMissingStub: true)])
 ```
 
+#### Fallback generators
+
+If a class has a method with a type variable as a return type (for example,
+`T get<T>();`), mockito cannot generate code which will internally return valid
+values. For example, given this class and test:
+
+```dart
+abstract class Foo {
+  T m<T>(T a, int b);
+}
+
+@GenerateMocks([], customMocks: [MockSpec<Foo>(as: #MockFoo)])
+void testFoo(Foo foo) {
+  when(   foo.m(7)   ).thenReturn(42);
+       // ^^^^^^^^
+       // mockito needs a valid value which this call to `foo.m` will return.
+}
+```
+
+In order to generate a mock for such a class, pass a `fallbackGenerators`
+argument. Specify a mapping from the method to a top level function with
+_almost_ the same signature as the method. The function must have the same
+return type as the method, and it must have the same positional and named
+parameters as the method, except that each parameter must be made nullable:
+
+```dart
+abstract class Foo {
+  T m<T>(T a, int b);
+}
+
+T mShim<T>(T a, int? b) {
+  if (a is int) return 1;
+  throw 'unknown';
+}
+
+@GenerateMocks([], customMocks: [
+  MockSpec<Foo>(as: #MockFoo, fallbackGenerators: {#m: mShim})
+])
+```
+
+The fallback values will never be returned from a real method call; these are
+not stub return values. They are only used internally by mockito as valid return
+values.
+
 ### Manual mock implementaion
 
 **In the general case, we strongly recommend generating mocks with the above
@@ -247,7 +291,8 @@
 class MockHttpServer extends Mock implements HttpServer {
   @override
   Uri get uri =>
-      super.noSuchMethod(Invocation.getter(#uri), Uri.http('example.org', '/'));
+      super.noSuchMethod(
+          Invocation.getter(#uri), returnValue: Uri.http('example.org', '/'));
 }
 ```
 
diff --git a/mockito/README.md b/mockito/README.md
index 5f6b063..91ea624 100644
--- a/mockito/README.md
+++ b/mockito/README.md
@@ -277,7 +277,8 @@
 
 ## Capturing arguments for further assertions
 
-Use the [`captureAny`], [`captureThat`], and [`captureNamed`] argument matchers:
+Use the [`captureAny`], [`captureThat`], and [`captureAnyNamed`] argument
+matchers:
 
 ```dart
 // Simple capture
diff --git a/mockito/bin/codegen.dart b/mockito/bin/codegen.dart
index 9983624..e216c35 100644
--- a/mockito/bin/codegen.dart
+++ b/mockito/bin/codegen.dart
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// @dart=2.9
-
 import 'package:build/build.dart';
 import 'package:mockito/src/builder.dart' as b;
 
diff --git a/mockito/build.yaml b/mockito/build.yaml
index 2120fc9..baa5dc2 100644
--- a/mockito/build.yaml
+++ b/mockito/build.yaml
@@ -3,6 +3,7 @@
     builders:
       mockito|mockBuilder:
         generate_for:
+          - example/**.dart
           - test/end2end/*.dart
 
 builders:
diff --git a/mockito/example/example.dart b/mockito/example/example.dart
index 9e63a11..21a4336 100644
--- a/mockito/example/example.dart
+++ b/mockito/example/example.dart
@@ -1,8 +1,11 @@
 // ignore_for_file: sdk_version_async_exported_from_core
 // ignore_for_file: unawaited_futures
+import 'package:mockito/annotations.dart';
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
 
+import 'example.mocks.dart';
+
 // Real class
 class Cat {
   String? sound() => 'Meow';
@@ -14,9 +17,6 @@
   int lives = 9;
 }
 
-// Mock class
-class MockCat extends Mock implements Cat {}
-
 // Fake class
 class FakeCat extends Fake implements Cat {
   @override
@@ -26,6 +26,11 @@
   }
 }
 
+@GenerateMocks([
+  Cat
+], customMocks: [
+  MockSpec<Cat>(as: #MockCatRelaxed, returnNullOnMissingStub: true),
+])
 void main() {
   late Cat cat;
 
@@ -35,6 +40,9 @@
   });
 
   test("Let's verify some behaviour!", () {
+    // Stub a method before interacting with it.
+    when(cat.sound()).thenReturn('Meow');
+
     // Interact with the mock object.
     cat.sound();
 
@@ -43,6 +51,12 @@
   });
 
   test('How about some stubbing?', () {
+    try {
+      cat.sound();
+    } on MissingStubError {
+      // Unstubbed methods throw MissingStubError.
+    }
+
     // Unstubbed methods return null.
     expect(cat.sound(), null);
 
@@ -118,6 +132,8 @@
   });
 
   test('Verifying exact number of invocations / at least x / never', () {
+    when(cat.sound()).thenReturn('Meow');
+
     cat.sound();
     cat.sound();
     // Exact number of invocations
@@ -134,6 +150,9 @@
   });
 
   test('Verification in order', () {
+    when(cat.sound()).thenReturn('Meow');
+    when(cat.eatFood(any)).thenReturn(true);
+
     cat.eatFood('Milk');
     cat.sound();
     cat.eatFood('Fish');
@@ -145,12 +164,16 @@
   });
 
   test('Finding redundant invocations', () {
+    when(cat.sound()).thenReturn('Meow');
+
     cat.sound();
     verify(cat.sound());
     verifyNoMoreInteractions(cat);
   });
 
   test('Capturing arguments for further assertions', () {
+    when(cat.eatFood(any)).thenReturn(true);
+
     // Simple capture:
     cat.eatFood('Fish');
     expect(verify(cat.eatFood(captureAny)).captured.single, 'Fish');
@@ -168,6 +191,8 @@
   });
 
   test('Waiting for an interaction', () async {
+    when(cat.eatFood(any)).thenReturn(true);
+
     Future<void> chewHelper(Cat cat) {
       return cat.chew();
     }
@@ -188,4 +213,18 @@
     cat.eatFood('Milk'); // Prints 'Fake eat Milk'.
     expect(() => cat.sleep(), throwsUnimplementedError);
   });
+
+  test('Relaxed mock class', () {
+    // Create a new mock Cat at runtime.
+    var cat = MockCatRelaxed();
+
+    // You can call it without stubbing.
+    cat.sleep();
+
+    // Returns null unless you stub it.
+    expect(cat.sound(), null);
+    expect(cat.eatFood('Milk'), null);
+
+    verify(cat.sleep());
+  });
 }
diff --git a/mockito/example/iss/iss_test.dart b/mockito/example/iss/iss_test.dart
index dc4f3cd..2ec901a 100644
--- a/mockito/example/iss/iss_test.dart
+++ b/mockito/example/iss/iss_test.dart
@@ -14,14 +14,14 @@
 
 import 'dart:math';
 
+import 'package:mockito/annotations.dart';
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
 
 import 'iss.dart';
+import 'iss_test.mocks.dart';
 
-// The Mock class uses noSuchMethod to catch all method invocations.
-class MockIssLocator extends Mock implements IssLocator {}
-
+@GenerateMocks([IssLocator])
 void main() {
   // Given two predefined points on earth,
   // verify the calculated distance between them.
diff --git a/mockito/lib/annotations.dart b/mockito/lib/annotations.dart
index 53ecd45..d68210c 100644
--- a/mockito/lib/annotations.dart
+++ b/mockito/lib/annotations.dart
@@ -66,13 +66,28 @@
 /// directs Mockito to generate two mocks:
 /// `class MockFoo<T> extends Mocks implements Foo<T>` and
 /// `class MockFooOfInt extends Mock implements Foo<int>`.
-// TODO(srawlins): Document this in NULL_SAFETY_README.md.
-// TODO(srawlins): Add 'mixingIn'.
 class MockSpec<T> {
   final Symbol? mockName;
 
   final bool returnNullOnMissingStub;
 
-  const MockSpec({Symbol? as, this.returnNullOnMissingStub = false})
-      : mockName = as;
+  final Map<Symbol, Function> fallbackGenerators;
+
+  /// Constructs a custom mock specification.
+  ///
+  /// Specify a custom name with the [as] parameter.
+  ///
+  /// If [returnNullOnMissingStub] is true, the mock class will return `null`
+  /// when a method is called and no stub could be found. This may result in a
+  /// runtime error, if the return type of the method is non-nullable.
+  ///
+  /// Each entry in [fallbackGenerators] specifies a mapping from a method name
+  /// to a function, with the same signature as the method. This function will
+  /// be used to generate fallback values when a non-null value needs to be
+  /// returned when stubbing or verifying.
+  const MockSpec({
+    Symbol? as,
+    this.returnNullOnMissingStub = false,
+    this.fallbackGenerators = const {},
+  }) : mockName = as;
 }
diff --git a/mockito/lib/src/builder.dart b/mockito/lib/src/builder.dart
index bc4480d..9d29c31 100644
--- a/mockito/lib/src/builder.dart
+++ b/mockito/lib/src/builder.dart
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// @dart=2.9
+import 'dart:collection';
 
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -28,8 +28,8 @@
 // provides a [refer] function and a [referBasic] function. The former requires
 // a URL to be passed.
 import 'package:code_builder/code_builder.dart' hide refer;
+import 'package:collection/collection.dart';
 import 'package:dart_style/dart_style.dart';
-import 'package:meta/meta.dart';
 import 'package:mockito/src/version.dart';
 import 'package:path/path.dart' as p;
 import 'package:source_gen/source_gen.dart';
@@ -59,8 +59,8 @@
     final mockTargetGatherer = _MockTargetGatherer(entryLib);
 
     var entryAssetId = await buildStep.resolver.assetIdForElement(entryLib);
-    final assetUris = await _resolveAssetUris(
-        buildStep.resolver, mockTargetGatherer._mockTargets, entryAssetId.path);
+    final assetUris = await _resolveAssetUris(buildStep.resolver,
+        mockTargetGatherer._mockTargets, entryAssetId.path, entryLib);
 
     final mockLibraryInfo = _MockLibraryInfo(mockTargetGatherer._mockTargets,
         assetUris: assetUris, entryLib: entryLib);
@@ -74,13 +74,18 @@
     final mockLibrary = Library((b) {
       // These comments are added after import directives; leading newlines
       // are necessary.
+      b.body.add(
+          Code('\n\n// ignore_for_file: avoid_redundant_argument_values\n'));
       // We don't properly prefix imported class names in doc comments.
-      b.body.add(Code('\n\n// ignore_for_file: comment_references\n'));
+      b.body.add(Code('// ignore_for_file: comment_references\n'));
+      // `Mock.noSuchMethod` is `@visibleForTesting`, but the generated code is
+      // not always in a test directory; the Mockito `example/iss` tests, for
+      // example.
+      b.body.add(Code(
+          '// ignore_for_file: invalid_use_of_visible_for_testing_member\n'));
+      b.body.add(Code('// ignore_for_file: prefer_const_constructors\n'));
       // The code_builder `asA` API unconditionally adds defensive parentheses.
       b.body.add(Code('// ignore_for_file: unnecessary_parenthesis\n\n'));
-      b.body.add(Code('// ignore_for_file: prefer_const_constructors\n\n'));
-      b.body
-          .add(Code('// ignore_for_file: avoid_redundant_argument_values\n\n'));
       b.body.addAll(mockLibraryInfo.fakeClasses);
       b.body.addAll(mockLibraryInfo.mockClasses);
     });
@@ -99,10 +104,14 @@
     await buildStep.writeAsString(mockLibraryAsset, mockLibraryContent);
   }
 
-  Future<Map<Element, String>> _resolveAssetUris(Resolver resolver,
-      List<_MockTarget> mockTargets, String entryAssetPath) async {
+  Future<Map<Element, String>> _resolveAssetUris(
+      Resolver resolver,
+      List<_MockTarget> mockTargets,
+      String entryAssetPath,
+      LibraryElement entryLib) async {
     final typeVisitor = _TypeVisitor();
     final seenTypes = <analyzer.InterfaceType>{};
+    final librariesWithTypes = <LibraryElement>{};
 
     void addTypesFrom(analyzer.InterfaceType type) {
       // Prevent infinite recursion.
@@ -110,9 +119,10 @@
         return;
       }
       seenTypes.add(type);
+      librariesWithTypes.add(type.element.library);
       type.element.accept(typeVisitor);
       // For a type like `Foo<Bar>`, add the `Bar`.
-      (type.typeArguments ?? [])
+      type.typeArguments
           .whereType<analyzer.InterfaceType>()
           .forEach(addTypesFrom);
       // For a type like `Foo extends Bar<Baz>`, add the `Baz`.
@@ -127,14 +137,28 @@
 
     final typeUris = <Element, String>{};
 
-    for (var element in typeVisitor._elements) {
-      if (element.library.isInSdk) {
-        typeUris[element] = element.library.source.uri.toString();
+    final elements = [
+      // Types which may be referenced.
+      ...typeVisitor._elements,
+      // Fallback generator functions which may be referenced.
+      for (final mockTarget in mockTargets)
+        ...mockTarget.fallbackGenerators.values,
+    ];
+
+    for (final element in elements) {
+      final elementLibrary = element.library!;
+      if (elementLibrary.isInSdk) {
+        if (elementLibrary.name!.startsWith('dart._')) {
+          typeUris[element] = _findPublicExportOf(
+              Queue.of(librariesWithTypes), elementLibrary)!;
+        } else {
+          typeUris[element] = elementLibrary.source.uri.toString();
+        }
         continue;
       }
 
       try {
-        var typeAssetId = await resolver.assetIdForElement(element.library);
+        final typeAssetId = await resolver.assetIdForElement(elementLibrary);
 
         if (typeAssetId.path.startsWith('lib/')) {
           typeUris[element] = typeAssetId.uri.toString();
@@ -144,7 +168,7 @@
         }
       } on UnresolvableAssetException {
         // Asset may be in a summary.
-        typeUris[element] = element.library.source.uri.toString();
+        typeUris[element] = elementLibrary.source.uri.toString();
         continue;
       }
     }
@@ -152,6 +176,26 @@
     return typeUris;
   }
 
+  /// Returns the String import path of the correct public library which
+  /// exports [privateLibrary], selecting from the imports of [inputLibraries].
+  static String? _findPublicExportOf(
+      Queue<LibraryElement> inputLibraries, LibraryElement privateLibrary) {
+    final libraries = Queue.of([
+      for (final library in inputLibraries) ...library.importedLibraries,
+    ]);
+
+    while (libraries.isNotEmpty) {
+      final library = libraries.removeFirst();
+      if (library.exportedLibraries.contains(privateLibrary)) {
+        return library.source.uri.toString();
+      }
+      // A library may provide [privateLibrary] by exporting a library which
+      // provides it (directly or via further exporting).
+      libraries.addAll(library.exportedLibraries);
+    }
+    return null;
+  }
+
   @override
   final buildExtensions = const {
     '.dart': ['.mocks.dart']
@@ -185,7 +229,7 @@
   void visitParameterElement(ParameterElement element) {
     _addType(element.type);
     if (element.hasDefaultValue) {
-      _addTypesFromConstant(element.computeConstantValue());
+      _addTypesFromConstant(element.computeConstantValue()!);
     }
     super.visitParameterElement(element);
   }
@@ -197,15 +241,15 @@
   }
 
   /// Adds [type] to the collected [_elements].
-  void _addType(analyzer.DartType type) {
+  void _addType(analyzer.DartType? type) {
     if (type == null) return;
 
     if (type is analyzer.InterfaceType) {
       final alreadyVisitedElement = _elements.contains(type.element);
       _elements.add(type.element);
-      (type.typeArguments ?? []).forEach(_addType);
+      type.typeArguments.forEach(_addType);
       if (!alreadyVisitedElement) {
-        (type.element.typeParameters ?? []).forEach(visitTypeParameterElement);
+        type.element.typeParameters.forEach(visitTypeParameterElement);
       }
     } else if (type is analyzer.FunctionType) {
       _addType(type.returnType);
@@ -248,11 +292,11 @@
       }
     } else if (constant.isMap) {
       for (var pair in constant.mapValue.entries) {
-        _addTypesFromConstant(pair.key);
-        _addTypesFromConstant(pair.value);
+        _addTypesFromConstant(pair.key!);
+        _addTypesFromConstant(pair.value!);
       }
     } else if (object.toFunctionValue() != null) {
-      _elements.add(object.toFunctionValue());
+      _elements.add(object.toFunctionValue()!);
     } else {
       // If [constant] is not null, a literal, or a type, then it must be an
       // object constructed with `const`. Revive it.
@@ -277,7 +321,14 @@
 
   final bool returnNullOnMissingStub;
 
-  _MockTarget(this.classType, this.mockName, {this.returnNullOnMissingStub});
+  final Map<String, ExecutableElement> fallbackGenerators;
+
+  _MockTarget(
+    this.classType,
+    this.mockName, {
+    required this.returnNullOnMissingStub,
+    required this.fallbackGenerators,
+  });
 
   ClassElement get classElement => classType.element;
 }
@@ -305,7 +356,7 @@
       for (final annotation in element.metadata) {
         if (annotation == null) continue;
         if (annotation.element is! ConstructorElement) continue;
-        final annotationClass = annotation.element.enclosingElement.name;
+        final annotationClass = annotation.element!.enclosingElement!.name;
         // TODO(srawlins): check library as well.
         if (annotationClass == 'GenerateMocks') {
           mockTargets
@@ -319,15 +370,15 @@
 
   static Iterable<_MockTarget> _mockTargetsFromGenerateMocks(
       ElementAnnotation annotation, LibraryElement entryLib) {
-    final generateMocksValue = annotation.computeConstantValue();
-    final classesField = generateMocksValue.getField('classes');
+    final generateMocksValue = annotation.computeConstantValue()!;
+    final classesField = generateMocksValue.getField('classes')!;
     if (classesField.isNull) {
       throw InvalidMockitoAnnotationException(
           'The GenerateMocks "classes" argument is missing, includes an '
           'unknown type, or includes an extension');
     }
     final mockTargets = <_MockTarget>[];
-    for (var objectToMock in classesField.toListValue()) {
+    for (var objectToMock in classesField.toListValue()!) {
       final typeToMock = objectToMock.toTypeValue();
       if (typeToMock == null) {
         throw InvalidMockitoAnnotationException(
@@ -338,18 +389,20 @@
             'Mockito cannot mock `dynamic`');
       }
       final type = _determineDartType(typeToMock, entryLib.typeProvider);
-      // [type] is `Foo<dynamic>` for generic classes. Switch to declaration,
-      // which will yield `Foo<T>`.
+      // For a generic class like `Foo<T>` or `Foo<T extends num>`, a type
+      // literal (`Foo`) cannot express type arguments. The type argument(s) on
+      // `type` have been instantiated to bounds here. Switch to the
+      // declaration, which will be an uninstantiated type.
       final declarationType =
           (type.element.declaration as ClassElement).thisType;
       final mockName = 'Mock${declarationType.element.name}';
       mockTargets.add(_MockTarget(declarationType, mockName,
-          returnNullOnMissingStub: false));
+          returnNullOnMissingStub: false, fallbackGenerators: {}));
     }
     final customMocksField = generateMocksValue.getField('customMocks');
     if (customMocksField != null && !customMocksField.isNull) {
-      for (var mockSpec in customMocksField.toListValue()) {
-        final mockSpecType = mockSpec.type;
+      for (var mockSpec in customMocksField.toListValue()!) {
+        final mockSpecType = mockSpec.type as analyzer.InterfaceType;
         assert(mockSpecType.typeArguments.length == 1);
         final typeToMock = mockSpecType.typeArguments.single;
         if (typeToMock.isDynamic) {
@@ -358,17 +411,48 @@
               'arguments on MockSpec(), in @GenerateMocks.');
         }
         var type = _determineDartType(typeToMock, entryLib.typeProvider);
-        final mockName = mockSpec.getField('mockName').toSymbolValue() ??
+
+        if (!type.hasExplicitTypeArguments) {
+          // We assume the type was given without explicit type arguments. In
+          // this case the type argument(s) on `type` have been instantiated to
+          // bounds. Switch to the declaration, which will be an uninstantiated
+          // type.
+          type = (type.element.declaration as ClassElement).thisType;
+        }
+        final mockName = mockSpec.getField('mockName')!.toSymbolValue() ??
             'Mock${type.element.name}';
         final returnNullOnMissingStub =
-            mockSpec.getField('returnNullOnMissingStub').toBoolValue();
+            mockSpec.getField('returnNullOnMissingStub')!.toBoolValue()!;
+        final fallbackGeneratorObjects =
+            mockSpec.getField('fallbackGenerators')!.toMapValue()!;
         mockTargets.add(_MockTarget(type, mockName,
-            returnNullOnMissingStub: returnNullOnMissingStub));
+            returnNullOnMissingStub: returnNullOnMissingStub,
+            fallbackGenerators:
+                _extractFallbackGenerators(fallbackGeneratorObjects)));
       }
     }
     return mockTargets;
   }
 
+  static Map<String, ExecutableElement> _extractFallbackGenerators(
+      Map<DartObject?, DartObject?> objects) {
+    final fallbackGenerators = <String, ExecutableElement>{};
+    objects.forEach((methodName, generator) {
+      if (methodName == null) {
+        throw InvalidMockitoAnnotationException(
+            'Unexpected null key in fallbackGenerators: $objects');
+      }
+      if (generator == null) {
+        throw InvalidMockitoAnnotationException(
+            'Unexpected null value in fallbackGenerators for key '
+            '"$methodName"');
+      }
+      fallbackGenerators[methodName.toSymbolValue()!] =
+          generator.toFunctionValue()!;
+    });
+    return fallbackGenerators;
+  }
+
   /// Map the values passed to the GenerateMocks annotation to the classes which
   /// they represent.
   ///
@@ -383,7 +467,7 @@
         throw InvalidMockitoAnnotationException(
             'Mockito cannot mock an enum: ${elementToMock.displayName}');
       }
-      if (typeProvider.nonSubtypableClasses.contains(elementToMock)) {
+      if (typeProvider.isNonSubtypableClass(elementToMock)) {
         throw InvalidMockitoAnnotationException(
             'Mockito cannot mock a non-subtypable type: '
             '${elementToMock.displayName}. It is illegal to subtype this '
@@ -418,29 +502,30 @@
   }
 
   void _checkClassesToMockAreValid() {
-    var classesInEntryLib =
+    final classesInEntryLib =
         _entryLib.topLevelElements.whereType<ClassElement>();
-    var classNamesToMock = <String, ClassElement>{};
-    var uniqueNameSuggestion =
+    final classNamesToMock = <String, _MockTarget>{};
+    final uniqueNameSuggestion =
         "use the 'customMocks' argument in @GenerateMocks to specify a unique "
         'name';
     for (final mockTarget in _mockTargets) {
-      var name = mockTarget.mockName;
+      final name = mockTarget.mockName;
       if (classNamesToMock.containsKey(name)) {
-        var firstSource = classNamesToMock[name].source.fullName;
-        var secondSource = mockTarget.classElement.source.fullName;
+        final firstClass = classNamesToMock[name]!.classElement;
+        final firstSource = firstClass.source.fullName;
+        final secondSource = mockTarget.classElement.source.fullName;
         throw InvalidMockitoAnnotationException(
             'Mockito cannot generate two mocks with the same name: $name (for '
-            '${classNamesToMock[name].name} declared in $firstSource, and for '
+            '${firstClass.name} declared in $firstSource, and for '
             '${mockTarget.classElement.name} declared in $secondSource); '
             '$uniqueNameSuggestion.');
       }
-      classNamesToMock[name] = mockTarget.classElement;
+      classNamesToMock[name] = mockTarget;
     }
 
-    classNamesToMock.forEach((name, element) {
-      var conflictingClass = classesInEntryLib.firstWhere((c) => c.name == name,
-          orElse: () => null);
+    classNamesToMock.forEach((name, mockTarget) {
+      var conflictingClass =
+          classesInEntryLib.firstWhereOrNull((c) => c.name == name);
       if (conflictingClass != null) {
         throw InvalidMockitoAnnotationException(
             'Mockito cannot generate a mock with a name which conflicts with '
@@ -448,11 +533,11 @@
             '$uniqueNameSuggestion.');
       }
 
-      var preexistingMock = classesInEntryLib.firstWhere(
-          (c) =>
-              c.interfaces.map((type) => type.element).contains(element) &&
-              _isMockClass(c.supertype),
-          orElse: () => null);
+      var preexistingMock = classesInEntryLib.firstWhereOrNull((c) =>
+          c.interfaces
+              .map((type) => type.element)
+              .contains(mockTarget.classElement) &&
+          _isMockClass(c.supertype!));
       if (preexistingMock != null) {
         throw InvalidMockitoAnnotationException(
             'The GenerateMocks annotation contains a class which appears to '
@@ -460,7 +545,7 @@
             '$uniqueNameSuggestion.');
       }
 
-      _checkMethodsToStubAreValid(element);
+      _checkMethodsToStubAreValid(mockTarget);
     });
   }
 
@@ -470,13 +555,17 @@
   /// A method is not valid for stubbing if:
   /// - It has a private type anywhere in its signature; Mockito cannot override
   ///   such a method.
-  /// - It has a non-nullable type variable return type, for example `T m<T>()`.
-  ///   Mockito cannot generate dummy return values for unknown types.
-  void _checkMethodsToStubAreValid(ClassElement classElement) {
-    var className = classElement.name;
-    var unstubbableErrorMessages = classElement.methods
+  /// - It has a non-nullable type variable return type, for example `T m<T>()`,
+  ///   and no corresponding dummy generator. Mockito cannot generate its own
+  ///   dummy return values for unknown types.
+  void _checkMethodsToStubAreValid(_MockTarget mockTarget) {
+    final classElement = mockTarget.classElement;
+    final className = classElement.name;
+    final unstubbableErrorMessages = classElement.methods
         .where((m) => !m.isPrivate && !m.isStatic)
-        .expand((m) => _checkFunction(m.type, m))
+        .expand((m) => _checkFunction(m.type, m,
+            hasDummyGenerator:
+                mockTarget.fallbackGenerators.containsKey(m.name)))
         .toList();
 
     if (unstubbableErrorMessages.isNotEmpty) {
@@ -496,11 +585,15 @@
   /// - bounds of type parameters
   /// - type arguments
   List<String> _checkFunction(
-      analyzer.FunctionType function, Element enclosingElement) {
-    var errorMessages = <String>[];
-    var returnType = function.returnType;
+    analyzer.FunctionType function,
+    Element enclosingElement, {
+    bool isParameter = false,
+    bool hasDummyGenerator = false,
+  }) {
+    final errorMessages = <String>[];
+    final returnType = function.returnType;
     if (returnType is analyzer.InterfaceType) {
-      if (returnType.element?.isPrivate ?? false) {
+      if (returnType.element.isPrivate) {
         errorMessages.add(
             '${enclosingElement.fullName} features a private return type, and '
             'cannot be stubbed.');
@@ -510,11 +603,13 @@
     } else if (returnType is analyzer.FunctionType) {
       errorMessages.addAll(_checkFunction(returnType, enclosingElement));
     } else if (returnType is analyzer.TypeParameterType) {
-      if (function.returnType is analyzer.TypeParameterType &&
-          _entryLib.typeSystem.isPotentiallyNonNullable(function.returnType)) {
+      if (!isParameter &&
+          !hasDummyGenerator &&
+          _entryLib.typeSystem.isPotentiallyNonNullable(returnType)) {
         errorMessages
             .add('${enclosingElement.fullName} features a non-nullable unknown '
-                'return type, and cannot be stubbed.');
+                'return type, and cannot be stubbed without a dummy generator '
+                'specified on the MockSpec.');
       }
     }
 
@@ -522,7 +617,7 @@
       var parameterType = parameter.type;
       if (parameterType is analyzer.InterfaceType) {
         var parameterTypeElement = parameterType.element;
-        if (parameterTypeElement?.isPrivate ?? false) {
+        if (parameterTypeElement.isPrivate) {
           // Technically, we can expand the type in the mock to something like
           // `Object?`. However, until there is a decent use case, we will not
           // generate such a mock.
@@ -533,7 +628,8 @@
         errorMessages.addAll(
             _checkTypeArguments(parameterType.typeArguments, enclosingElement));
       } else if (parameterType is analyzer.FunctionType) {
-        errorMessages.addAll(_checkFunction(parameterType, enclosingElement));
+        errorMessages.addAll(
+            _checkFunction(parameterType, enclosingElement, isParameter: true));
       }
     }
 
@@ -558,7 +654,7 @@
       var typeParameter = element.bound;
       if (typeParameter == null) continue;
       if (typeParameter is analyzer.InterfaceType) {
-        if (typeParameter.element?.isPrivate ?? false) {
+        if (typeParameter.element.isPrivate) {
           errorMessages.add(
               '${enclosingElement.fullName} features a private type parameter '
               'bound, and cannot be stubbed.');
@@ -578,7 +674,7 @@
     var errorMessages = <String>[];
     for (var typeArgument in typeArguments) {
       if (typeArgument is analyzer.InterfaceType) {
-        if (typeArgument.element?.isPrivate ?? false) {
+        if (typeArgument.element.isPrivate) {
           errorMessages.add(
               '${enclosingElement.fullName} features a private type argument, '
               'and cannot be stubbed.');
@@ -625,50 +721,29 @@
   /// Asset-resolving while building the mock library.
   final Map<Element, String> assetUris;
 
+  /// A mapping of any fallback generators specified for the classes-to-mock.
+  ///
+  /// Each value is another mapping from method names to the generator
+  /// function elements.
+  final Map<ClassElement, Map<String, ExecutableElement>> fallbackGenerators;
+
   /// Build mock classes for [mockTargets].
-  _MockLibraryInfo(Iterable<_MockTarget> mockTargets,
-      {this.assetUris, LibraryElement entryLib})
-      : sourceLibIsNonNullable = entryLib.isNonNullableByDefault,
+  _MockLibraryInfo(
+    Iterable<_MockTarget> mockTargets, {
+    required this.assetUris,
+    required LibraryElement entryLib,
+  })  : sourceLibIsNonNullable = entryLib.isNonNullableByDefault,
         typeProvider = entryLib.typeProvider,
-        typeSystem = entryLib.typeSystem {
+        typeSystem = entryLib.typeSystem,
+        fallbackGenerators = {
+          for (final mockTarget in mockTargets)
+            mockTarget.classElement: mockTarget.fallbackGenerators
+        } {
     for (final mockTarget in mockTargets) {
       mockClasses.add(_buildMockClass(mockTarget));
     }
   }
 
-  bool _hasExplicitTypeArguments(analyzer.InterfaceType type) {
-    if (type.typeArguments == null) return false;
-
-    // If it appears that one type argument was given, then they all were. This
-    // returns the wrong result when the type arguments given are all `dynamic`,
-    // or are each equal to the bound of the corresponding type parameter. There
-    // may not be a way to get around this.
-    for (var i = 0; i < type.typeArguments.length; i++) {
-      var typeArgument = type.typeArguments[i];
-      // If [typeArgument] is a type parameter, this indicates that no type
-      // arguments were passed. This likely came from the 'classes' argument of
-      // GenerateMocks, and [type] is the declaration type (`Foo<T>` vs
-      // `Foo<dynamic>`).
-      if (typeArgument is analyzer.TypeParameterType) return false;
-
-      // If [type] was given to @GenerateMocks as a Type, and no explicit type
-      // argument is given, [typeArgument] is `dynamic` (_not_ the bound, as one
-      // might think). We determine that an explicit type argument was given if
-      // it is not `dynamic`.
-      if (typeArgument.isDynamic) continue;
-
-      // If, on the other hand, [type] was given to @GenerateMock as a type
-      // argument to `Of()`, and no type argument is given, [typeArgument] is
-      // the bound of the corresponding type paramter (dynamic or otherwise). We
-      // determine that an explicit type argument was given if [typeArgument] is
-      // not [bound].
-      var bound =
-          type.element.typeParameters[i].bound ?? typeProvider.dynamicType;
-      if (!typeArgument.isDynamic && typeArgument != bound) return true;
-    }
-    return false;
-  }
-
   Class _buildMockClass(_MockTarget mockTarget) {
     final typeToMock = mockTarget.classType;
     final classToMock = mockTarget.classElement;
@@ -692,14 +767,16 @@
       // parameter with same type variables, and a mirrored type argument for
       // the "implements" clause.
       var typeArguments = <Reference>[];
-      if (_hasExplicitTypeArguments(typeToMock)) {
+      if (typeToMock.hasExplicitTypeArguments) {
         // [typeToMock] is a reference to a type with type arguments (for
         // example: `Foo<int>`). Generate a non-generic mock class which
         // implements the mock target with said type arguments. For example:
         // `class MockFoo extends Mock implements Foo<int> {}`
         for (var typeArgument in typeToMock.typeArguments) {
           typeArguments.add(referImported(
-              typeArgument.element.name, _typeImport(typeArgument.element)));
+              typeArgument.getDisplayString(
+                  withNullability: sourceLibIsNonNullable),
+              _typeImport(typeArgument.element)));
         }
       } else if (classToMock.typeParameters != null) {
         // [typeToMock] is a simple reference to a generic type (for example:
@@ -748,6 +825,10 @@
       if (overriddenFields.contains(accessor.name)) {
         continue;
       }
+      if (accessor.name == 'hashCode') {
+        // Never override this getter; user code cannot narrow the return type.
+        continue;
+      }
       overriddenFields.add(accessor.name);
       if (accessor.isGetter && _returnTypeIsNonNullable(accessor)) {
         yield Method((mBuilder) => _buildOverridingGetter(mBuilder, accessor));
@@ -761,6 +842,11 @@
         yield* fieldOverrides(mixin, overriddenFields);
       }
     }
+    if (type.interfaces != null) {
+      for (var interface in type.interfaces) {
+        yield* fieldOverrides(interface, overriddenFields);
+      }
+    }
     var superclass = type.superclass;
     if (superclass != null && !superclass.isDartCoreObject) {
       yield* fieldOverrides(superclass, overriddenFields);
@@ -790,11 +876,15 @@
       if (methodName == 'noSuchMethod') {
         continue;
       }
+      if (methodName == '==') {
+        // Never override this operator; user code cannot add parameters or
+        // narrow the return type.
+        continue;
+      }
       if (_returnTypeIsNonNullable(method) ||
           _hasNonNullableParameter(method) ||
           _needsOverrideForVoidStub(method)) {
-        yield Method((mBuilder) => _buildOverridingMethod(mBuilder, method,
-            className: type.getDisplayString(withNullability: true)));
+        yield Method((mBuilder) => _buildOverridingMethod(mBuilder, method));
       }
     }
     if (type.mixins != null) {
@@ -802,6 +892,11 @@
         yield* methodOverrides(mixin, overriddenMethods);
       }
     }
+    if (type.interfaces != null) {
+      for (var interface in type.interfaces) {
+        yield* methodOverrides(interface, overriddenMethods);
+      }
+    }
     var superclass = type.superclass;
     if (superclass != null && !superclass.isDartCoreObject) {
       yield* methodOverrides(superclass, overriddenMethods);
@@ -841,8 +936,7 @@
   ///
   /// This new method just calls `super.noSuchMethod`, optionally passing a
   /// return value for methods with a non-nullable return type.
-  void _buildOverridingMethod(MethodBuilder builder, MethodElement method,
-      {@required String className}) {
+  void _buildOverridingMethod(MethodBuilder builder, MethodElement method) {
     var name = method.displayName;
     if (method.isOperator) name = 'operator$name';
     builder
@@ -876,20 +970,33 @@
       }
     }
 
+    if (name == 'toString') {
+      // We cannot call `super.noSuchMethod` here; we must use [Mock]'s
+      // implementation.
+      builder.body = refer('super').property('toString').call([]).code;
+      return;
+    }
+
     final invocation = refer('Invocation').property('method').call([
       refer('#${method.displayName}'),
       literalList(invocationPositionalArgs),
       if (invocationNamedArgs.isNotEmpty) literalMap(invocationNamedArgs),
     ]);
 
-    Expression returnValueForMissingStub;
+    Expression? returnValueForMissingStub;
     if (method.returnType.isVoid) {
       returnValueForMissingStub = refer('null');
     } else if (method.returnType.isFutureOfVoid) {
       returnValueForMissingStub = _futureReference().property('value').call([]);
     }
+    final class_ = method.enclosingElement;
+    final fallbackGenerator = fallbackGenerators.containsKey(class_)
+        ? fallbackGenerators[class_]![method.name]
+        : null;
     final namedArgs = {
-      if (_returnTypeIsNonNullable(method))
+      if (fallbackGenerator != null)
+        'returnValue': _fallbackGeneratorCode(method, fallbackGenerator)
+      else if (_returnTypeIsNonNullable(method))
         'returnValue': _dummyValue(method.returnType),
       if (returnValueForMissingStub != null)
         'returnValueForMissingStub': returnValueForMissingStub,
@@ -902,9 +1009,24 @@
           superNoSuchMethod.asA(_typeReference(method.returnType));
     }
 
-    builder
-      ..lambda = true
-      ..body = superNoSuchMethod.code;
+    builder.body = superNoSuchMethod.code;
+  }
+
+  Expression _fallbackGeneratorCode(
+      MethodElement method, ExecutableElement function) {
+    final positionalArguments = <Expression>[];
+    final namedArguments = <String, Expression>{};
+    for (final parameter in method.parameters) {
+      if (parameter.isPositional) {
+        positionalArguments.add(refer(parameter.name));
+      } else if (parameter.isNamed) {
+        namedArguments[parameter.name] = refer(parameter.name);
+      }
+    }
+    final functionReference =
+        referImported(function.name, _typeImport(function));
+    return functionReference.call(positionalArguments, namedArguments,
+        [for (var t in method.typeParameters) refer(t.name)]);
   }
 
   Expression _dummyValue(analyzer.DartType type) {
@@ -917,39 +1039,40 @@
       return literalNull;
     }
 
-    var interfaceType = type as analyzer.InterfaceType;
-    var typeArguments = interfaceType.typeArguments;
-    if (interfaceType.isDartCoreBool) {
+    var typeArguments = type.typeArguments;
+    if (type.isDartCoreBool) {
       return literalFalse;
-    } else if (interfaceType.isDartCoreDouble) {
+    } else if (type.isDartCoreDouble) {
       return literalNum(0.0);
-    } else if (interfaceType.isDartAsyncFuture ||
-        interfaceType.isDartAsyncFutureOr) {
-      var typeArgument = typeArguments.first;
+    } else if (type.isDartAsyncFuture || type.isDartAsyncFutureOr) {
+      final typeArgument = typeArguments.first;
+      final futureValueArguments =
+          typeSystem.isPotentiallyNonNullable(typeArgument)
+              ? [_dummyValue(typeArgument)]
+              : <Expression>[];
       return _futureReference(_typeReference(typeArgument))
           .property('value')
-          .call([_dummyValue(typeArgument)]);
-    } else if (interfaceType.isDartCoreInt) {
+          .call(futureValueArguments);
+    } else if (type.isDartCoreInt) {
       return literalNum(0);
-    } else if (interfaceType.isDartCoreIterable) {
+    } else if (type.isDartCoreIterable) {
       return literalList([]);
-    } else if (interfaceType.isDartCoreList) {
+    } else if (type.isDartCoreList) {
       assert(typeArguments.length == 1);
       var elementType = _typeReference(typeArguments[0]);
       return literalList([], elementType);
-    } else if (interfaceType.isDartCoreMap) {
+    } else if (type.isDartCoreMap) {
       assert(typeArguments.length == 2);
       var keyType = _typeReference(typeArguments[0]);
       var valueType = _typeReference(typeArguments[1]);
       return literalMap({}, keyType, valueType);
-    } else if (interfaceType.isDartCoreNum) {
+    } else if (type.isDartCoreNum) {
       return literalNum(0);
-    } else if (interfaceType.isDartCoreSet) {
+    } else if (type.isDartCoreSet) {
       assert(typeArguments.length == 1);
       var elementType = _typeReference(typeArguments[0]);
       return literalSet({}, elementType);
-    } else if (interfaceType.element?.declaration ==
-        typeProvider.streamElement) {
+    } else if (type.element.declaration == typeProvider.streamElement) {
       assert(typeArguments.length == 1);
       var elementType = _typeReference(typeArguments[0]);
       return TypeReference((b) {
@@ -957,18 +1080,26 @@
           ..symbol = 'Stream'
           ..types.add(elementType);
       }).property('empty').call([]);
-    } else if (interfaceType.isDartCoreString) {
+    } else if (type.isDartCoreString) {
       return literalString('');
+    } else if (type.isDartTypedDataList) {
+      // These "List" types from dart:typed_data are "non-subtypeable", but they
+      // have predicatble constructors; each has an unnamed constructor which
+      // takes a single int argument.
+      return referImported(type.displayName, 'dart:typed_data')
+          .call([literalNum(0)]);
+      // TODO(srawlins): Do other types from typed_data have a "non-subtypeable"
+      // restriction as well?
     }
 
     // This class is unknown; we must likely generate a fake class, and return
     // an instance here.
-    return _dummyValueImplementing(type as analyzer.InterfaceType);
+    return _dummyValueImplementing(type);
   }
 
   /// Returns a reference to [Future], optionally with a type argument for the
   /// value of the Future.
-  TypeReference _futureReference([Reference valueType]) => TypeReference((b) {
+  TypeReference _futureReference([Reference? valueType]) => TypeReference((b) {
         b.symbol = 'Future';
         if (valueType != null) {
           b.types.add(valueType);
@@ -1005,11 +1136,7 @@
   }
 
   Expression _dummyValueImplementing(analyzer.InterfaceType dartType) {
-    // For each type parameter on [dartType], the Mock class needs a type
-    // parameter with same type variables, and a mirrored type argument for the
-    // "implements" clause.
-    var typeParameters = <Reference>[];
-    var elementToFake = dartType.element;
+    final elementToFake = dartType.element;
     if (elementToFake.isEnum) {
       return _typeReference(dartType).property(
           elementToFake.fields.firstWhere((f) => f.isEnumConstant).name);
@@ -1017,29 +1144,12 @@
       // There is a potential for these names to collide. If one mock class
       // requires a fake for a certain Foo, and another mock class requires a
       // fake for a different Foo, they will collide.
-      var fakeName = '_Fake${elementToFake.name}';
+      final fakeName = '_Fake${elementToFake.name}';
       // Only make one fake class for each class that needs to be faked.
       if (!fakedClassElements.contains(elementToFake)) {
-        fakeClasses.add(Class((cBuilder) {
-          cBuilder
-            ..name = fakeName
-            ..extend = referImported('Fake', 'package:mockito/mockito.dart');
-          if (elementToFake.typeParameters != null) {
-            for (var typeParameter in elementToFake.typeParameters) {
-              cBuilder.types.add(_typeParameterReference(typeParameter));
-              typeParameters.add(refer(typeParameter.name));
-            }
-          }
-          cBuilder.implements.add(TypeReference((b) {
-            b
-              ..symbol = elementToFake.name
-              ..url = _typeImport(elementToFake)
-              ..types.addAll(typeParameters);
-          }));
-        }));
-        fakedClassElements.add(elementToFake);
+        _addFakeClass(fakeName, elementToFake);
       }
-      var typeArguments = dartType.typeArguments;
+      final typeArguments = dartType.typeArguments;
       return TypeReference((b) {
         b
           ..symbol = fakeName
@@ -1048,6 +1158,41 @@
     }
   }
 
+  /// Adds a [Fake] implementation of [elementToFake], named [fakeName].
+  void _addFakeClass(String fakeName, ClassElement elementToFake) {
+    fakeClasses.add(Class((cBuilder) {
+      // For each type parameter on [elementToFake], the Fake class needs a type
+      // parameter with same type variables, and a mirrored type argument for
+      // the "implements" clause.
+      final typeParameters = <Reference>[];
+      cBuilder
+        ..name = fakeName
+        ..extend = referImported('Fake', 'package:mockito/mockito.dart');
+      if (elementToFake.typeParameters != null) {
+        for (var typeParameter in elementToFake.typeParameters) {
+          cBuilder.types.add(_typeParameterReference(typeParameter));
+          typeParameters.add(refer(typeParameter.name));
+        }
+      }
+      cBuilder.implements.add(TypeReference((b) {
+        b
+          ..symbol = elementToFake.name
+          ..url = _typeImport(elementToFake)
+          ..types.addAll(typeParameters);
+      }));
+
+      final toStringMethod = elementToFake.methods
+          .firstWhereOrNull((method) => method.name == 'toString');
+      if (toStringMethod != null) {
+        // If [elementToFake] includes an overriding `toString` implementation,
+        // we need to include an implementation which matches the signature.
+        cBuilder.methods.add(Method(
+            (mBuilder) => _buildOverridingMethod(mBuilder, toStringMethod)));
+      }
+    }));
+    fakedClassElements.add(elementToFake);
+  }
+
   /// Returns a [Parameter] which matches [parameter].
   ///
   /// If [parameter] is unnamed (like a positional parameter in a function
@@ -1056,8 +1201,13 @@
   /// If the type needs to be nullable, rather than matching the nullability of
   /// [parameter], use [forceNullable].
   Parameter _matchingParameter(ParameterElement parameter,
-      {String defaultName, bool forceNullable = false}) {
-    var name = parameter.name?.isEmpty ?? false ? defaultName : parameter.name;
+      {String? defaultName, bool forceNullable = false}) {
+    assert(
+        parameter.name.isNotEmpty || defaultName != null,
+        'parameter must have a non-empty name, or non-null defaultName must be '
+        'passed, but parameter name is "${parameter.name}" and defaultName is '
+        '$defaultName');
+    var name = parameter.name.isEmpty ? defaultName! : parameter.name;
     return Parameter((pBuilder) {
       pBuilder
         ..name = name
@@ -1066,10 +1216,10 @@
       if (parameter.defaultValueCode != null) {
         try {
           pBuilder.defaultTo =
-              _expressionFromDartObject(parameter.computeConstantValue()).code;
+              _expressionFromDartObject(parameter.computeConstantValue()!).code;
         } on _ReviveException catch (e) {
-          final method = parameter.enclosingElement;
-          final clazz = method.enclosingElement;
+          final method = parameter.enclosingElement!;
+          final clazz = method.enclosingElement!;
           throw InvalidMockitoAnnotationException(
               'Mockito cannot generate a valid stub for method '
               "'${clazz.displayName}.${method.displayName}'; parameter "
@@ -1104,8 +1254,8 @@
     } else if (constant.isMap) {
       return literalConstMap({
         for (var pair in constant.mapValue.entries)
-          _expressionFromDartObject(pair.key):
-              _expressionFromDartObject(pair.value)
+          _expressionFromDartObject(pair.key!):
+              _expressionFromDartObject(pair.value!)
       });
     } else if (constant.isSet) {
       return literalConstSet({
@@ -1115,7 +1265,7 @@
     } else if (constant.isType) {
       // TODO(srawlins): It seems like this might be revivable, but Angular
       // does not revive Types; we should investigate this if users request it.
-      var type = object.toTypeValue();
+      var type = object.toTypeValue()!;
       var typeStr = type.getDisplayString(withNullability: false);
       throw _ReviveException('default value is a Type: $typeStr.');
     } else {
@@ -1123,7 +1273,7 @@
       // object constructed with `const`. Revive it.
       var revivable = constant.revive();
       if (revivable.isPrivate) {
-        final privateReference = revivable.accessor?.isNotEmpty == true
+        final privateReference = revivable.accessor.isNotEmpty
             ? '${revivable.source}::${revivable.accessor}'
             : '${revivable.source}';
         throw _ReviveException(
@@ -1138,7 +1288,7 @@
         // We can create this invocation by referring to a const field or
         // top-level variable.
         return referImported(
-            revivable.accessor, _typeImport(object.type.element));
+            revivable.accessor, _typeImport(object.type!.element));
       }
 
       final name = revivable.source.fragment;
@@ -1150,7 +1300,7 @@
         for (var pair in revivable.namedArguments.entries)
           pair.key: _expressionFromDartObject(pair.value)
       };
-      final type = referImported(name, _typeImport(object.type.element));
+      final type = referImported(name, _typeImport(object.type!.element));
       if (revivable.accessor.isNotEmpty) {
         return type.constInstanceNamed(
           revivable.accessor,
@@ -1187,9 +1337,7 @@
           superNoSuchMethod.asA(_typeReference(getter.returnType));
     }
 
-    builder
-      ..lambda = true
-      ..body = superNoSuchMethod.code;
+    builder.body = superNoSuchMethod.code;
   }
 
   /// Build a setter which overrides [setter], widening the single parameter
@@ -1203,15 +1351,12 @@
       ..annotations.addAll([refer('override')])
       ..type = MethodType.setter;
 
-    Expression invocationPositionalArg;
     assert(setter.parameters.length == 1);
     final parameter = setter.parameters.single;
-    if (parameter.isRequiredPositional) {
-      builder.requiredParameters.add(Parameter((pBuilder) => pBuilder
-        ..name = parameter.displayName
-        ..type = _typeReference(parameter.type, forceNullable: true)));
-      invocationPositionalArg = refer(parameter.displayName);
-    }
+    builder.requiredParameters.add(Parameter((pBuilder) => pBuilder
+      ..name = parameter.displayName
+      ..type = _typeReference(parameter.type, forceNullable: true)));
+    final invocationPositionalArg = refer(parameter.displayName);
 
     final invocation = refer('Invocation').property('setter').call([
       refer('#${setter.displayName}'),
@@ -1230,7 +1375,7 @@
     return TypeReference((b) {
       b.symbol = typeParameter.name;
       if (typeParameter.bound != null) {
-        b.bound = _typeReference(typeParameter.bound);
+        b.bound = _typeReference(typeParameter.bound!);
       }
     });
   }
@@ -1285,7 +1430,7 @@
           ..symbol = element.name
           ..url = _typeImport(element)
           ..isNullable = forceNullable || typeSystem.isNullable(type);
-        for (var typeArgument in type.aliasArguments) {
+        for (var typeArgument in type.aliasArguments!) {
           b.types.add(_typeReference(typeArgument));
         }
       });
@@ -1306,7 +1451,7 @@
   /// Returns the import URL for [element].
   ///
   /// For some types, like `dynamic` and type variables, this may return null.
-  String _typeImport(Element element) {
+  String? _typeImport(Element? element) {
     // For type variables, no import needed.
     if (element is TypeParameterElement) return null;
 
@@ -1316,14 +1461,14 @@
     assert(assetUris.containsKey(element),
         'An element, "$element", is missing from the asset URI mapping');
 
-    return assetUris[element];
+    return assetUris[element]!;
   }
 
   /// Returns a [Reference] to [symbol] with [url].
   ///
   /// This function overrides [code_builder.refer] so as to ensure that [url] is
   /// given.
-  static Reference referImported(String symbol, String url) =>
+  static Reference referImported(String symbol, String? url) =>
       Reference(symbol, url);
 
   /// Returns a [Reference] to [symbol] with no URL.
@@ -1360,7 +1505,7 @@
     if (this is ClassElement) {
       return "The class '$name'";
     } else if (this is MethodElement) {
-      var className = enclosingElement.name;
+      var className = enclosingElement!.name;
       return "The method '$className.$name'";
     } else {
       return 'unknown element';
@@ -1373,4 +1518,57 @@
   bool get isFutureOfVoid =>
       isDartAsyncFuture &&
       (this as analyzer.InterfaceType).typeArguments.first.isVoid;
+
+  /// Returns whether this type is a "List" type from the dart:typed_data
+  /// library.
+  bool get isDartTypedDataList {
+    if (element!.library!.name != 'dart.typed_data') {
+      return false;
+    }
+    final name = element!.name;
+    return name == 'Float32List' ||
+        name == 'Float64List' ||
+        name == 'Int8List' ||
+        name == 'Int16List' ||
+        name == 'Int32List' ||
+        name == 'Int64List' ||
+        name == 'Uint8List' ||
+        name == 'Uint16List' ||
+        name == 'Uint32List' ||
+        name == 'Uint64List';
+  }
+}
+
+extension on analyzer.InterfaceType {
+  bool get hasExplicitTypeArguments {
+    if (typeArguments == null) return false;
+
+    // If it appears that one type argument was given, then they all were. This
+    // returns the wrong result when the type arguments given are all `dynamic`,
+    // or are each equal to the bound of the corresponding type parameter. There
+    // may not be a way to get around this.
+    for (var i = 0; i < typeArguments.length; i++) {
+      final typeArgument = typeArguments[i];
+      // If [typeArgument] is a type parameter, this indicates that no type
+      // arguments were passed. This likely came from the 'classes' argument of
+      // GenerateMocks, and [type] is the declaration type (`Foo<T>` vs
+      // `Foo<dynamic>`).
+      if (typeArgument is analyzer.TypeParameterType) return false;
+
+      // If [type] was given to @GenerateMocks as a Type, and no explicit type
+      // argument is given, [typeArgument] is `dynamic` (_not_ the bound, as one
+      // might think). We determine that an explicit type argument was given if
+      // it is not `dynamic`.
+      if (typeArgument.isDynamic) continue;
+
+      // If, on the other hand, [type] was given to @GenerateMock as a type
+      // argument to `MockSpec()`, and no type argument is given, [typeArgument]
+      // is the bound of the corresponding type paramter (`dynamic` or
+      // otherwise). We determine that an explicit type argument was given if
+      // [typeArgument] is not equal to [bound].
+      final bound = element.typeParameters[i].bound;
+      if (!typeArgument.isDynamic && typeArgument != bound) return true;
+    }
+    return false;
+  }
 }
diff --git a/mockito/lib/src/invocation_matcher.dart b/mockito/lib/src/invocation_matcher.dart
index 40f5b54..005eb05 100644
--- a/mockito/lib/src/invocation_matcher.dart
+++ b/mockito/lib/src/invocation_matcher.dart
@@ -126,7 +126,7 @@
   @override
   Description describe(Description d) => _describeInvocation(d, _invocation);
 
-  // TODO(matanl): Better implement describeMismatch and use state from matches.
+  // TODO: Better implement describeMismatch and use state from matches.
   // Specifically, if a Matcher is passed as an argument, we'd like to get an
   // error like "Expected fly(miles: > 10), Actual: fly(miles: 5)".
   @override
diff --git a/mockito/lib/src/mock.dart b/mockito/lib/src/mock.dart
index b90d036..f2db811 100644
--- a/mockito/lib/src/mock.dart
+++ b/mockito/lib/src/mock.dart
@@ -703,31 +703,65 @@
   String toString() => '$ArgMatcher {$matcher: $_capture}';
 }
 
-/// An argument matcher that matches any argument passed in "this" position.
+/// An argument matcher that matches any argument passed in this argument
+/// position.
+///
+/// See the README section on
+/// [argument matchers](https://pub.dev/packages/mockito#argument-matchers)
+/// for examples.
 Null get any => _registerMatcher(anything, false, argumentMatcher: 'any');
 
 /// An argument matcher that matches any named argument passed in for the
 /// parameter named [named].
+///
+/// See the README section on
+/// [named argument matchers](https://pub.dev/packages/mockito#named-arguments)
+/// for examples.
 Null anyNamed(String named) => _registerMatcher(anything, false,
     named: named, argumentMatcher: 'anyNamed');
 
-/// An argument matcher that matches any argument passed in "this" position, and
-/// captures the argument for later access with `captured`.
+/// An argument matcher that matches any argument passed in this argument
+/// position, and captures the argument for later access with
+/// [VerificationResult.captured].
+///
+/// See the README section on
+/// [capturing arguments](https://pub.dev/packages/mockito#capturing-arguments-for-further-assertions)
+/// for examples.
 Null get captureAny =>
     _registerMatcher(anything, true, argumentMatcher: 'captureAny');
 
 /// An argument matcher that matches any named argument passed in for the
 /// parameter named [named], and captures the argument for later access with
-/// `captured`.
+/// [VerificationResult.captured].
+///
+/// See the README section on
+/// [capturing arguments](https://pub.dev/packages/mockito#capturing-arguments-for-further-assertions)
+/// for examples.
 Null captureAnyNamed(String named) => _registerMatcher(anything, true,
     named: named, argumentMatcher: 'captureAnyNamed');
 
-/// An argument matcher that matches an argument that matches [matcher].
+/// An argument matcher that matches an argument (named or positional) that
+/// matches [matcher].
+
+/// When capturing a named argument, the name of the argument must be passed via
+/// [named].
+///
+/// See the README section on
+/// [argument matchers](https://pub.dev/packages/mockito#argument-matchers)
+/// for examples.
 Null argThat(Matcher matcher, {String? named}) =>
     _registerMatcher(matcher, false, named: named, argumentMatcher: 'argThat');
 
-/// An argument matcher that matches an argument that matches [matcher], and
-/// captures the argument for later access with `captured`.
+/// An argument matcher that matches an argument (named or positional) that
+/// matches [matcher], and captures the argument for later access with
+/// [VerificationResult.captured].
+
+/// When capturing a named argument, the name of the argument must be passed via
+/// [named].
+///
+/// See the README section on
+/// [capturing arguments](https://pub.dev/packages/mockito#capturing-arguments-for-further-assertions)
+/// for examples.
 Null captureThat(Matcher matcher, {String? named}) =>
     _registerMatcher(matcher, true,
         named: named, argumentMatcher: 'captureThat');
@@ -1155,7 +1189,9 @@
   /// improve readability.
   String toPrettyString() {
     String argString;
-    var args = positionalArguments.map((v) => '$v');
+    // Add quotes around strings to clarify the type of the argument to the user
+    // and so the empty string is represented.
+    var args = positionalArguments.map((v) => v is String ? "'$v'" : '$v');
     if (args.any((arg) => arg.contains('\n'))) {
       // As one or more arg contains newlines, put each on its own line, and
       // indent each, for better readability.
diff --git a/mockito/lib/src/version.dart b/mockito/lib/src/version.dart
index 5fd5c67..1b9e3c1 100644
--- a/mockito/lib/src/version.dart
+++ b/mockito/lib/src/version.dart
@@ -1 +1 @@
-const packageVersion = '5.0.7';
+const packageVersion = '5.0.10';
diff --git a/mockito/pubspec.yaml b/mockito/pubspec.yaml
index 607cf6a..1324447 100644
--- a/mockito/pubspec.yaml
+++ b/mockito/pubspec.yaml
@@ -1,7 +1,7 @@
 name: mockito
-version: 5.0.7
+version: 5.0.10
 
-description: A mock framework inspired by Mockito.
+description: A mock framework inspired by Mockito.  With APIs for Fakes, Mocks, behavior verification, stubbing, and much more.
 homepage: https://github.com/dart-lang/mockito
 
 environment:
@@ -10,7 +10,7 @@
 dependencies:
   analyzer: ^1.0.0
   build: '>=1.3.0 <3.0.0'
-  code_builder: '>=3.7.0 <5.0.0'
+  code_builder: ^4.0.0
   collection: ^1.15.0
   dart_style: '>=1.3.6 <3.0.0'
   matcher: ^0.12.10
@@ -20,9 +20,9 @@
   test_api: '>=0.2.1 <0.5.0'
 
 dev_dependencies:
-  build_runner: ^1.12.0
+  build_runner: ^2.0.0
   build_test: ^2.0.0
-  build_web_compilers: '>=1.0.0 <3.0.0'
+  build_web_compilers: ^3.0.0
   http: ^0.13.0
   package_config: '>=1.9.3 <3.0.0'
   pedantic: ^1.10.0
diff --git a/multicast_dns/BUILD.gn b/multicast_dns/BUILD.gn
index d0761b2..1d5773a 100644
--- a/multicast_dns/BUILD.gn
+++ b/multicast_dns/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for multicast_dns-0.3.0
+# This file is generated by package_importer.py for multicast_dns-0.3.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/mustache_template/BUILD.gn b/mustache_template/BUILD.gn
index b3abb9e..948abd0 100644
--- a/mustache_template/BUILD.gn
+++ b/mustache_template/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for mustache_template-2.0.0
+# This file is generated by package_importer.py for mustache_template-2.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/native_stack_traces/BUILD.gn b/native_stack_traces/BUILD.gn
index 9ddc0c5..3130940 100644
--- a/native_stack_traces/BUILD.gn
+++ b/native_stack_traces/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for native_stack_traces-0.4.0
+# This file is generated by package_importer.py for native_stack_traces-0.4.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/nested/BUILD.gn b/nested/BUILD.gn
index aa20924..fdead87 100644
--- a/nested/BUILD.gn
+++ b/nested/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for nested-1.0.0
+# This file is generated by package_importer.py for nested-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/node_preamble/BUILD.gn b/node_preamble/BUILD.gn
index 837672c..63e34c4 100644
--- a/node_preamble/BUILD.gn
+++ b/node_preamble/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for node_preamble-2.0.1
+# This file is generated by package_importer.py for node_preamble-2.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/package_config.json b/package_config.json
index 18385e0..f42b97d 100644
--- a/package_config.json
+++ b/package_config.json
@@ -51,13 +51,13 @@
       "rootUri": "./build/"
     },
     {
-      "languageVersion": "2.6",
+      "languageVersion": "2.12",
       "name": "built_collection",
       "packageUri": "lib/",
       "rootUri": "./built_collection/"
     },
     {
-      "languageVersion": "2.3",
+      "languageVersion": "2.12",
       "name": "built_value",
       "packageUri": "lib/",
       "rootUri": "./built_value/"
@@ -93,7 +93,7 @@
       "rootUri": "./clock/"
     },
     {
-      "languageVersion": "2.7",
+      "languageVersion": "2.12",
       "name": "code_builder",
       "packageUri": "lib/",
       "rootUri": "./code_builder/"
@@ -135,7 +135,7 @@
       "rootUri": "./csslib/"
     },
     {
-      "languageVersion": "2.11",
+      "languageVersion": "2.12",
       "name": "dart_style",
       "packageUri": "lib/",
       "rootUri": "./dart_style/"
@@ -147,6 +147,12 @@
       "rootUri": "./dds/"
     },
     {
+      "languageVersion": "2.12",
+      "name": "devtools_shared",
+      "packageUri": "lib/",
+      "rootUri": "./devtools_shared/"
+    },
+    {
       "languageVersion": "2.13",
       "name": "dwds",
       "packageUri": "lib/",
@@ -177,7 +183,7 @@
       "rootUri": "./file/"
     },
     {
-      "languageVersion": "2.1",
+      "languageVersion": "2.12",
       "name": "fixnum",
       "packageUri": "lib/",
       "rootUri": "./fixnum/"
@@ -291,7 +297,7 @@
       "rootUri": "./json_annotation/"
     },
     {
-      "languageVersion": "2.2",
+      "languageVersion": "2.12",
       "name": "json_rpc_2",
       "packageUri": "lib/",
       "rootUri": "./json_rpc_2/"
@@ -430,18 +436,6 @@
     },
     {
       "languageVersion": "2.7",
-      "name": "protobuf",
-      "packageUri": "lib/",
-      "rootUri": "./protobuf/"
-    },
-    {
-      "languageVersion": "2.7",
-      "name": "protoc_plugin",
-      "packageUri": "lib/",
-      "rootUri": "./protoc_plugin/"
-    },
-    {
-      "languageVersion": "2.7",
       "name": "provider",
       "packageUri": "lib/",
       "rootUri": "./provider/"
@@ -501,7 +495,7 @@
       "rootUri": "./shelf_web_socket/"
     },
     {
-      "languageVersion": "2.11",
+      "languageVersion": "2.12",
       "name": "source_gen",
       "packageUri": "lib/",
       "rootUri": "./source_gen/"
@@ -525,7 +519,7 @@
       "rootUri": "./source_span/"
     },
     {
-      "languageVersion": "2.2",
+      "languageVersion": "2.12",
       "name": "sse",
       "packageUri": "lib/",
       "rootUri": "./sse/"
diff --git a/package_config/BUILD.gn b/package_config/BUILD.gn
index 7038c67..1f5a14c 100644
--- a/package_config/BUILD.gn
+++ b/package_config/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for package_config-2.0.0
+# This file is generated by package_importer.py for package_config-2.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/path/BUILD.gn b/path/BUILD.gn
index e1089e7..91970b9 100644
--- a/path/BUILD.gn
+++ b/path/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for path-1.8.0
+# This file is generated by package_importer.py for path-1.8.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/path_drawing/BUILD.gn b/path_drawing/BUILD.gn
index 3bde936..7696666 100644
--- a/path_drawing/BUILD.gn
+++ b/path_drawing/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for path_drawing-0.5.1
+# This file is generated by package_importer.py for path_drawing-0.5.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/path_parsing/BUILD.gn b/path_parsing/BUILD.gn
index c7ac575..9b06715 100644
--- a/path_parsing/BUILD.gn
+++ b/path_parsing/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for path_parsing-0.2.1
+# This file is generated by package_importer.py for path_parsing-0.2.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/pedantic/.gitignore b/pedantic/.gitignore
deleted file mode 100644
index 79f51c3..0000000
--- a/pedantic/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.dart_tool
-.packages
-pubspec.lock
diff --git a/pedantic/BUILD.gn b/pedantic/BUILD.gn
index 9926029..096fc50 100644
--- a/pedantic/BUILD.gn
+++ b/pedantic/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for pedantic-1.11.0
+# This file is generated by package_importer.py for pedantic-1.11.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/pedantic/CHANGELOG.md b/pedantic/CHANGELOG.md
index d35346a..66d4b45 100644
--- a/pedantic/CHANGELOG.md
+++ b/pedantic/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.11.1
+
+- Deprecated. Please use `package:lints` or `package:flutter_lints` instead.
+
 ## 1.11.0
 
 - Enforce 8 new lint rules:
diff --git a/pedantic/README.md b/pedantic/README.md
index 1e2d507..a337416 100644
--- a/pedantic/README.md
+++ b/pedantic/README.md
@@ -1,296 +1,17 @@
 [![Pub](https://img.shields.io/pub/v/pedantic.svg)](https://pub.dev/packages/pedantic)
 
-This package serves three purposes:
+This package is deprecated.
 
- - It documents how Dart static analysis is used internally at Google,
-   including best practices for the code we write. The documentation is
-   this `README.md`.
- - It contains a corresponding sample `analysis_options.yaml`.
- - It contains occasional small snippets of Dart code that are used in
-   implementing those best practices.
+Before it was deprecated, it was the way to get analysis options
+matching those used internally at Google. This was useful because
+it was a good starting point for which lints to enable.
 
-Most of the recommended lints directly implement the guidelines set out in
-[Effective Dart](https://dart.dev/guides/language/effective-dart).
-In a few cases the lints are stricter than the style guide for the sake of
-consistency. Details [below](#stricter-than-effective-dart).
+Instead, please see [package:lints](https://pub.dev/packages/lints) which
+is the lint recommendation from the Dart team, or
+[package:flutter_lints](https://pub.dev/packages/flutter_lints) which
+extends that for Flutter. These are better than the Google lint set
+because they take into account the needs of all Dart users, not just
+Google engineers.
 
-See
-[this Medium article](https://medium.com/dartlang/pedantic-dart-1c7d365510de)
-for more background.
-
-
-## Using Static Analysis
-
-Here is how static analysis is used internally at Google:
-
- - By default we disallow checking in code with any errors, warnings, or hints.
-   - The `TODO` hint is a permanent exception.
-   - Deprecation hints are a permanent exception. Deprecations are handled
-     separately on a case by case basis.
-   - `unused_element`, `unused_field` and `unused_local_variable` are allowed.
-   - When a new SDK version adds new errors, warnings or hints, we either clean
-     up everything before switching SDK version or maintain a list of allowed
-     violations so it can be gradually cleaned up.
- - Lints are considered and enabled on a case by case basis. When enabling a
-   lint we first clean up all pre-existing violations. After it's enabled, any
-   attempt to check in a further violation will be blocked.
-
-## Enabled Lints
-
-The currently enabled lints can be found in
-[analysis_options.1.11.0.yaml](https://github.com/dart-lang/pedantic/blob/master/lib/analysis_options.1.11.0.yaml).
-
-## Stricter than Effective Dart
-
-Here are the important places where `pedantic` is stricter than Effective Dart:
-
-`annotate_overrides` is stricter; Effective Dart says nothing about
-`@override`.
-
-`omit_local_variable_types` is stricter; Effective Dart only says to
-[avoid](https://dart.dev/guides/language/effective-dart/design#avoid-type-annotating-initialized-local-variables)
-type annotating initialized local variables.
-
-`prefer_single_quotes` is stricter; Effective Dart says nothing about
-single vs double quotes.
-
-`use_function_type_syntax` is stricter; Effective Dart only says to
-[consider](https://dart.dev/guides/language/effective-dart/design#consider-using-function-type-syntax-for-parameters)
-using the new syntax.
-
-## Using the Lints
-
-To use the lints add a dependency in your `pubspec.yaml`:
-
-```yaml
-# If you use `package:pedantic/pedantic.dart`, add a normal dependency.
-dependencies:
-  pedantic: ^1.11.0
-
-# Or, if you just want `analysis_options.yaml`, it can be a dev dependency.
-dev_dependencies:
-  pedantic: ^1.11.0
-```
-
-then, add an include in your `analysis_options.yaml`. If you want to always
-use the latest version of the lints, add a dependency on the main
-`analysis_options` file:
-
-
-```yaml
-include: package:pedantic/analysis_options.yaml
-```
-
-If your continuous build and/or presubmit check lints then they will likely
-fail whenever a new version of `package:pedantic` is released. To avoid this,
-specify a specific version of `analysis_options.yaml` instead:
-
-```yaml
-include: package:pedantic/analysis_options.1.11.0.yaml
-```
-
-## Unused Lints
-
-The following lints have been considered and will _not_ be enforced:
-
-`always_put_control_body_on_new_line`
-violates Effective Dart "DO format your code using dartfmt". See note about
-Flutter SDK style below.
-
-`always_put_required_named_parameters_first`
-does not allow for other logical orderings of parameters, such as matching the
-class field order or alphabetical.
-
-`always_specify_types`
-violates Effective Dart "AVOID type annotating initialized local variables"
-and others. See note about Flutter SDK style below.
-
-`avoid_annotating_with_dynamic`
-violates Effective Dart "PREFER annotating with `dynamic` instead of letting
-inference fail".
-
-`avoid_as`
-does not reflect common usage. See note about Flutter SDK style below.
-
-`avoid_catches_without_on_clauses`
-is too strict, catching anything is useful and common even if not always the
-most correct thing to do.
-
-`avoid_catching_errors`
-is too strict, the distinction between `Error` and `Exception` is followed
-closely in the SDK but is impractical to follow everywhere.
-
-`avoid_classes_with_only_static_members`
-is too strict, Effective Dart explicitly calls out some cases (constants,
-enum-like types) where it makes sense to violate this lint.
-
-`avoid_double_and_int_checks`
-only applies to web, but there is currently no mechanism for enabling a lint
-on web code only.
-
-`avoid_empty_else`
-redundant when `curly_braces_in_control_structures` is also enabled.
-
-`avoid_equals_and_hash_code_on_mutable_classes`
-would require the `@immutable` annotation to be consistently and correctly
-used everywhere.
-
-`avoid_field_initializers_in_const_classes`
-does not offer a clear readability benefit.
-
-`avoid_js_rounded_ints`
-only applies to web, but there is currently no mechanism for enabling a lint
-on web code only.
-
-`avoid_print`
-is too strict, it's okay to `print` in some code.
-
-`avoid_returning_null`
-will be obsoleted by sound null safety.
-
-`avoid_returning_this`
-has occasional false positives, and adds little value as the cascade operator
-for fluent interfaces in Dart is already very popular.
-
-`avoid_slow_async_io`
-gives wrong advice if the underlying filesystem has unusual properties, for
-example a network mount.
-
-`avoid_void_async`
-prevents a valid style where an asynchronous method has a `void` return type
-to indicate that nobody should `await` for the result.
-
-`cancel_subscriptions`
-has false positives when you use a utility method or class to call `cancel`.
-
-`cascade_invocations`
-does not reflect common usage.
-
-`close_sinks`
-has false positives when you use a utility method or class to call `close`.
-
-`constant_identifier_names`
-is too strict as it does not support the exceptions allowed in Effective Dart
-for use of ALL_CAPS.
-
-`control_flow_in_finally`
-does not offer enough value: people are unlikely to do this by accident,
-and there are occasional valid uses.
-
-`directives_ordering`
-would enforce a slightly different ordering to that used by IntelliJ and other
-tools using the analyzer.
-
-`diagnose_describe_all_properties`
-requires too much maintenance for most codebases.
-
-`empty_statements`
-is superfluous, enforcing use of `dartfmt` is sufficient to make empty
- statements obvious.
-
-`flutter_style_todos`
-is for Flutter SDK internal use, see note about Flutter SDK style below.
-
-`invariant_booleans`
-is experimental.
-
-`join_return_with_assignment`
-does not reflect common usage.
-
-`literal_only_boolean_expressions`
-does not offer enough value: such expressions are easily spotted and so hard
-to add by accident.
-
-`no_adjacent_strings_in_list`
-does not offer enough value: adjacent strings in lists are easily spotted
-when the code is formatted with `dartfmt`.
-
-`one_member_abstracts`
-is too strict, classes might implement more than one such abstract class and
-there is no equivalent way to do this using functions.
-
-`only_throw_errors`
-does not reflect common usage.
-
-`parameter_assignments`
-does not reflect common usage, and will cause particular problems with null safe
-code.
-
-`prefer_asserts_in_initializer_lists`
-does not reflect common usage.
-
-`prefer_asserts_with_message`
-is too strict; some asserts do not benefit from documentation.
-
-`prefer_bool_in_asserts`
- is obsolete in Dart 2; bool is required in asserts.
-
-`prefer_const_constructors`
-would add a lot of noise to code now that `new` is optional.
-
-`prefer_const_constructors_in_immutables`
-does not often apply as `@immutable` is not widely used.
-
-`prefer_const_literals_to_create_immutables`
-is too strict, requiring `const` everywhere adds noise.
-
-`prefer_constructors_over_static_methods`
-is too strict, in some cases static methods are better.
-
-`prefer_double_quotes`
-does not reflect common usage.
-
-`prefer_expression_function_bodies`
-is too strict, braces look better for long expressions.
-
-`prefer_final_in_for_each`
-does not reflect common usage.
-
-`prefer_final_locals`
-does not reflect common usage.
-
-`prefer_foreach`
-is too strict; `forEach` is not always an improvement.
-
-`prefer_if_elements_to_conditional_expressions`
-users prefer `?:` over `if`/`else`.
-
-`prefer_int_literals`
-does not reflect common usage.
-
-`sort_constructors_first`
-does not reflect common usage.
-
-`sort_unnamed_constructors_first`
-is too strict, people are free to choose the best constructor ordering.
-
-`test_types_in_equals`
-does not offer enough value: there are plenty of other mistakes possible in
-`operator ==` implementations. It's better to use codegen.
-
-`throw_in_finally`
-does not offer enough value: people are unlikely to do this by accident,
-and there are occasional valid uses.
-
-`unnecessary_null_aware_assignments`
-does not offer enough value: this is hard to do by mistake, and harmless.
-
-`use_setters_to_change_properties`
-is too strict: it can't detect when something is conceptually a property.
-
-`use_string_buffers`
-does not improve performance on web.
-
-`use_to_and_as_if_applicable`
-is too strict: it can't detect when the style rule actually applies.
-
-
-Note on Flutter SDK Style: some lints were created specifically to support
-Flutter SDK development. Flutter app developers should instead use standard
-Dart style as described in Effective Dart, and should not use these lints.
-
-## Features and bugs
-
-Please file feature requests and bugs at the [issue tracker][tracker].
-
-[tracker]: https://github.com/dart-lang/pedantic/issues
+To migrate away from `package:pedantic` please see the instructions
+[here](https://github.com/dart-lang/lints#migrating-from-packagepedantic).
diff --git a/pedantic/pubspec.yaml b/pedantic/pubspec.yaml
index 5309839..c056cd8 100644
--- a/pedantic/pubspec.yaml
+++ b/pedantic/pubspec.yaml
@@ -1,5 +1,5 @@
 name: pedantic
-version: 1.11.0
+version: 1.11.1
 description: >-
   The Dart analyzer settings and best practices used internally at Google.
 homepage: https://github.com/google/pedantic
diff --git a/petitparser/BUILD.gn b/petitparser/BUILD.gn
index 2430ecf..769c259 100644
--- a/petitparser/BUILD.gn
+++ b/petitparser/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for petitparser-4.2.0
+# This file is generated by package_importer.py for petitparser-4.2.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/platform/BUILD.gn b/platform/BUILD.gn
index efbf43b..c5f395e 100644
--- a/platform/BUILD.gn
+++ b/platform/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for platform-3.0.0
+# This file is generated by package_importer.py for platform-3.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/pool/BUILD.gn b/pool/BUILD.gn
index 94531af..82f66a8 100644
--- a/pool/BUILD.gn
+++ b/pool/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for pool-1.5.0
+# This file is generated by package_importer.py for pool-1.5.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/process/BUILD.gn b/process/BUILD.gn
index 1ded2c7..ada2367 100644
--- a/process/BUILD.gn
+++ b/process/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for process-4.2.1
+# This file is generated by package_importer.py for process-4.2.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/provider/BUILD.gn b/provider/BUILD.gn
index 0774b42..7d4072a 100644
--- a/provider/BUILD.gn
+++ b/provider/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for provider-4.3.3
+# This file is generated by package_importer.py for provider-4.3.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/pub_semver/BUILD.gn b/pub_semver/BUILD.gn
index 327db69..0906d3d 100644
--- a/pub_semver/BUILD.gn
+++ b/pub_semver/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for pub_semver-2.0.0
+# This file is generated by package_importer.py for pub_semver-2.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/pubspec_parse/BUILD.gn b/pubspec_parse/BUILD.gn
index 570befb..84ee31a 100644
--- a/pubspec_parse/BUILD.gn
+++ b/pubspec_parse/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for pubspec_parse-0.1.8
+# This file is generated by package_importer.py for pubspec_parse-0.1.8
 
 import("//build/dart/dart_library.gni")
 
diff --git a/quiver/BUILD.gn b/quiver/BUILD.gn
index 12c9d19..83f000d 100644
--- a/quiver/BUILD.gn
+++ b/quiver/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for quiver-2.1.5
+# This file is generated by package_importer.py for quiver-2.1.5
 
 import("//build/dart/dart_library.gni")
 
diff --git a/retry/BUILD.gn b/retry/BUILD.gn
index 6e62fba..399a99f 100644
--- a/retry/BUILD.gn
+++ b/retry/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for retry-3.1.0
+# This file is generated by package_importer.py for retry-3.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/shelf/BUILD.gn b/shelf/BUILD.gn
index 893fd2a..2479a7f 100644
--- a/shelf/BUILD.gn
+++ b/shelf/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for shelf-1.1.4
+# This file is generated by package_importer.py for shelf-1.1.4
 
 import("//build/dart/dart_library.gni")
 
diff --git a/shelf_packages_handler/BUILD.gn b/shelf_packages_handler/BUILD.gn
index 12b3558..6cbd3de 100644
--- a/shelf_packages_handler/BUILD.gn
+++ b/shelf_packages_handler/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for shelf_packages_handler-3.0.0
+# This file is generated by package_importer.py for shelf_packages_handler-3.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/shelf_proxy/BUILD.gn b/shelf_proxy/BUILD.gn
index f5a6536..8b10ad8 100644
--- a/shelf_proxy/BUILD.gn
+++ b/shelf_proxy/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for shelf_proxy-1.0.0
+# This file is generated by package_importer.py for shelf_proxy-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/shelf_static/BUILD.gn b/shelf_static/BUILD.gn
index c6ac20f..6e39583 100644
--- a/shelf_static/BUILD.gn
+++ b/shelf_static/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for shelf_static-1.0.0
+# This file is generated by package_importer.py for shelf_static-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/shelf_web_socket/BUILD.gn b/shelf_web_socket/BUILD.gn
index bf7671c..dd8ef5b 100644
--- a/shelf_web_socket/BUILD.gn
+++ b/shelf_web_socket/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for shelf_web_socket-1.0.1
+# This file is generated by package_importer.py for shelf_web_socket-1.0.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/source_gen/BUILD.gn b/source_gen/BUILD.gn
index 6d6ac2d..8c8d306 100644
--- a/source_gen/BUILD.gn
+++ b/source_gen/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for source_gen-0.9.10+4
+# This file is generated by package_importer.py for source_gen-1.0.2
 
 import("//build/dart/dart_library.gni")
 
 dart_library("source_gen") {
   package_name = "source_gen"
 
-  language_version = "2.11"
+  language_version = "2.12"
 
   disable_analysis = true
 
@@ -19,6 +19,7 @@
     "//third_party/dart-pkg/pub/path",
     "//third_party/dart-pkg/pub/pedantic",
     "//third_party/dart-pkg/pub/source_span",
+    "//third_party/dart-pkg/pub/yaml",
   ]
 
   sources = [
diff --git a/source_gen/CHANGELOG.md b/source_gen/CHANGELOG.md
index 0b77450..4410ee5 100644
--- a/source_gen/CHANGELOG.md
+++ b/source_gen/CHANGELOG.md
@@ -1,3 +1,20 @@
+## 1.0.2
+
+* Fix `TypeChecker.fromRuntimeType` for types that come from non-package uris.
+
+## 1.0.1
+
+* Improve the error message for a missing part directive.
+* Upgrade to `package:analyzer` version `1.7.0`.
+
+## 1.0.0
+
+* Migrate to null safety.
+* Drop deprecated APIs:
+  - `ConstantReader.isAny`.
+  - `ConstantReader.anyValue`.
+  - `LibraryReader.classElements`.
+
 ## 0.9.10+4
 
 * Upgrade to `package:build` version `2.0.0`.
diff --git a/source_gen/LICENSE b/source_gen/LICENSE
index de31e1a..633672a 100644
--- a/source_gen/LICENSE
+++ b/source_gen/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2015, the Dart project authors. All rights reserved.
+Copyright 2015, the Dart project authors.
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/source_gen/lib/builder.dart b/source_gen/lib/builder.dart
index 5fe72ac..0ad821f 100644
--- a/source_gen/lib/builder.dart
+++ b/source_gen/lib/builder.dart
@@ -22,12 +22,12 @@
 const _outputExtensions = '.g.dart';
 const _partFiles = '.g.part';
 
-Builder combiningBuilder([BuilderOptions options]) {
-  final optionsMap = Map<String, dynamic>.from(options?.config ?? {});
+Builder combiningBuilder([BuilderOptions options = BuilderOptions.empty]) {
+  final optionsMap = Map<String, dynamic>.from(options.config);
 
-  final includePartName = optionsMap.remove('include_part_name') as bool;
+  final includePartName = optionsMap.remove('include_part_name') as bool?;
   final ignoreForFile = Set<String>.from(
-    optionsMap.remove('ignore_for_file') as List ?? <String>[],
+    optionsMap.remove('ignore_for_file') as List? ?? <String>[],
   );
 
   final builder = CombiningBuilder(
@@ -36,11 +36,7 @@
   );
 
   if (optionsMap.isNotEmpty) {
-    if (log == null) {
-      throw StateError('Upgrade `build_runner` to at least 0.8.2.');
-    } else {
-      log.warning('These options were ignored: `$optionsMap`.');
-    }
+    log.warning('These options were ignored: `$optionsMap`.');
   }
   return builder;
 }
@@ -67,8 +63,8 @@
   /// is output as a comment before its content. This can be useful when
   /// debugging build issues.
   const CombiningBuilder({
-    bool includePartName = false,
-    Set<String> ignoreForFile,
+    bool? includePartName,
+    Set<String>? ignoreForFile,
   })  : _includePartName = includePartName ?? false,
         _ignoreForFile = ignoreForFile ?? const <String>{};
 
@@ -89,7 +85,7 @@
       partIdRegExpLiteral, // A valid part ID
       RegExp.escape(_partFiles), // the ending part extension
       '\$', // end of string
-    ].join(''));
+    ].join());
 
     final assetIds = await buildStep
         .findAssets(Glob(pattern))
diff --git a/source_gen/lib/src/builder.dart b/source_gen/lib/src/builder.dart
index c956a21..d902f82 100644
--- a/source_gen/lib/src/builder.dart
+++ b/source_gen/lib/src/builder.dart
@@ -41,10 +41,10 @@
   /// Wrap [_generators] to form a [Builder]-compatible API.
   _Builder(
     this._generators, {
-    String Function(String code) formatOutput,
+    String Function(String code)? formatOutput,
     String generatedExtension = '.g.dart',
     List<String> additionalOutputExtensions = const [],
-    String header,
+    String? header,
     this.allowSyntaxErrors = false,
   })  : _generatedExtension = generatedExtension,
         buildExtensions = {
@@ -55,9 +55,6 @@
         },
         formatOutput = formatOutput ?? _formatter.format,
         _header = (header ?? defaultFileHeader).trim() {
-    if (_generatedExtension == null) {
-      throw ArgumentError.notNull('generatedExtension');
-    }
     if (_generatedExtension.isEmpty || !_generatedExtension.startsWith('.')) {
       throw ArgumentError.value(_generatedExtension, 'generatedExtension',
           'Extension must be in the format of .*');
@@ -109,14 +106,6 @@
     if (!_isLibraryBuilder) {
       final asset = buildStep.inputId;
       final name = nameOfPartial(library, asset);
-      if (name == null) {
-        final suggest = suggestLibraryName(asset);
-        throw InvalidGenerationSourceError(
-            'Could not find library identifier so a "part of" cannot be built.',
-            todo: ''
-                'Consider adding the following to your source file:\n\n'
-                'library $suggest;');
-      }
       contentBuffer.writeln();
 
       String part;
@@ -138,7 +127,8 @@
           .any((e) => e.uri.stringValue == part);
       if (!hasLibraryPartDirectiveWithOutputUri) {
         // TODO: Upgrade to error in a future breaking change?
-        log.warning('Missing "part \'$part\';".');
+        log.warning('$part must be included as a part directive in '
+            'the input library with:\n    part \'$part\';');
         return;
       }
     }
@@ -212,7 +202,7 @@
   SharedPartBuilder(
     List<Generator> generators,
     String partId, {
-    String Function(String code) formatOutput,
+    String Function(String code)? formatOutput,
     List<String> additionalOutputExtensions = const [],
     bool allowSyntaxErrors = false,
   }) : super(
@@ -267,9 +257,9 @@
   PartBuilder(
     List<Generator> generators,
     String generatedExtension, {
-    String Function(String code) formatOutput,
+    String Function(String code)? formatOutput,
     List<String> additionalOutputExtensions = const [],
-    String header,
+    String? header,
     bool allowSyntaxErrors = false,
   }) : super(
           generators,
@@ -307,10 +297,10 @@
   /// libraries.
   LibraryBuilder(
     Generator generator, {
-    String Function(String code) formatOutput,
+    String Function(String code)? formatOutput,
     String generatedExtension = '.g.dart',
     List<String> additionalOutputExtensions = const [],
-    String header,
+    String? header,
     bool allowSyntaxErrors = false,
   }) : super(
           [generator],
@@ -359,7 +349,8 @@
     if (directive.metadata.isNotEmpty) return true;
     if (directive is PartDirective) {
       partIds.add(
-          AssetId.resolve(Uri.parse(directive.uri.stringValue), from: input));
+        AssetId.resolve(Uri.parse(directive.uri.stringValue!), from: input),
+      );
     }
   }
   for (var declaration in parsed.declarations) {
diff --git a/source_gen/lib/src/constants/reader.dart b/source_gen/lib/src/constants/reader.dart
index 3a2d6fc..711255e 100644
--- a/source_gen/lib/src/constants/reader.dart
+++ b/source_gen/lib/src/constants/reader.dart
@@ -16,24 +16,12 @@
 /// Unlike [DartObject.getField], the [read] method attempts to access super
 /// classes for the field value if not found.
 abstract class ConstantReader {
-  factory ConstantReader(DartObject object) =>
-      isNullLike(object) ? const _NullConstant() : _DartObjectConstant(object);
+  factory ConstantReader(DartObject? object) =>
+      isNullLike(object) ? const _NullConstant() : _DartObjectConstant(object!);
 
   const ConstantReader._();
 
   /// Whether this constant is a literal value.
-  @Deprecated('Use `isLiteral`, will be removed in 0.8.0')
-  bool get isAny => isLiteral;
-
-  /// Constant as a literal value.
-  ///
-  /// Throws [FormatException] if a valid literal value cannot be returned. This
-  /// is the case if the constant is not a literal or if the literal value
-  /// is represented at least partially with [DartObject] instances.
-  @Deprecated('Use `literalValue`, will be removed in 0.8.0')
-  Object get anyValue => literalValue;
-
-  /// Whether this constant is a literal value.
   bool get isLiteral => true;
 
   /// Constant as a literal value.
@@ -41,7 +29,7 @@
   /// Throws [FormatException] if a valid literal value cannot be returned. This
   /// is the case if the constant is not a literal or if the literal value
   /// is represented at least partially with [DartObject] instances.
-  Object get literalValue => null;
+  Object? get literalValue => null;
 
   /// Underlying object this instance is reading from.
   DartObject get objectValue;
@@ -59,7 +47,7 @@
   /// Reads [field] from the constant as another constant value.
   ///
   /// Unlike [read], returns `null` if the field is not found.
-  ConstantReader peek(String field);
+  ConstantReader? peek(String field);
 
   /// Whether this constant is a `null` value.
   bool get isNull => true;
@@ -104,7 +92,7 @@
   bool get isMap => false;
 
   /// Constant as a `Map` value.
-  Map<DartObject, DartObject> get mapValue;
+  Map<DartObject?, DartObject?> get mapValue;
 
   /// Whether this constant represents a `List` value.
   bool get isList => false;
@@ -156,7 +144,7 @@
   Map<DartObject, DartObject> get mapValue => _throw('Map');
 
   @override
-  ConstantReader peek(_) => null;
+  ConstantReader? peek(_) => null;
 
   @override
   ConstantReader read(_) => throw UnsupportedError('Null');
@@ -180,7 +168,7 @@
 
   const _DartObjectConstant(this.objectValue) : super._();
 
-  T _check<T>(T value, String expected) {
+  T _check<T>(T? value, String expected) {
     if (value == null) {
       throw FormatException('Not an instance of $expected.', objectValue);
     }
@@ -212,7 +200,7 @@
 
   @override
   bool instanceOf(TypeChecker checker) =>
-      checker.isAssignableFromType(objectValue.type);
+      checker.isAssignableFromType(objectValue.type!);
 
   @override
   bool get isNull => isNullLike(objectValue);
@@ -251,7 +239,7 @@
   bool get isMap => objectValue.toMapValue() != null;
 
   @override
-  Map<DartObject, DartObject> get mapValue =>
+  Map<DartObject?, DartObject?> get mapValue =>
       _check(objectValue.toMapValue(), 'Map');
 
   @override
@@ -274,7 +262,7 @@
   DartType get typeValue => _check(objectValue.toTypeValue(), 'Type');
 
   @override
-  ConstantReader peek(String field) {
+  ConstantReader? peek(String field) {
     final constant = ConstantReader(getFieldRecursive(objectValue, field));
     return constant.isNull ? null : constant;
   }
@@ -283,7 +271,7 @@
   ConstantReader read(String field) {
     final reader = peek(field);
     if (reader == null) {
-      assertHasField(objectValue?.type?.element as ClassElement, field);
+      assertHasField(objectValue.type?.element as ClassElement, field);
       return const _NullConstant();
     }
     return reader;
diff --git a/source_gen/lib/src/constants/revive.dart b/source_gen/lib/src/constants/revive.dart
index 3cb82dd..9926f3f 100644
--- a/source_gen/lib/src/constants/revive.dart
+++ b/source_gen/lib/src/constants/revive.dart
@@ -5,6 +5,7 @@
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+
 // ignore: implementation_imports
 import 'package:analyzer/src/dart/constant/value.dart';
 
@@ -19,9 +20,9 @@
 /// **NOTE**: Some returned [Revivable] instances are not representable as valid
 /// Dart source code (such as referencing private constructors). It is up to the
 /// build tool(s) using this library to surface error messages to the user.
-Revivable reviveInstance(DartObject object, [LibraryElement origin]) {
+Revivable reviveInstance(DartObject object, [LibraryElement? origin]) {
   final objectType = object.type;
-  Element element = object.type.aliasElement;
+  Element? element = objectType!.aliasElement;
   if (element == null) {
     if (objectType is InterfaceType) {
       element = objectType.element;
@@ -29,8 +30,8 @@
       element = object.toFunctionValue();
     }
   }
-  origin ??= element.library;
-  var url = Uri.parse(urlOfElement(element));
+  origin ??= element!.library;
+  var url = Uri.parse(urlOfElement(element!));
   if (element is FunctionElement) {
     return Revivable._(
       source: url.removeFragment(),
@@ -64,7 +65,7 @@
     return !result.isPrivate;
   }
 
-  for (final e in origin.definingCompilationUnit.types
+  for (final e in origin!.definingCompilationUnit.types
       .expand((t) => t.fields)
       .where((f) => f.isConst && f.computeConstantValue() == object)) {
     final result = Revivable._(
@@ -88,17 +89,15 @@
       return result;
     }
   }
-  if (origin != null) {
-    for (final e in origin.definingCompilationUnit.topLevelVariables.where(
-      (f) => f.isConst && f.computeConstantValue() == object,
-    )) {
-      final result = Revivable._(
-        source: Uri.parse(urlOfElement(origin)).replace(fragment: ''),
-        accessor: e.name,
-      );
-      if (tryResult(result)) {
-        return result;
-      }
+  for (final e in origin.definingCompilationUnit.topLevelVariables.where(
+    (f) => f.isConst && f.computeConstantValue() == object,
+  )) {
+    final result = Revivable._(
+      source: Uri.parse(urlOfElement(origin)).replace(fragment: ''),
+      accessor: e.name,
+    );
+    if (tryResult(result)) {
+      return result;
     }
   }
   // We could try and return the "best" result more intelligently.
@@ -128,7 +127,7 @@
   final Map<String, DartObject> namedArguments;
 
   const Revivable._({
-    this.source,
+    required this.source,
     this.accessor = '',
     this.positionalArguments = const [],
     this.namedArguments = const {},
diff --git a/source_gen/lib/src/constants/utils.dart b/source_gen/lib/src/constants/utils.dart
index 989ae0c..cbb8666 100644
--- a/source_gen/lib/src/constants/utils.dart
+++ b/source_gen/lib/src/constants/utils.dart
@@ -9,7 +9,7 @@
 ///
 /// Super types [ClassElement.supertype] are also checked before throwing.
 void assertHasField(ClassElement root, String name) {
-  var element = root;
+  ClassElement? element = root;
   while (element != null) {
     final field = element.getField(name);
     if (field != null) {
@@ -26,16 +26,16 @@
 }
 
 /// Returns whether or not [object] is or represents a `null` value.
-bool isNullLike(DartObject object) => object?.isNull != false;
+bool isNullLike(DartObject? object) => object?.isNull != false;
 
 /// Similar to [DartObject.getField], but traverses super classes.
 ///
 /// Returns `null` if ultimately [field] is never found.
-DartObject getFieldRecursive(DartObject object, String field) {
+DartObject? getFieldRecursive(DartObject? object, String field) {
   if (isNullLike(object)) {
     return null;
   }
-  final result = object.getField(field);
+  final result = object!.getField(field);
   if (isNullLike(result)) {
     return getFieldRecursive(object.getField('(super)'), field);
   }
diff --git a/source_gen/lib/src/generated_output.dart b/source_gen/lib/src/generated_output.dart
index 3f5476c..5756113 100644
--- a/source_gen/lib/src/generated_output.dart
+++ b/source_gen/lib/src/generated_output.dart
@@ -9,8 +9,7 @@
   final String generatorDescription;
 
   GeneratedOutput(Generator generator, this.output)
-      : assert(output != null),
-        assert(output.isNotEmpty),
+      : assert(output.isNotEmpty),
         // assuming length check is cheaper than simple string equality
         assert(output.length == output.trim().length),
         generatorDescription = _toString(generator);
diff --git a/source_gen/lib/src/generator.dart b/source_gen/lib/src/generator.dart
index d657ccd..2b66ca0 100644
--- a/source_gen/lib/src/generator.dart
+++ b/source_gen/lib/src/generator.dart
@@ -22,7 +22,8 @@
   /// output is Dart code returned through the Future. If there is nothing to
   /// generate for this library may return null, or a Future that resolves to
   /// null or the empty string.
-  FutureOr<String> generate(LibraryReader library, BuildStep buildStep) => null;
+  FutureOr<String?> generate(LibraryReader library, BuildStep buildStep) =>
+      null;
 
   @override
   String toString() => runtimeType.toString();
@@ -41,10 +42,9 @@
   /// The code element associated with this error.
   ///
   /// May be `null` if the error had no associated element.
-  final Element element;
+  final Element? element;
 
-  InvalidGenerationSourceError(this.message, {String todo, this.element})
-      : todo = todo ?? '';
+  InvalidGenerationSourceError(this.message, {this.todo = '', this.element});
 
   @override
   String toString() {
@@ -52,7 +52,7 @@
 
     if (element != null) {
       try {
-        final span = spanForElement(element);
+        final span = spanForElement(element!);
         buffer
           ..writeln()
           ..writeln(span.start.toolString)
diff --git a/source_gen/lib/src/generator_for_annotation.dart b/source_gen/lib/src/generator_for_annotation.dart
index a88d282..6edf426 100644
--- a/source_gen/lib/src/generator_for_annotation.dart
+++ b/source_gen/lib/src/generator_for_annotation.dart
@@ -53,7 +53,7 @@
       final generatedValue = generateForAnnotatedElement(
           annotatedElement.element, annotatedElement.annotation, buildStep);
       await for (var value in normalizeGeneratorOutput(generatedValue)) {
-        assert(value == null || (value.length == value.trim().length));
+        assert(value.length == value.trim().length);
         values.add(value);
       }
     }
diff --git a/source_gen/lib/src/library.dart b/source_gen/lib/src/library.dart
index e270b91..c9832be 100644
--- a/source_gen/lib/src/library.dart
+++ b/source_gen/lib/src/library.dart
@@ -28,7 +28,7 @@
   ///
   /// Unlike [LibraryElement.getType], this also correctly traverses identifiers
   /// that are accessible via one or more `export` directives.
-  ClassElement findType(String name) {
+  ClassElement? findType(String name) {
     final type = element.exportNamespace.get(name);
     return type is ClassElement ? type : null;
   }
@@ -49,10 +49,12 @@
 
   /// All of the declarations in this library annotated with [checker].
   Iterable<AnnotatedElement> annotatedWith(TypeChecker checker,
-      {bool throwOnUnresolved}) sync* {
+      {bool throwOnUnresolved = true}) sync* {
     for (final element in allElements) {
-      final annotation = checker.firstAnnotationOf(element,
-          throwOnUnresolved: throwOnUnresolved);
+      final annotation = checker.firstAnnotationOf(
+        element,
+        throwOnUnresolved: throwOnUnresolved,
+      );
       if (annotation != null) {
         yield AnnotatedElement(ConstantReader(annotation), element);
       }
@@ -61,7 +63,7 @@
 
   /// All of the declarations in this library annotated with exactly [checker].
   Iterable<AnnotatedElement> annotatedWithExact(TypeChecker checker,
-      {bool throwOnUnresolved}) sync* {
+      {bool throwOnUnresolved = true}) sync* {
     for (final element in allElements) {
       final annotation = checker.firstAnnotationOfExact(element,
           throwOnUnresolved: throwOnUnresolved);
@@ -81,7 +83,7 @@
   ///
   /// This is a typed convenience function for using [pathToUrl], and the same
   /// API restrictions hold around supported schemes and relative paths.
-  Uri pathToElement(Element element) => pathToUrl(element.source.uri);
+  Uri pathToElement(Element element) => pathToUrl(element.source!.uri);
 
   /// Returns a [Uri] from the current library to the one provided.
   ///
@@ -120,9 +122,6 @@
         return assetToPackageUrl(to);
       }
       var from = element.source.uri;
-      if (from == null) {
-        throw StateError('Current library has no source URL');
-      }
       // Normalize (convert to an asset: URL).
       from = normalizeUrl(from);
       if (_isRelative(from, to)) {
@@ -165,9 +164,6 @@
   /// All of the elements representing classes in this library.
   Iterable<ClassElement> get classes => element.units.expand((cu) => cu.types);
 
-  @Deprecated('Use classes instead')
-  Iterable<ClassElement> get classElements => classes;
-
   /// All of the elements representing enums in this library.
   Iterable<ClassElement> get enums => element.units.expand((cu) => cu.enums);
 }
diff --git a/source_gen/lib/src/output_helpers.dart b/source_gen/lib/src/output_helpers.dart
index 8ccc034..e9106ac 100644
--- a/source_gen/lib/src/output_helpers.dart
+++ b/source_gen/lib/src/output_helpers.dart
@@ -7,7 +7,7 @@
 /// Converts [Future], [Iterable], and [Stream] implementations
 /// containing [String] to a single [Stream] while ensuring all thrown
 /// exceptions are forwarded through the return value.
-Stream<String> normalizeGeneratorOutput(Object value) {
+Stream<String> normalizeGeneratorOutput(Object? value) {
   if (value == null) {
     return const Stream.empty();
   } else if (value is Future) {
@@ -17,7 +17,7 @@
   }
 
   if (value is Iterable) {
-    value = Stream.fromIterable(value as Iterable);
+    value = Stream.fromIterable(value);
   }
 
   if (value is Stream) {
@@ -26,7 +26,7 @@
         return e.trim();
       }
 
-      throw _argError(e);
+      throw _argError(e as Object);
     }).where((e) => e.isNotEmpty);
   }
   throw _argError(value);
diff --git a/source_gen/lib/src/span_for_element.dart b/source_gen/lib/src/span_for_element.dart
index 14398b1..2dada57 100644
--- a/source_gen/lib/src/span_for_element.dart
+++ b/source_gen/lib/src/span_for_element.dart
@@ -18,10 +18,10 @@
 ///
 /// Not all results from the analyzer API may return source information as part
 /// of the element, so [file] may need to be manually provided in those cases.
-SourceSpan spanForElement(Element element, [SourceFile file]) {
-  final url = assetToPackageUrl(element.source.uri);
+SourceSpan spanForElement(Element element, [SourceFile? file]) {
+  final url = assetToPackageUrl(element.source!.uri);
   if (file == null) {
-    final contents = element?.source?.contents;
+    final contents = element.source?.contents;
     if (contents == null) {
       return SourceSpan(
         SourceLocation(
@@ -32,7 +32,7 @@
           element.nameOffset + element.nameLength,
           sourceUrl: url,
         ),
-        element.name,
+        element.name!,
       );
     }
     file = SourceFile.fromString(contents.data, url: url);
@@ -40,11 +40,11 @@
   if (element.nameOffset < 0) {
     if (element is PropertyInducingElement) {
       if (element.getter != null) {
-        return spanForElement(element.getter);
+        return spanForElement(element.getter!);
       }
 
       if (element.setter != null) {
-        return spanForElement(element.setter);
+        return spanForElement(element.setter!);
       }
     }
   }
diff --git a/source_gen/lib/src/type_checker.dart b/source_gen/lib/src/type_checker.dart
index 749621e..5b6e24a 100644
--- a/source_gen/lib/src/type_checker.dart
+++ b/source_gen/lib/src/type_checker.dart
@@ -4,10 +4,12 @@
 
 import 'dart:mirrors' hide SourceLocation;
 
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:build/build.dart';
 import 'package:source_span/source_span.dart';
 
 import 'utils.dart';
@@ -59,7 +61,10 @@
   /// Otherwise returns `null`.
   ///
   /// Throws on unresolved annotations unless [throwOnUnresolved] is `false`.
-  DartObject firstAnnotationOf(Element element, {bool throwOnUnresolved}) {
+  DartObject? firstAnnotationOf(
+    Element element, {
+    bool throwOnUnresolved = true,
+  }) {
     if (element.metadata.isEmpty) {
       return null;
     }
@@ -71,14 +76,17 @@
   /// Returns if a constant annotating [element] is assignable to this type.
   ///
   /// Throws on unresolved annotations unless [throwOnUnresolved] is `false`.
-  bool hasAnnotationOf(Element element, {bool throwOnUnresolved}) =>
+  bool hasAnnotationOf(Element element, {bool throwOnUnresolved = true}) =>
       firstAnnotationOf(element, throwOnUnresolved: throwOnUnresolved) != null;
 
   /// Returns the first constant annotating [element] that is exactly this type.
   ///
   /// Throws [UnresolvedAnnotationException] on unresolved annotations unless
   /// [throwOnUnresolved] is explicitly set to `false` (default is `true`).
-  DartObject firstAnnotationOfExact(Element element, {bool throwOnUnresolved}) {
+  DartObject? firstAnnotationOfExact(
+    Element element, {
+    bool throwOnUnresolved = true,
+  }) {
     if (element.metadata.isEmpty) {
       return null;
     }
@@ -91,16 +99,15 @@
   ///
   /// Throws [UnresolvedAnnotationException] on unresolved annotations unless
   /// [throwOnUnresolved] is explicitly set to `false` (default is `true`).
-  bool hasAnnotationOfExact(Element element, {bool throwOnUnresolved}) =>
+  bool hasAnnotationOfExact(Element element, {bool throwOnUnresolved = true}) =>
       firstAnnotationOfExact(element, throwOnUnresolved: throwOnUnresolved) !=
       null;
 
-  DartObject _computeConstantValue(
+  DartObject? _computeConstantValue(
     Element element,
     int annotationIndex, {
-    bool throwOnUnresolved,
+    bool throwOnUnresolved = true,
   }) {
-    throwOnUnresolved ??= true;
     final annotation = element.metadata[annotationIndex];
     final result = annotation.computeConstantValue();
     if (result == null && throwOnUnresolved) {
@@ -115,7 +122,7 @@
   /// [throwOnUnresolved] is explicitly set to `false` (default is `true`).
   Iterable<DartObject> annotationsOf(
     Element element, {
-    bool throwOnUnresolved,
+    bool throwOnUnresolved = true,
   }) =>
       _annotationsWhere(
         element,
@@ -126,7 +133,7 @@
   Iterable<DartObject> _annotationsWhere(
     Element element,
     bool Function(DartType) predicate, {
-    bool throwOnUnresolved,
+    bool throwOnUnresolved = true,
   }) sync* {
     for (var i = 0; i < element.metadata.length; i++) {
       final value = _computeConstantValue(
@@ -134,7 +141,7 @@
         i,
         throwOnUnresolved: throwOnUnresolved,
       );
-      if (value?.type != null && predicate(value.type)) {
+      if (value?.type != null && predicate(value!.type!)) {
         yield value;
       }
     }
@@ -146,7 +153,7 @@
   /// [throwOnUnresolved] is explicitly set to `false` (default is `true`).
   Iterable<DartObject> annotationsOfExact(
     Element element, {
-    bool throwOnUnresolved,
+    bool throwOnUnresolved = true,
   }) =>
       _annotationsWhere(
         element,
@@ -161,13 +168,13 @@
 
   /// Returns `true` if [staticType] can be assigned to this type.
   bool isAssignableFromType(DartType staticType) =>
-      isAssignableFrom(staticType.element);
+      isAssignableFrom(staticType.element!);
 
   /// Returns `true` if representing the exact same class as [element].
   bool isExactly(Element element);
 
   /// Returns `true` if representing the exact same type as [staticType].
-  bool isExactlyType(DartType staticType) => isExactly(staticType.element);
+  bool isExactlyType(DartType staticType) => isExactly(staticType.element!);
 
   /// Returns `true` if representing a super class of [element].
   ///
@@ -178,7 +185,7 @@
       var theSuper = element.supertype;
 
       do {
-        if (isExactlyType(theSuper)) {
+        if (isExactlyType(theSuper!)) {
           return true;
         }
 
@@ -193,7 +200,7 @@
   ///
   /// This only takes into account the *extends* hierarchy. If you wish
   /// to check mixins and interfaces, use [isAssignableFromType].
-  bool isSuperTypeOf(DartType staticType) => isSuperOf(staticType.element);
+  bool isSuperTypeOf(DartType staticType) => isSuperOf(staticType.element!);
 }
 
 // Checks a static type against another static type;
@@ -207,7 +214,7 @@
       element is ClassElement && element == _type.element;
 
   @override
-  String toString() => urlOfElement(_type.element);
+  String toString() => urlOfElement(_type.element!);
 }
 
 // Checks a runtime type against a static type.
@@ -287,24 +294,45 @@
   /// Source span of the annotation that was not resolved.
   ///
   /// May be `null` if the import library was not found.
-  final SourceSpan annotationSource;
+  final SourceSpan? annotationSource;
 
-  static SourceSpan _findSpan(
+  static SourceSpan? _findSpan(
     Element annotatedElement,
     int annotationIndex,
   ) {
-    final parsedLibrary = annotatedElement.session
-        .getParsedLibraryByElement(annotatedElement.library);
-    final declaration = parsedLibrary.getElementDeclaration(annotatedElement);
-    final annotatedNode = declaration.node as AnnotatedNode;
-    final annotation = annotatedNode.metadata[annotationIndex];
-    final start = annotation.offset;
-    final end = start + annotation.length;
-    return SourceSpan(
-      SourceLocation(start, sourceUrl: declaration.parsedUnit.uri),
-      SourceLocation(end, sourceUrl: declaration.parsedUnit.uri),
-      declaration.parsedUnit.content.substring(start, end),
-    );
+    try {
+      final parsedLibrary = annotatedElement.session!
+              .getParsedLibraryByElement2(annotatedElement.library!)
+          as ParsedLibraryResult;
+      final declaration = parsedLibrary.getElementDeclaration(annotatedElement);
+      if (declaration == null) {
+        return null;
+      }
+      final annotatedNode = declaration.node as AnnotatedNode;
+      final annotation = annotatedNode.metadata[annotationIndex];
+      final start = annotation.offset;
+      final end = start + annotation.length;
+      final parsedUnit = declaration.parsedUnit!;
+      return SourceSpan(
+        SourceLocation(start, sourceUrl: parsedUnit.uri),
+        SourceLocation(end, sourceUrl: parsedUnit.uri),
+        parsedUnit.content.substring(start, end),
+      );
+    } catch (e, stack) {
+      // Trying to get more information on https://github.com/dart-lang/sdk/issues/45127
+      log.warning(
+        '''
+An unexpected error was thrown trying to get location information on `$annotatedElement` (${annotatedElement.runtimeType}). 
+
+Please file an issue at https://github.com/dart-lang/source_gen/issues/new
+Include the contents of this warning and the stack trace along with
+the version of `package:source_gen`, `package:analyzer` from `pubspec.lock`.
+''',
+        e,
+        stack,
+      );
+      return null;
+    }
   }
 
   /// Creates an exception from an annotation ([annotationIndex]) that was not
@@ -324,9 +352,9 @@
 
   @override
   String toString() {
-    final message = 'Could not resolve annotation for $annotatedElement';
+    final message = 'Could not resolve annotation for `$annotatedElement`.';
     if (annotationSource != null) {
-      return annotationSource.message(message);
+      return annotationSource!.message(message);
     }
     return message;
   }
diff --git a/source_gen/lib/src/utils.dart b/source_gen/lib/src/utils.dart
index 6c95f30..bb60d9a 100644
--- a/source_gen/lib/src/utils.dart
+++ b/source_gen/lib/src/utils.dart
@@ -2,10 +2,13 @@
 // 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 'dart:io';
+
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:build/build.dart';
 import 'package:path/path.dart' as p;
+import 'package:yaml/yaml.dart';
 
 /// Returns a non-null name for the provided [type].
 ///
@@ -38,8 +41,8 @@
 ///
 /// Returns `null` if [element] is missing identifier.
 String nameOfPartial(LibraryElement element, AssetId source) {
-  if (element.name != null && element.name.isNotEmpty) {
-    return element.name;
+  if (element.name != null && element.name!.isNotEmpty) {
+    return element.name!;
   }
 
   final sourceUrl = p.basename(source.uri.toString());
@@ -69,7 +72,7 @@
 String urlOfElement(Element element) => element.kind == ElementKind.DYNAMIC
     ? 'dart:core#dynamic'
     // using librarySource.uri – in case the element is in a part
-    : normalizeUrl(element.librarySource.uri)
+    : normalizeUrl(element.librarySource!.uri)
         .replace(fragment: element.name)
         .toString();
 
@@ -79,6 +82,8 @@
       return normalizeDartUrl(url);
     case 'package':
       return packageToAssetUrl(url);
+    case 'file':
+      return fileToAssetUrl(url);
     default:
       return url;
   }
@@ -94,6 +99,12 @@
     ? url.replace(pathSegments: url.pathSegments.take(1))
     : url;
 
+Uri fileToAssetUrl(Uri url) {
+  if (!p.isWithin(p.current, url.path)) return url;
+  return Uri(
+      scheme: 'asset', path: p.join(rootPackageName, p.relative(url.path)));
+}
+
 /// Returns a `package:` URL converted to a `asset:` URL.
 ///
 /// This makes internal comparison logic much easier, but still allows users
@@ -130,3 +141,13 @@
         ...url.pathSegments.skip(2),
       ])
     : url;
+
+final String rootPackageName = () {
+  final name = loadYaml(File('pubspec.yaml').readAsStringSync())['name'];
+  if (name is! String) {
+    throw StateError(
+        'Your pubspec.yaml file is missing a `name` field or it isn\'t '
+        'a String.');
+  }
+  return name;
+}();
diff --git a/source_gen/pubspec.yaml b/source_gen/pubspec.yaml
index 3ff1adb..184de57 100644
--- a/source_gen/pubspec.yaml
+++ b/source_gen/pubspec.yaml
@@ -1,26 +1,26 @@
 name: source_gen
-version: 0.9.10+4
+version: 1.0.2
 description: >-
   Source code generation builders and utilities for the Dart build system
 repository: https://github.com/dart-lang/source_gen
 
 environment:
-  sdk: ">=2.11.999 <3.0.0"
+  sdk: ">=2.12.0 <3.0.0"
 
 dependencies:
-  analyzer: '>=0.42.0 <2.0.0'
-  async: ^2.0.7
+  analyzer: ^1.7.0
+  async: ^2.5.0
   build: ^2.0.0
-  dart_style: ^1.0.0
-  glob: ">=1.1.0 <3.0.0"
-  meta: ^1.1.0
-  path: ^1.3.2
-  pedantic: ^1.0.0
-  source_span: ^1.4.0
+  dart_style: ^2.0.0
+  glob: ^2.0.0
+  meta: ^1.3.0
+  path: ^1.8.0
+  pedantic: ^1.10.0
+  source_span: ^1.8.0
+  yaml: ^3.0.0
 
 dev_dependencies:
   _test_annotations:
     path: ../_test_annotations
   build_test: ^2.0.0
-  test: ^1.0.0
-
+  test: ^1.16.0
diff --git a/source_map_stack_trace/BUILD.gn b/source_map_stack_trace/BUILD.gn
index 18b8d09..de55a4f 100644
--- a/source_map_stack_trace/BUILD.gn
+++ b/source_map_stack_trace/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for source_map_stack_trace-2.1.0
+# This file is generated by package_importer.py for source_map_stack_trace-2.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/source_maps/BUILD.gn b/source_maps/BUILD.gn
index cc4e3cc..26f3f86 100644
--- a/source_maps/BUILD.gn
+++ b/source_maps/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for source_maps-0.10.10
+# This file is generated by package_importer.py for source_maps-0.10.10
 
 import("//build/dart/dart_library.gni")
 
diff --git a/source_span/BUILD.gn b/source_span/BUILD.gn
index f4cd52e..68ba8ef 100644
--- a/source_span/BUILD.gn
+++ b/source_span/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for source_span-1.8.1
+# This file is generated by package_importer.py for source_span-1.8.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/sse/BUILD.gn b/sse/BUILD.gn
index c63da39..1143d1b 100644
--- a/sse/BUILD.gn
+++ b/sse/BUILD.gn
@@ -1,11 +1,11 @@
-# This file is generated by importer.py for sse-3.8.3
+# This file is generated by package_importer.py for sse-4.1.0
 
 import("//build/dart/dart_library.gni")
 
 dart_library("sse") {
   package_name = "sse"
 
-  language_version = "2.2"
+  language_version = "2.12"
 
   disable_analysis = true
 
@@ -13,6 +13,7 @@
     "//third_party/dart-pkg/pub/async",
     "//third_party/dart-pkg/pub/collection",
     "//third_party/dart-pkg/pub/logging",
+    "//third_party/dart-pkg/pub/pool",
     "//third_party/dart-pkg/pub/pedantic",
     "//third_party/dart-pkg/pub/stream_channel",
     "//third_party/dart-pkg/pub/shelf",
diff --git a/sse/CHANGELOG.md b/sse/CHANGELOG.md
index 50de15e..cd0dd52 100644
--- a/sse/CHANGELOG.md
+++ b/sse/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 4.1.0
+
+- Limit the number of concurrent requests to prevent Chrome from automatically
+  dropping them on the floor.
+
+## 4.0.0
+
+- Support null safety.
+
 ## 3.8.3
 
 - Require the latest shelf and remove dead code.
diff --git a/sse/LICENSE b/sse/LICENSE
index f75d7c2..a0d5f54 100644
--- a/sse/LICENSE
+++ b/sse/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2019, the Dart project authors. All rights reserved.
+Copyright 2019, the Dart project authors.
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/sse/lib/client/sse_client.dart b/sse/lib/client/sse_client.dart
index 00def10..3335346 100644
--- a/sse/lib/client/sse_client.dart
+++ b/sse/lib/client/sse_client.dart
@@ -7,15 +7,24 @@
 import 'dart:html';
 
 import 'package:logging/logging.dart';
+import 'package:pool/pool.dart';
 import 'package:stream_channel/stream_channel.dart';
 
 import '../src/util/uuid.dart';
 
+/// Limit for the number of concurrent outgoing requests.
+///
+/// Chrome drops outgoing requests on the floor after some threshold. To prevent
+/// these errors we buffer outgoing requests with a pool.
+///
+/// Note Chrome's limit is 6000. So this gives us plenty of headroom.
+final _requestPool = Pool(1000);
+
 /// A client for bi-directional sse communcation.
 ///
 /// The client can send any JSON-encodable messages to the server by adding
 /// them to the [sink] and listen to messages from the server on the [stream].
-class SseClient extends StreamChannelMixin<String> {
+class SseClient extends StreamChannelMixin<String?> {
   final _incomingController = StreamController<String>();
 
   final _outgoingController = StreamController<String>();
@@ -26,11 +35,11 @@
 
   int _lastMessageId = -1;
 
-  EventSource _eventSource;
+  late EventSource _eventSource;
 
-  String _serverUrl;
+  late String _serverUrl;
 
-  Timer _errorTimer;
+  Timer? _errorTimer;
 
   /// [serverUrl] is the URL under which the server is listening for
   /// incoming bi-directional SSE connections.
@@ -113,21 +122,23 @@
     close();
   }
 
-  void _onOutgoingMessage(String message) async {
-    String encodedMessage;
-    try {
-      encodedMessage = jsonEncode(message);
-    } on JsonUnsupportedObjectError catch (e) {
-      _logger.warning('Unable to encode outgoing message: $e');
-    } on ArgumentError catch (e) {
-      _logger.warning('Invalid argument: $e');
-    }
-    try {
-      await HttpRequest.request('$_serverUrl&messageId=${++_lastMessageId}',
-          method: 'POST', sendData: encodedMessage, withCredentials: true);
-    } catch (e) {
-      _logger.severe('Failed to send $message:\n $e');
-      close();
-    }
+  void _onOutgoingMessage(String? message) async {
+    String? encodedMessage;
+    await _requestPool.withResource(() async {
+      try {
+        encodedMessage = jsonEncode(message);
+      } on JsonUnsupportedObjectError catch (e) {
+        _logger.warning('Unable to encode outgoing message: $e');
+      } on ArgumentError catch (e) {
+        _logger.warning('Invalid argument: $e');
+      }
+      try {
+        await HttpRequest.request('$_serverUrl&messageId=${++_lastMessageId}',
+            method: 'POST', sendData: encodedMessage, withCredentials: true);
+      } catch (e) {
+        _logger.severe('Failed to send $message:\n $e');
+        close();
+      }
+    });
   }
 }
diff --git a/sse/lib/src/server/sse_handler.dart b/sse/lib/src/server/sse_handler.dart
index 8dec99e..d8a867e 100644
--- a/sse/lib/src/server/sse_handler.dart
+++ b/sse/lib/src/server/sse_handler.dart
@@ -13,12 +13,12 @@
 import 'package:stream_channel/stream_channel.dart';
 
 // RFC 2616 requires carriage return delimiters.
-String _sseHeaders(String origin) => 'HTTP/1.1 200 OK\r\n'
+String _sseHeaders(String? origin) => 'HTTP/1.1 200 OK\r\n'
     'Content-Type: text/event-stream\r\n'
     'Cache-Control: no-cache\r\n'
     'Connection: keep-alive\r\n'
-    'Access-Control-Allow-Credentials: true\r\n'
-    'Access-Control-Allow-Origin: $origin\r\n'
+    'Access-Control-Allow-Credentials: true\r\n' 
+    "${origin != null ? 'Access-Control-Allow-Origin: $origin\r\n'  : ''}"
     '\r\n\r\n';
 
 class _SseMessage {
@@ -28,7 +28,7 @@
 }
 
 /// A bi-directional SSE connection between server and browser.
-class SseConnection extends StreamChannelMixin<String> {
+class SseConnection extends StreamChannelMixin<String?> {
   /// Incoming messages from the Browser client.
   final _incomingController = StreamController<String>();
 
@@ -38,10 +38,10 @@
   Sink _sink;
 
   /// How long to wait after a connection drops before considering it closed.
-  final Duration _keepAlive;
+  final Duration? _keepAlive;
 
   /// A timer counting down the KeepAlive period (null if hasn't disconnected).
-  Timer _keepAliveTimer;
+  Timer? _keepAliveTimer;
 
   /// Whether this connection is currently in the KeepAlive timeout period.
   bool get isInKeepAlivePeriod => _keepAliveTimer?.isActive ?? false;
@@ -57,7 +57,7 @@
 
   /// Wraps the `_outgoingController.stream` to buffer events to enable keep
   /// alive.
-  StreamQueue _outgoingStreamQueue;
+  late StreamQueue _outgoingStreamQueue;
 
   /// Creates an [SseConnection] for the supplied [_sink].
   ///
@@ -67,7 +67,7 @@
   ///
   /// If [keepAlive] is not supplied, the connection will be closed immediately
   /// after a disconnect.
-  SseConnection(this._sink, {Duration keepAlive}) : _keepAlive = keepAlive {
+  SseConnection(this._sink, {Duration? keepAlive}) : _keepAlive = keepAlive {
     _outgoingStreamQueue = StreamQueue(_outgoingController.stream);
     unawaited(_setUpListener());
     _outgoingController.onCancel = _close;
@@ -154,7 +154,7 @@
       // been completely closed, set a timer to close after the timeout period.
       // If the connection comes back, this will be cancelled and all messages left
       // in the queue tried again.
-      _keepAliveTimer = Timer(_keepAlive, _close);
+      _keepAliveTimer = Timer(_keepAlive!, _close);
     }
   }
 
@@ -187,11 +187,11 @@
 class SseHandler {
   final _logger = Logger('SseHandler');
   final Uri _uri;
-  final Duration _keepAlive;
-  final _connections = <String, SseConnection>{};
+  final Duration? _keepAlive;
+  final _connections = <String?, SseConnection>{};
   final _connectionController = StreamController<SseConnection>();
 
-  StreamQueue<SseConnection> _connectionsStream;
+  StreamQueue<SseConnection>? _connectionsStream;
 
   /// [_uri] is the URL under which the server is listening for
   /// incoming bi-directional SSE connections.
@@ -203,7 +203,7 @@
   ///
   /// If [keepAlive] is not supplied, connections will be closed immediately
   /// after a disconnect.
-  SseHandler(this._uri, {Duration keepAlive}) : _keepAlive = keepAlive;
+  SseHandler(this._uri, {Duration? keepAlive}) : _keepAlive = keepAlive;
 
   StreamQueue<SseConnection> get connections =>
       _connectionsStream ??= StreamQueue(_connectionController.stream);
@@ -221,8 +221,8 @@
       // Check if we already have a connection for this ID that is in the process
       // of timing out (in which case we can reconnect it transparently).
       if (_connections[clientId] != null &&
-          _connections[clientId].isInKeepAlivePeriod) {
-        _connections[clientId]._acceptReconnection(sink);
+          _connections[clientId]!.isInKeepAlivePeriod) {
+        _connections[clientId]!._acceptReconnection(sink);
       } else {
         var connection = SseConnection(sink, keepAlive: _keepAlive);
         _connections[clientId] = connection;
@@ -280,7 +280,7 @@
   String _originFor(shelf.Request req) =>
       // Firefox does not set header "origin".
       // https://bugzilla.mozilla.org/show_bug.cgi?id=1508661
-      req.headers['origin'] ?? req.headers['host'];
+      req.headers['origin'] ?? req.headers['host']!;
 
   /// Immediately close all connections, ignoring any keepAlive periods.
   void shutdown() {
diff --git a/sse/pubspec.yaml b/sse/pubspec.yaml
index 0c67e5c..1c2b351 100644
--- a/sse/pubspec.yaml
+++ b/sse/pubspec.yaml
@@ -1,5 +1,5 @@
 name: sse
-version: 3.8.3
+version: 4.1.0
 homepage: https://github.com/dart-lang/sse
 description: >-
   Provides client and server functionality for setting up bi-directional
@@ -7,12 +7,13 @@
   requests.
 
 environment:
-  sdk: ">=2.2.0 <3.0.0"
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   async: ^2.0.8
   collection: ^1.0.0
   logging: '>=0.11.3+2 <2.0.0'
+  pool: ^1.5.0
   pedantic: ^1.4.0
   stream_channel: '>=1.6.8 <3.0.0'
   shelf: ^1.1.0
diff --git a/stack_trace/BUILD.gn b/stack_trace/BUILD.gn
index 5abfd01..ddc1e82 100644
--- a/stack_trace/BUILD.gn
+++ b/stack_trace/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for stack_trace-1.10.0
+# This file is generated by package_importer.py for stack_trace-1.10.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/stream_channel/BUILD.gn b/stream_channel/BUILD.gn
index 89fe8d6..047cd20 100644
--- a/stream_channel/BUILD.gn
+++ b/stream_channel/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for stream_channel-2.1.0
+# This file is generated by package_importer.py for stream_channel-2.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/string_scanner/BUILD.gn b/string_scanner/BUILD.gn
index 92150ee..f900dbe 100644
--- a/string_scanner/BUILD.gn
+++ b/string_scanner/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for string_scanner-1.1.0
+# This file is generated by package_importer.py for string_scanner-1.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/strings/BUILD.gn b/strings/BUILD.gn
index d730715..a8f5216 100644
--- a/strings/BUILD.gn
+++ b/strings/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for strings-0.1.4
+# This file is generated by package_importer.py for strings-0.1.4
 
 import("//build/dart/dart_library.gni")
 
diff --git a/sync_http/BUILD.gn b/sync_http/BUILD.gn
index 6cb8fd4..5fab192 100644
--- a/sync_http/BUILD.gn
+++ b/sync_http/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for sync_http-0.3.0
+# This file is generated by package_importer.py for sync_http-0.3.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/term_glyph/BUILD.gn b/term_glyph/BUILD.gn
index 0c4307a..a5b0f5f 100644
--- a/term_glyph/BUILD.gn
+++ b/term_glyph/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for term_glyph-1.2.0
+# This file is generated by package_importer.py for term_glyph-1.2.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 276db5e..a70793c 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for test-1.17.5
+# This file is generated by package_importer.py for test-1.17.8
 
 import("//build/dart/dart_library.gni")
 
diff --git a/test/CHANGELOG.md b/test/CHANGELOG.md
index 394f963..b79ca30 100644
--- a/test/CHANGELOG.md
+++ b/test/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 1.17.8
+
+* Update json reporter docs with updated nullability annotations and
+  descriptions.
+* Add `time` field to the json reporters `allSuites` event type so that all
+  event types can be unified.
+
+## 1.17.7
+
+* Support the latest `test_core`.
+
+## 1.17.6
+
+* Give a better error when `printOnFailure` is called from outside a test
+  zone.
+
 ## 1.17.5
 
 * Support the latest vm_service release (`7.0.0`).
diff --git a/test/README.md b/test/README.md
index fd26947..aec58e0 100644
--- a/test/README.md
+++ b/test/README.md
@@ -29,8 +29,9 @@
 Tests are specified using the top-level [`test()`] function, and test assertions
 are made using [`expect()`]:
 
-[`test()`]: https://pub.dev/documentation/test_core/latest/test_core/test.html
-[`expect()`]: https://pub.dev/documentation/test_api/latest/test_api/expect.html
+[`test()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/test.html
+
+[`expect()`]: https://pub.dev/documentation/test_api/latest/expect/expect.html
 
 ```dart
 import 'package:test/test.dart';
@@ -51,7 +52,7 @@
 Tests can be grouped together using the [`group()`] function. Each group's
 description is added to the beginning of its test's descriptions.
 
-[`group()`]: https://pub.dev/documentation/test_api/latest/test_api/group.html
+[`group()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/group.html
 
 ```dart
 import 'package:test/test.dart';
@@ -103,6 +104,10 @@
 You can also test exceptions with the [`throwsA()`] function or a matcher
 such as [`throwsException`]:
 
+[`throwsA()`]: https://pub.dev/documentation/test_api/latest/expect/throwsA.html
+
+[`throwsFormatException`]: https://pub.dev/documentation/test_api/latest/expect/throwsFormatException-constant.html
+
 ```dart
 import 'package:test/test.dart';
 
@@ -139,8 +144,9 @@
 }
 ```
 
-[`setUp()`]: https://pub.dev/documentation/test_api/latest/test_api/setUp.html
-[`tearDown()`]: https://pub.dev/documentation/test_api/latest/test_api/tearDown.html
+[`setUp()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/setUp.html
+
+[`tearDown()`]: https://pub.dev/documentation/test_core/latest/test_core.scaffolding/tearDown.html
 
 ## Running Tests
 
@@ -185,6 +191,7 @@
 ```
 
 ### Shuffling Tests
+
 Test order can be shuffled with the `--test-randomize-ordering-seed` argument.
 This allows you to shuffle your tests with a specific seed (deterministic) or
 a random seed for each run. For example, consider the following test runs:
@@ -198,6 +205,7 @@
 specifying it at all, meaning the test order will remain as-is.
 
 ### Collecting Code Coverage
+
 To collect code coverage, you can run tests with the `--coverage <directory>`
 argument. The directory specified can be an absolute or relative path.
 If a directory does not exist at the path specified, a directory will be
@@ -252,6 +260,7 @@
 only supports boolean operations. The following identifiers are defined:
 
 [boolean selector syntax]: https://github.com/dart-lang/boolean_selector/blob/master/README.md
+
 [`boolean_selector`]: https://pub.dev/packages/boolean_selector
 
 * `vm`: Whether the test is running on the command-line Dart VM.
@@ -280,7 +289,7 @@
 * `windows`: Whether the test is running on Windows. This can only be `true` if
   either `vm` or `node` is true.
 
-* `mac-os`: Whether the test is running on Mac OS. This can only be `true` if
+* `mac-os`: Whether the test is running on MacOS. This can only be `true` if
   either `vm` or `node` is true.
 
 * `linux`: Whether the test is running on Linux. This can only be `true` if
@@ -309,6 +318,7 @@
 meant to be used by JavaScript code.
 
 [Node.js]: https://nodejs.org/en/
+
 [`js`]: https://pub.dev/packages/js
 
 The test runner looks for an executable named `node` (on Mac OS or Linux) or
@@ -354,7 +364,6 @@
 
 [early-handler]:https://dart.dev/guides/libraries/futures-error-handling#potential-problem-failing-to-register-error-handlers-early
 
-
 ### Future Matchers
 
 There are a number of useful functions and matchers for more advanced
@@ -362,7 +371,7 @@
 ensures that the test doesn't finish until the `Future` completes, and runs a
 matcher against that `Future`'s value.
 
-[`completion()`]: https://pub.dev/documentation/test_api/latest/test_api/completion.html
+[`completion()`]: https://pub.dev/documentation/test_api/latest/expect/completion.html
 
 ```dart
 import 'dart:async';
@@ -376,11 +385,11 @@
 }
 ```
 
-The [`throwsA()`] matcher and the various `throwsExceptionType` matchers work
+The [`throwsA()`] matcher and the various [`throwsExceptionType`] matchers work
 with both synchronous callbacks and asynchronous `Future`s. They ensure that a
 particular type of exception is thrown:
 
-[`throwsA()`]: https://pub.dev/documentation/test_api/latest/test_api/throwsA.html
+[`throwsExceptionType`]: https://pub.dev/documentation/test_api/latest/expect/throwsException-constant.html
 
 ```dart
 import 'dart:async';
@@ -468,6 +477,7 @@
 which can only have one subscriber. For example:
 
 [`async`]: https://pub.dev/packages/async
+
 [`StreamQueue`]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html
 
 ```dart
@@ -518,14 +528,23 @@
 You can also define your own custom stream matchers with [`StreamMatcher()`].
 
 [`emits()`]: https://pub.dev/documentation/test_api/latest/test_api/emits.html
+
 [`emitsError()`]: https://pub.dev/documentation/test_api/latest/test_api/emitsError.html
+
 [`emitsDone`]: https://pub.dev/documentation/test_api/latest/test_api/emitsDone.html
+
 [`mayEmit()`]: https://pub.dev/documentation/test_api/latest/test_api/mayEmit.html
+
 [`mayEmitMultiple()`]: https://pub.dev/documentation/test_api/latest/test_api/mayEmitMultiple.html
+
 [`emitsAnyOf()`]: https://pub.dev/documentation/test_api/latest/test_api/emitsAnyOf.html
+
 [`emitsInOrder()`]: https://pub.dev/documentation/test_api/latest/test_api/emitsInOrder.html
+
 [`emitsInAnyOrder()`]: https://pub.dev/documentation/test_api/latest/test_api/emitsInAnyOrder.html
+
 [`neverEmits()`]: https://pub.dev/documentation/test_api/latest/test_api/neverEmits.html
+
 [`StreamMatcher()`]: https://pub.dev/documentation/test_api/latest/test_api/StreamMatcher-class.html
 
 ## Running Tests With Custom HTML
@@ -535,7 +554,7 @@
 files have three requirements:
 
 * They must have the same name as the test, with `.dart` replaced by `.html`. You can also
-  provide a configuration path to an html file if you want it to be reused across all tests.
+  provide a configuration path to an HTML file if you want it to be reused across all tests.
   See [Providing a custom HTML template](#providing-a-custom-html-template) below.
 
 * They must contain a `link` tag with `rel="x-dart-test"` and an `href`
@@ -594,16 +613,15 @@
 </html>
 ```
 
-
 ## Configuring Tests
 
 ### Skipping Tests
 
-If a test, group, or entire suite isn't working yet and you just want it to stop
+If a test, group, or entire suite isn't working yet, and you just want it to stop
 complaining, you can mark it as "skipped". The test or tests won't be run, and,
 if you supply a reason why, that reason will be printed. In general, skipping
 tests indicates that they should run but is temporarily not working. If they're
-is fundamentally incompatible with a platform, [`@TestOn`/`testOn`][TestOn]
+fundamentally incompatible with a platform, [`@TestOn`/`testOn`][TestOn]
 should be used instead.
 
 [TestOn]: #restricting-tests-to-certain-platforms
@@ -624,7 +642,8 @@
 include it, but it's a good idea to document why the test isn't running.
 
 Groups and individual tests can be skipped by passing the `skip` parameter. This
-can be either `true` or a String describing why the test is skipped. For example:
+can be either `true` or a String describing why the test is skipped. For
+example:
 
 ```dart
 import 'package:test/test.dart';
@@ -675,7 +694,7 @@
 
     test('even slower test', () {
       // ...
-    }, timeout: Timeout.factor(2))
+    }, timeout: Timeout.factor(2));
   }, timeout: Timeout(Duration(minutes: 1)));
 }
 ```
@@ -710,7 +729,7 @@
 ```
 
 Both the annotation and the parameter take a map. The map's keys are [platform
-selectors](#platform-selector-syntax) which describe the platforms for which the
+selectors](#platform-selectors) which describe the platforms for which the
 specialized configuration applies. Its values are instances of some of the same
 annotation classes that can be used for a suite: `Skip` and `Timeout`. A value
 can also be a list of these values.
@@ -796,17 +815,17 @@
 
 The test runner does not support general purpose flags to control compilation
 such as `-D` defines or flags like `--no-sound-null-safety`. In most cases it is
-preferable to avoid writing tests that depend on the fine grained compiler
+preferable to avoid writing tests that depend on the fine-grained compiler
 configuration. For instance to choose between sound and unsound null safety,
 prefer to choose a language version for each test which has the desired behavior
 by default - choose a language version below `2.12` to disable sound null
 safety, and a language version above `2.12` to enable sound null safety. When
-fine grained configuration is unavoidable the approach varies by platform.
+fine-grained configuration is unavoidable, the approach varies by platform.
 
 Compilation for browser and node tests can be configured by passing arguments to
 `dart2js` with `--dart2js-args` options.
 
-Fine grained compilation configuration is not supported for the VM. Any
+Fine-grained compilation configuration is not supported for the VM. Any
 configuration which impacts runtime behavior for the entire VM, such as `-D`
 defines (when used for non-const values) and runtime behavior experiments, will
 influence both the test runner and the isolates spawned to run test suites.
@@ -856,9 +875,12 @@
 `spawnHybridUri()` takes a URL. They both return a [`StreamChannel`] that
 communicates with the hybrid isolate. For example:
 
-[`spawnHybridCode()`]: https://pub.dev/documentation/test_api/latest/test_api/spawnHybridCode.html
-[`spawnHybridUri()`]: https://pub.dev/documentation/test_api/latest/test_api/spawnHybridUri.html
+[`spawnHybridCode()`]: https://pub.dev/documentation/test_api/latest/test_api.scaffolding/spawnHybridCode.html
+
+[`spawnHybridUri()`]: https://pub.dev/documentation/test_api/latest/test_api.scaffolding/spawnHybridUri.html
+
 [dart:isolate]: https://api.dart.dev/stable/dart-isolate/dart-isolate-library.html
+
 [`StreamChannel`]: https://pub.dev/documentation/stream_channel/latest/stream_channel/StreamChannel-class.html
 
 ```dart
diff --git a/test/doc/json_reporter.md b/test/doc/json_reporter.md
index 9cb4202..72d0276 100644
--- a/test/doc/json_reporter.md
+++ b/test/doc/json_reporter.md
@@ -99,7 +99,9 @@
   String protocolVersion;
 
   // The version of the test runner being used.
-  String runnerVersion;
+  //
+  // This is null if for some reason the version couldn't be loaded.
+  String? runnerVersion;
 
   // The pid of the VM process running the tests.
   int pid;
@@ -112,7 +114,7 @@
 ### AllSuitesEvent
 
 ```
-class AllSuitesEvent {
+class AllSuitesEvent extends Event {
   String type = "allSuites";
 
   /// The total number of suites that will be loaded.
@@ -151,11 +153,11 @@
 
   /// The HTTP URL for the Dart Observatory, or `null` if the Observatory isn't
   /// available for this suite.
-  String observatory;
+  String? observatory;
 
   /// The HTTP URL for the remote debugger for this suite's host page, or `null`
   /// if no remote debugger is available for this suite.
-  String remoteDebugger;
+  String? remoteDebugger;
 }
 ```
 
@@ -316,7 +318,10 @@
   String type = "done";
 
   // Whether all tests succeeded (or were skipped).
-  bool success;
+  //
+  // Will be `null` if the test runner was close before all tests completed
+  // running.
+  bool? success;
 }
 ```
 
@@ -343,30 +348,30 @@
   List<int> groupIDs;
 
   // The (1-based) line on which the test was defined, or `null`.
-  int line;
+  int? line;
 
   // The (1-based) column on which the test was defined, or `null`.
-  int column;
+  int? column;
 
   // The URL for the file in which the test was defined, or `null`.
-  String url;
+  String? url;
 
   // The (1-based) line in the original test suite from which the test
   // originated.
   //
   // Will only be present if `root_url` is different from `url`.
-  int root_line;
+  int? root_line;
 
   // The (1-based) line on in the original test suite from which the test
   // originated.
   //
   // Will only be present if `root_url` is different from `url`.
-  int root_column;
+  int? root_column;
 
   // The URL for the original test suite in which the test was defined.
   //
   // Will only be present if different from `url`.
-  String root_url;
+  String? root_url;
 
   // This field is deprecated and should not be used.
   Metadata metadata;
@@ -394,10 +399,10 @@
   int id;
 
   // The platform on which the suite is running.
-  String? platform;
+  String platform;
 
-  // The path to the suite's file.
-  String path;
+  // The path to the suite's file, or `null` if that path is unknown.
+  String? path;
 }
 ```
 
@@ -418,7 +423,7 @@
   int id;
 
   // The name of the group, including prefixes from any containing groups.
-  String? name;
+  String name;
 
   // The ID of the suite containing this group.
   int suiteID;
@@ -430,13 +435,13 @@
   int testCount;
 
   // The (1-based) line on which the group was defined, or `null`.
-  int line;
+  int? line;
 
   // The (1-based) column on which the group was defined, or `null`.
-  int column;
+  int? column;
 
   // The URL for the file in which the group was defined, or `null`.
-  String url;
+  String? url;
 
   // This field is deprecated and should not be used.
   Metadata metadata;
@@ -460,6 +465,8 @@
 ```
 class Metadata {
   bool skip;
+
+  // The reason the tests was skipped, or `null` if it wasn't skipped.
   String? skipReason;
 }
 ```
diff --git a/test/pubspec.yaml b/test/pubspec.yaml
index 7c28f95..6cbc07d 100644
--- a/test/pubspec.yaml
+++ b/test/pubspec.yaml
@@ -1,5 +1,5 @@
 name: test
-version: 1.17.5
+version: 1.17.8
 description: >-
   A full featured library for writing and running Dart tests across platforms.
 repository: https://github.com/dart-lang/test/blob/master/pkgs/test
@@ -33,8 +33,8 @@
   webkit_inspection_protocol: ^1.0.0
   yaml: ^3.0.0
   # Use an exact version until the test_api and test_core package are stable.
-  test_api: 0.4.0
-  test_core: 0.3.25
+  test_api: 0.4.1
+  test_core: 0.3.28
 
 dev_dependencies:
   fake_async: ^1.0.0
diff --git a/test_api/BUILD.gn b/test_api/BUILD.gn
index 0e936b0..241b0bd 100644
--- a/test_api/BUILD.gn
+++ b/test_api/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for test_api-0.4.0
+# This file is generated by package_importer.py for test_api-0.4.1
 
 import("//build/dart/dart_library.gni")
 
diff --git a/test_api/CHANGELOG.md b/test_api/CHANGELOG.md
index a34b1ab..a10bf24 100644
--- a/test_api/CHANGELOG.md
+++ b/test_api/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.4.1
+
+* Give a better error when `printOnFailure` is called from outside a test
+  zone.
+
 ## 0.4.0
 
 * Add libraries `scaffolding.dart`, and `expect.dart` to allow importing as
diff --git a/test_api/lib/src/backend/configuration/retry.dart b/test_api/lib/src/backend/configuration/retry.dart
index 681fcce..b813526 100644
--- a/test_api/lib/src/backend/configuration/retry.dart
+++ b/test_api/lib/src/backend/configuration/retry.dart
@@ -6,13 +6,13 @@
 
 /// An annotation for marking a test suite to be retried.
 ///
-/// A test with retries enabled will be re-run if it fails for a reason
-/// other than [TestFailure].
+/// A suite-level retry configuration will enable retries for every test in the
+/// suite, unless the group or test is configured with a more specific retry.
 @Target({TargetKind.library})
 class Retry {
-  /// The number of times the test will be retried.
+  /// The number of times the tests in the suite will be retried.
   final int count;
 
-  /// Marks a test to be retried.
+  /// Marks all tests in a test suite to be retried.
   const Retry(this.count);
 }
diff --git a/test_api/lib/src/scaffolding/utils.dart b/test_api/lib/src/scaffolding/utils.dart
index 1f2fa5d..d620775 100644
--- a/test_api/lib/src/scaffolding/utils.dart
+++ b/test_api/lib/src/scaffolding/utils.dart
@@ -33,7 +33,15 @@
 /// without cluttering the output for successful tests. Note that unlike
 /// [print], each individual message passed to [printOnFailure] will be
 /// separated by a blank line.
-void printOnFailure(String message) => Invoker.current!.printOnFailure(message);
+void printOnFailure(String message) {
+  var invoker = Invoker.current;
+  if (invoker == null) {
+    throw StateError(
+        'There is no current invoker. Please make sure that you are making the '
+        'call inside a test zone.');
+  }
+  return invoker.printOnFailure(message);
+}
 
 /// Marks the current test as skipped.
 ///
diff --git a/test_api/mono_pkg.yaml b/test_api/mono_pkg.yaml
index 8275ff9..d4cee91 100644
--- a/test_api/mono_pkg.yaml
+++ b/test_api/mono_pkg.yaml
@@ -12,3 +12,6 @@
   - unit_test:
     - group:
       - command: pub run test --preset travis -x browser
+        os:
+        - linux
+        - windows
diff --git a/test_api/pubspec.yaml b/test_api/pubspec.yaml
index 98439eb..544ca23 100644
--- a/test_api/pubspec.yaml
+++ b/test_api/pubspec.yaml
@@ -1,5 +1,5 @@
 name: test_api
-version: 0.4.0
+version: 0.4.1
 description: A library for writing Dart tests.
 homepage: https://github.com/dart-lang/test/blob/master/pkgs/test_api
 
diff --git a/test_core/BUILD.gn b/test_core/BUILD.gn
index 87098c8..40b4d7d 100644
--- a/test_core/BUILD.gn
+++ b/test_core/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for test_core-0.3.25
+# This file is generated by package_importer.py for test_core-0.3.28
 
 import("//build/dart/dart_library.gni")
 
diff --git a/test_core/CHANGELOG.md b/test_core/CHANGELOG.md
index f2d438d..a7fc460 100644
--- a/test_core/CHANGELOG.md
+++ b/test_core/CHANGELOG.md
@@ -1,3 +1,17 @@
+## 0.3.28
+
+* Add `time` field to the json reporters `allSuites` event type so that all
+  event types can be unified.
+
+## 0.3.27
+
+* Restore the `Configuration.loadFromString` constructor.
+
+## 0.3.26
+
+* Give a better error when `printOnFailure` is called from outside a test
+  zone.
+
 ## 0.3.25
 
 * Support the latest vm_service release (`7.0.0`).
diff --git a/test_core/lib/src/runner/configuration.dart b/test_core/lib/src/runner/configuration.dart
index cdf1f75..6bb3c35 100644
--- a/test_core/lib/src/runner/configuration.dart
+++ b/test_core/lib/src/runner/configuration.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:io';
 
 import 'package:boolean_selector/boolean_selector.dart';
 import 'package:collection/collection.dart';
@@ -202,28 +203,31 @@
   /// Throws a [FormatException] if [args] are invalid.
   factory Configuration.parse(List<String> arguments) => args.parse(arguments);
 
-  /// Loads the configuration from [path].
+  /// Loads configuration from [path].
   ///
-  /// If [global] is `true`, this restricts the configuration file to only rules
-  /// that are supported globally.
+  /// If [global] is `true`, this restricts the configuration to rules that are
+  /// supported globally.
   ///
   /// Throws an [IOException] if [path] does not exist or cannot be read. Throws
-  /// a [FormatException] if its contents are invalid.
-  factory Configuration.load(String path, {bool global = false}) =>
-      load(path, global: global);
+  /// a [FormatException] if the file contents are invalid.
+  factory Configuration.load(String path, {bool global = false}) {
+    final content = File(path).readAsStringSync();
+    final sourceUrl = p.toUri(path);
+    return parse(content, global: global, sourceUrl: sourceUrl);
+  }
 
-  /// Loads the configuration from YAML formatted [source].
+  /// Parses configuration from YAML formatted [content].
   ///
-  /// If [global] is `true`, this restricts the configuration file to only rules
-  /// that are supported globally.
+  /// If [global] is `true`, this restricts the configuration to rules that are
+  /// supported globally.
   ///
-  /// If [sourceUrl] is provided then that will be set as the source url for
-  /// the yaml document.
+  /// If [sourceUrl] is provided it will be set as the source url for the yaml
+  /// document.
   ///
-  /// Throws a [FormatException] if its contents are invalid.
-  factory Configuration.loadFromString(String source,
+  /// Throws a [FormatException] if the content is invalid.
+  factory Configuration.loadFromString(String content,
           {bool global = false, Uri? sourceUrl}) =>
-      loadFromString(source, global: global, sourceUrl: sourceUrl);
+      parse(content, global: global, sourceUrl: sourceUrl);
 
   factory Configuration(
       {bool? help,
diff --git a/test_core/lib/src/runner/configuration/load.dart b/test_core/lib/src/runner/configuration/load.dart
index 18b741a..57084e1 100644
--- a/test_core/lib/src/runner/configuration/load.dart
+++ b/test_core/lib/src/runner/configuration/load.dart
@@ -24,7 +24,6 @@
 import '../runtime_selection.dart';
 import '../suite.dart';
 import 'custom_runtime.dart';
-import 'load.dart' as self;
 import 'reporters.dart';
 import 'runtime_settings.dart';
 
@@ -41,15 +40,7 @@
 final _packageName =
     RegExp('^${_identifierRegExp.pattern}(\\.${_identifierRegExp.pattern})*\$');
 
-/// Loads configuration information from a YAML file at [path].
-///
-/// See [loadFromString] for further documentation.
-Configuration load(String path, {bool global = false}) {
-  var source = File(path).readAsStringSync();
-  return loadFromString(source, global: global, sourceUrl: p.toUri(path));
-}
-
-/// Loads configuration information from YAML formatted [source].
+/// Parses configuration from YAML formatted [content].
 ///
 /// If [global] is `true`, this restricts the configuration file to only rules
 /// that are supported globally.
@@ -58,19 +49,18 @@
 /// the yaml document.
 ///
 /// Throws a [FormatException] if the configuration is invalid.
-Configuration loadFromString(String source,
-    {bool global = false, Uri? sourceUrl}) {
-  var document = loadYamlNode(source, sourceUrl: sourceUrl);
+Configuration parse(String content, {Uri? sourceUrl, bool global = false}) {
+  var document = loadYamlNode(content, sourceUrl: sourceUrl);
 
   if (document.value == null) return Configuration.empty;
 
   if (document is! Map) {
     throw SourceSpanFormatException(
-        'The configuration must be a YAML map.', document.span, source);
+        'The configuration must be a YAML map.', document.span, content);
   }
 
   var loader =
-      _ConfigurationLoader(document as YamlMap, source, global: global);
+      _ConfigurationLoader(document as YamlMap, content, global: global);
   return loader.load();
 }
 
@@ -116,7 +106,7 @@
     var basePath =
         p.join(p.dirname(p.fromUri(_document.span.sourceUrl)), includePath);
     try {
-      return self.load(basePath);
+      return Configuration.load(basePath);
     } on FileSystemException catch (error) {
       throw SourceSpanFormatException(
           getErrorMessage(error), includeNode.span, _source);
diff --git a/test_core/lib/src/runner/engine.dart b/test_core/lib/src/runner/engine.dart
index 2973d76..06f58db 100644
--- a/test_core/lib/src/runner/engine.dart
+++ b/test_core/lib/src/runner/engine.dart
@@ -129,17 +129,6 @@
   Stream<RunnerSuite> get onSuiteAdded => _onSuiteAddedController.stream;
   final _onSuiteAddedController = StreamController<RunnerSuite>.broadcast();
 
-  /// All the currently-known suites that have run or are running.
-  ///
-  /// These are [LiveSuite]s, representing the in-progress state of each suite
-  /// as its component tests are being run.
-  ///
-  /// Note that unlike [addedSuites], for suites that are loaded using
-  /// [LoadSuite]s, both the [LoadSuite] and the suite it loads will eventually
-  /// be in this set.
-  Set<LiveSuite> get liveSuites => UnmodifiableSetView(_liveSuites);
-  final _liveSuites = <LiveSuite>{};
-
   /// A broadcast stream that emits each [LiveSuite] as it's loaded.
   ///
   /// Note that unlike [onSuiteAdded], for suites that are loaded using
@@ -491,7 +480,6 @@
   /// Add [liveSuite] and the information it exposes to the engine's
   /// informational streams and collections.
   void _addLiveSuite(LiveSuite liveSuite) {
-    _liveSuites.add(liveSuite);
     _onSuiteStartedController.add(liveSuite);
 
     _onTestStartedGroup.add(liveSuite.onTestStarted);
diff --git a/test_core/lib/src/runner/live_suite.dart b/test_core/lib/src/runner/live_suite.dart
index 6be0ab6..771655d 100644
--- a/test_core/lib/src/runner/live_suite.dart
+++ b/test_core/lib/src/runner/live_suite.dart
@@ -17,17 +17,6 @@
   /// The suite that's being run.
   RunnerSuite get suite;
 
-  /// Whether the suite has completed.
-  ///
-  /// Note that even if this returns `true`, the suite may still be running code
-  /// asynchronously. A suite is considered complete once all of its tests are
-  /// complete, but it's possible for a test to continue running even after it's
-  /// been marked complete—see [LiveTest.isComplete] for details.
-  ///
-  /// The [isClosed] getter can be used to determine whether the suite and its
-  /// tests are guaranteed to emit no more events.
-  bool get isComplete;
-
   /// A [Future] that completes once the suite is complete.
   ///
   /// Note that even once this completes, the suite may still be running code
diff --git a/test_core/lib/src/runner/live_suite_controller.dart b/test_core/lib/src/runner/live_suite_controller.dart
index f11b53d..b21568a 100644
--- a/test_core/lib/src/runner/live_suite_controller.dart
+++ b/test_core/lib/src/runner/live_suite_controller.dart
@@ -22,9 +22,6 @@
   RunnerSuite get suite => _controller._suite;
 
   @override
-  bool get isComplete => _controller._isComplete;
-
-  @override
   Future get onComplete => _controller._onCompleteGroup.future;
 
   @override
@@ -62,8 +59,7 @@
 /// down, [close] should be called.
 class LiveSuiteController {
   /// The [LiveSuite] being controlled.
-  LiveSuite get liveSuite => _liveSuite;
-  late final LiveSuite _liveSuite;
+  late final liveSuite = _LiveSuite(this);
 
   /// The suite that's being run.
   final RunnerSuite _suite;
@@ -73,9 +69,6 @@
   /// This contains all the futures from tests that are run in this suite.
   final _onCompleteGroup = FutureGroup();
 
-  /// Whether [_onCompleteGroup]'s future has fired.
-  var _isComplete = false;
-
   /// The completer that backs [LiveSuite.onClose].
   ///
   /// This is completed when the live suite is closed.
@@ -103,13 +96,7 @@
   /// Once this is called, the controller assumes responsibility for closing the
   /// suite. The caller should call [LiveSuiteController.close] rather than
   /// calling [RunnerSuite.close] directly.
-  LiveSuiteController(this._suite) {
-    _liveSuite = _LiveSuite(this);
-
-    _onCompleteGroup.future.then((_) {
-      _isComplete = true;
-    }, onError: (_) {});
-  }
+  LiveSuiteController(this._suite);
 
   /// Reports the status of [liveTest] through [liveSuite].
   ///
diff --git a/test_core/lib/src/runner/load_suite.dart b/test_core/lib/src/runner/load_suite.dart
index 15dbf18..92640bb 100644
--- a/test_core/lib/src/runner/load_suite.dart
+++ b/test_core/lib/src/runner/load_suite.dart
@@ -5,7 +5,6 @@
 import 'dart:async';
 
 import 'package:stack_trace/stack_trace.dart';
-import 'package:stream_channel/stream_channel.dart';
 import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
 import 'package:test_api/src/backend/invoker.dart'; // ignore: implementation_imports
 import 'package:test_api/src/backend/metadata.dart'; // ignore: implementation_imports
@@ -205,10 +204,6 @@
   }
 
   @override
-  StreamChannel channel(String name) =>
-      throw UnsupportedError('LoadSuite.channel() is not supported.');
-
-  @override
   Future close() async {}
 
   @override
diff --git a/test_core/lib/src/runner/reporter/json.dart b/test_core/lib/src/runner/reporter/json.dart
index c16c420..3fcb649 100644
--- a/test_core/lib/src/runner/reporter/json.dart
+++ b/test_core/lib/src/runner/reporter/json.dart
@@ -76,7 +76,10 @@
     _subscriptions.add(_engine.success.asStream().listen(_onDone));
 
     _subscriptions.add(_engine.onSuiteAdded.listen(null, onDone: () {
-      _emit('allSuites', {'count': _engine.addedSuites.length});
+      _emit('allSuites', {
+        'count': _engine.addedSuites.length,
+        'time': _stopwatch.elapsed.inMilliseconds
+      });
     }));
 
     _emit('start',
@@ -126,7 +129,7 @@
     // Don't emit groups for load suites. They're always empty and they provide
     // unnecessary clutter.
     var groupIDs = liveTest.suite is LoadSuite
-        ? []
+        ? <int>[]
         : _idsForGroups(liveTest.groups, liveTest.suite);
 
     var suiteConfig = _configFor(liveTest.suite);
diff --git a/test_core/lib/src/runner/runner_suite.dart b/test_core/lib/src/runner/runner_suite.dart
index fc992ef..9e9b917 100644
--- a/test_core/lib/src/runner/runner_suite.dart
+++ b/test_core/lib/src/runner/runner_suite.dart
@@ -44,14 +44,6 @@
   /// The event is `true` when debugging starts and `false` when it ends.
   Stream<bool> get onDebugging => _controller._onDebuggingController.stream;
 
-  /// Returns a channel that communicates with the remote suite.
-  ///
-  /// This connects to a channel created by code in the test worker calling the
-  /// `suiteChannel` argument from a `beforeLoad` callback to `serializeSuite`
-  /// with the same name.
-  /// It can be used used to send and receive any JSON-serializable object.
-  StreamChannel channel(String name) => _controller.channel(name);
-
   /// A shortcut constructor for creating a [RunnerSuite] that never goes into
   /// debugging mode and doesn't support suite channels.
   factory RunnerSuite(Environment environment, SuiteConfiguration config,
diff --git a/test_core/pubspec.yaml b/test_core/pubspec.yaml
index f44db4b..2d41cc5 100644
--- a/test_core/pubspec.yaml
+++ b/test_core/pubspec.yaml
@@ -1,5 +1,5 @@
 name: test_core
-version: 0.3.25
+version: 0.3.28
 description: A basic library for writing tests and running them on the VM.
 homepage: https://github.com/dart-lang/test/blob/master/pkgs/test_core
 
@@ -31,4 +31,4 @@
   # matcher is tightly constrained by test_api
   matcher: any
   # Use an exact version until the test_api package is stable.
-  test_api: 0.4.0
+  test_api: 0.4.1
diff --git a/tuple/BUILD.gn b/tuple/BUILD.gn
index 3a00402..2b47f92 100644
--- a/tuple/BUILD.gn
+++ b/tuple/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for tuple-1.0.3
+# This file is generated by package_importer.py for tuple-1.0.3
 
 import("//build/dart/dart_library.gni")
 
diff --git a/typed_data/BUILD.gn b/typed_data/BUILD.gn
index 0d4e79a..86e16e6 100644
--- a/typed_data/BUILD.gn
+++ b/typed_data/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for typed_data-1.3.0
+# This file is generated by package_importer.py for typed_data-1.3.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/unicode/BUILD.gn b/unicode/BUILD.gn
index 057287c..e2ebfd2 100644
--- a/unicode/BUILD.gn
+++ b/unicode/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for unicode-0.2.4
+# This file is generated by package_importer.py for unicode-0.2.4
 
 import("//build/dart/dart_library.gni")
 
diff --git a/usage/BUILD.gn b/usage/BUILD.gn
index c4cdf84..9a3299d 100644
--- a/usage/BUILD.gn
+++ b/usage/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for usage-4.0.2
+# This file is generated by package_importer.py for usage-4.0.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/uuid/BUILD.gn b/uuid/BUILD.gn
index 8d1203d..278db2e 100644
--- a/uuid/BUILD.gn
+++ b/uuid/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for uuid-3.0.4
+# This file is generated by package_importer.py for uuid-3.0.4
 
 import("//build/dart/dart_library.gni")
 
diff --git a/vector_math/BUILD.gn b/vector_math/BUILD.gn
index e2b66a4..1001a04 100644
--- a/vector_math/BUILD.gn
+++ b/vector_math/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for vector_math-2.1.0
+# This file is generated by package_importer.py for vector_math-2.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/vm_service/BUILD.gn b/vm_service/BUILD.gn
index d389e65..3a891fc 100644
--- a/vm_service/BUILD.gn
+++ b/vm_service/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for vm_service-6.2.0
+# This file is generated by package_importer.py for vm_service-7.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/vm_service/CHANGELOG.md b/vm_service/CHANGELOG.md
index 2492753..1613626 100644
--- a/vm_service/CHANGELOG.md
+++ b/vm_service/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Changelog
 
+## 7.1.0
+- Update to version `3.46` of the spec.
+- Move `sourcePosition` properties into `ClassRef`, `FieldRef`, and `FuncRef`.
+
+## 7.0.0
+- *breaking bug fix*: Fixed issue where response parsing could fail for `Context`.
+- Add support for `setBreakpointState` RPC and updated `Breakpoint` class to include
+  `enabled` property.
+
 ## 6.2.0
 - Added support for `getHttpProfile` and `clearHttpProfile` `dart:io` service extensions.
 
diff --git a/vm_service/LICENSE b/vm_service/LICENSE
index de31e1a..633672a 100644
--- a/vm_service/LICENSE
+++ b/vm_service/LICENSE
@@ -1,4 +1,5 @@
-Copyright 2015, the Dart project authors. All rights reserved.
+Copyright 2015, the Dart project authors.
+
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
@@ -9,7 +10,7 @@
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.
-    * Neither the name of Google Inc. nor the names of its
+    * Neither the name of Google LLC nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without specific prior written permission.
 
diff --git a/vm_service/example/vm_service_assert.dart b/vm_service/example/vm_service_assert.dart
index 7122a12..f9f2d6e 100644
--- a/vm_service/example/vm_service_assert.dart
+++ b/vm_service/example/vm_service_assert.dart
@@ -136,6 +136,7 @@
   if (obj == "BreakpointAdded") return obj;
   if (obj == "BreakpointRemoved") return obj;
   if (obj == "BreakpointResolved") return obj;
+  if (obj == "BreakpointUpdated") return obj;
   if (obj == "Extension") return obj;
   if (obj == "GC") return obj;
   if (obj == "Inspect") return obj;
@@ -293,6 +294,7 @@
   assertNotNull(obj);
   assertString(obj.id!);
   assertInt(obj.breakpointNumber!);
+  assertBool(obj.enabled!);
   assertBool(obj.resolved!);
   if (obj.location is vms.SourceLocation) {
     assertSourceLocation(obj.location!);
@@ -315,6 +317,7 @@
   assertNotNull(obj);
   assertString(obj.id!);
   assertString(obj.name!);
+  assertLibraryRef(obj.library!);
   return obj;
 }
 
@@ -329,10 +332,10 @@
   assertNotNull(obj);
   assertString(obj.id!);
   assertString(obj.name!);
+  assertLibraryRef(obj.library!);
   assertBool(obj.isAbstract!);
   assertBool(obj.isConst!);
   assertBool(obj.traceAllocations!);
-  assertLibraryRef(obj.library!);
   assertListOfInstanceRef(obj.interfaces!);
   assertListOfFieldRef(obj.fields!);
   assertListOfFuncRef(obj.functions!);
diff --git a/vm_service/java/src/org/dartlang/vm/service/VmService.java b/vm_service/java/src/org/dartlang/vm/service/VmService.java
index 1e79f48..a60fd3e 100644
--- a/vm_service/java/src/org/dartlang/vm/service/VmService.java
+++ b/vm_service/java/src/org/dartlang/vm/service/VmService.java
@@ -81,7 +81,7 @@
   /**
    * The minor version number of the protocol supported by this client.
    */
-  public static final int versionMinor = 44;
+  public static final int versionMinor = 46;
 
   /**
    * The [addBreakpoint] RPC is used to add a breakpoint at a specific line of some script.
@@ -676,6 +676,18 @@
   }
 
   /**
+   * The [setBreakpointState] RPC allows for breakpoints to be enabled or disabled, without
+   * requiring for the breakpoint to be completely removed.
+   */
+  public void setBreakpointState(String isolateId, String breakpointId, boolean enable, BreakpointConsumer consumer) {
+    final JsonObject params = new JsonObject();
+    params.addProperty("isolateId", isolateId);
+    params.addProperty("breakpointId", breakpointId);
+    params.addProperty("enable", enable);
+    request("setBreakpointState", params, consumer);
+  }
+
+  /**
    * The [setExceptionPauseMode] RPC is used to control if an isolate pauses when an exception is
    * thrown.
    * @param mode An [ExceptionPauseMode] indicates how the isolate pauses when an exception is
@@ -819,6 +831,12 @@
         return;
       }
     }
+    if (consumer instanceof BreakpointConsumer) {
+      if (responseType.equals("Breakpoint")) {
+        ((BreakpointConsumer) consumer).received(new Breakpoint(json));
+        return;
+      }
+    }
     if (consumer instanceof ClearCpuSamplesConsumer) {
       if (responseType.equals("Sentinel")) {
         ((ClearCpuSamplesConsumer) consumer).received(new Sentinel(json));
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/Breakpoint.java b/vm_service/java/src/org/dartlang/vm/service/element/Breakpoint.java
index d16150d..5bbe887 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/Breakpoint.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/Breakpoint.java
@@ -35,6 +35,13 @@
   }
 
   /**
+   * Is this breakpoint enabled?
+   */
+  public boolean getEnabled() {
+    return getAsBoolean("enabled");
+  }
+
+  /**
    * Is this a breakpoint that was added synthetically as part of a step OverAsyncSuspension resume
    * command?
    *
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/ClassRef.java b/vm_service/java/src/org/dartlang/vm/service/element/ClassRef.java
index 3fe6c17..eb248b3 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/ClassRef.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/ClassRef.java
@@ -28,6 +28,29 @@
   }
 
   /**
+   * The library which contains this class.
+   */
+  public LibraryRef getLibrary() {
+    return new LibraryRef((JsonObject) json.get("library"));
+  }
+
+  /**
+   * The location of this class in the source code.
+   *
+   * Can return <code>null</code>.
+   */
+  public SourceLocation getLocation() {
+    JsonObject obj = (JsonObject) json.get("location");
+    if (obj == null) return null;
+    final String type = json.get("type").getAsString();
+    if ("Instance".equals(type) || "@Instance".equals(type)) {
+      final String kind = json.get("kind").getAsString();
+      if ("Null".equals(kind)) return null;
+    }
+    return new SourceLocation(obj);
+  }
+
+  /**
    * The name of this class.
    */
   public String getName() {
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/Context.java b/vm_service/java/src/org/dartlang/vm/service/element/Context.java
index 24ec945..a8bb5e4 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/Context.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/Context.java
@@ -40,7 +40,7 @@
    *
    * Can return <code>null</code>.
    */
-  public Context getParent() {
+  public ContextRef getParent() {
     JsonObject obj = (JsonObject) json.get("parent");
     if (obj == null) return null;
     final String type = json.get("type").getAsString();
@@ -48,7 +48,7 @@
       final String kind = json.get("kind").getAsString();
       if ("Null".equals(kind)) return null;
     }
-    return new Context(obj);
+    return new ContextRef(obj);
   }
 
   /**
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/Event.java b/vm_service/java/src/org/dartlang/vm/service/element/Event.java
index b28546b..ece89d6 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/Event.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/Event.java
@@ -64,6 +64,7 @@
    *  - BreakpointAdded
    *  - BreakpointRemoved
    *  - BreakpointResolved
+   *  - BreakpointUpdated
    *
    * Can return <code>null</code>.
    */
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/EventKind.java b/vm_service/java/src/org/dartlang/vm/service/element/EventKind.java
index 0c4b8a5..72b6aea 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/EventKind.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/EventKind.java
@@ -38,6 +38,11 @@
   BreakpointResolved,
 
   /**
+   * A breakpoint has been updated.
+   */
+  BreakpointUpdated,
+
+  /**
    * Event from dart:developer.postEvent.
    */
   Extension,
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/FieldRef.java b/vm_service/java/src/org/dartlang/vm/service/element/FieldRef.java
index 8b95b42..d50e2ff 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/FieldRef.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/FieldRef.java
@@ -37,6 +37,22 @@
   }
 
   /**
+   * The location of this field in the source code.
+   *
+   * Can return <code>null</code>.
+   */
+  public SourceLocation getLocation() {
+    JsonObject obj = (JsonObject) json.get("location");
+    if (obj == null) return null;
+    final String type = json.get("type").getAsString();
+    if ("Instance".equals(type) || "@Instance".equals(type)) {
+      final String kind = json.get("kind").getAsString();
+      if ("Null".equals(kind)) return null;
+    }
+    return new SourceLocation(obj);
+  }
+
+  /**
    * The name of this field.
    */
   public String getName() {
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/FuncRef.java b/vm_service/java/src/org/dartlang/vm/service/element/FuncRef.java
index 8315fc9..5ab2402 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/FuncRef.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/FuncRef.java
@@ -28,6 +28,22 @@
   }
 
   /**
+   * The location of this function in the source code.
+   *
+   * Can return <code>null</code>.
+   */
+  public SourceLocation getLocation() {
+    JsonObject obj = (JsonObject) json.get("location");
+    if (obj == null) return null;
+    final String type = json.get("type").getAsString();
+    if ("Instance".equals(type) || "@Instance".equals(type)) {
+      final String kind = json.get("kind").getAsString();
+      if ("Null".equals(kind)) return null;
+    }
+    return new SourceLocation(obj);
+  }
+
+  /**
    * The name of this function.
    */
   public String getName() {
diff --git a/vm_service/java/src/org/dartlang/vm/service/element/Timeline.java b/vm_service/java/src/org/dartlang/vm/service/element/Timeline.java
index 83cb382..a3e60a4 100644
--- a/vm_service/java/src/org/dartlang/vm/service/element/Timeline.java
+++ b/vm_service/java/src/org/dartlang/vm/service/element/Timeline.java
@@ -40,7 +40,7 @@
   }
 
   /**
-   * A list of timeline events. No order is guarenteed for these events; in particular, these
+   * A list of timeline events. No order is guaranteed for these events; in particular, these
    * events may be unordered with respect to their timestamps.
    */
   public ElementList<TimelineEvent> getTraceEvents() {
diff --git a/vm_service/java/version.properties b/vm_service/java/version.properties
index 452573e..9e3fb90 100644
--- a/vm_service/java/version.properties
+++ b/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.44
+version=3.46
diff --git a/vm_service/lib/src/dart_io_extensions.dart b/vm_service/lib/src/dart_io_extensions.dart
index 73b3b84..9e9faaf 100644
--- a/vm_service/lib/src/dart_io_extensions.dart
+++ b/vm_service/lib/src/dart_io_extensions.dart
@@ -485,7 +485,7 @@
   HttpProfileRequestData.buildErrorRequest({
     required this.error,
     required this.events,
-  })   : _connectionInfo = null,
+  })  : _connectionInfo = null,
         _contentLength = null,
         _cookies = [],
         _followRedirects = null,
diff --git a/vm_service/lib/src/vm_service.dart b/vm_service/lib/src/vm_service.dart
index 8b869fb..a460c69 100644
--- a/vm_service/lib/src/vm_service.dart
+++ b/vm_service/lib/src/vm_service.dart
@@ -26,7 +26,7 @@
         HeapSnapshotObjectNoData,
         HeapSnapshotObjectNullData;
 
-const String vmServiceVersion = '3.44.0';
+const String vmServiceVersion = '3.46.0';
 
 /// @optional
 const String optional = 'optional';
@@ -229,6 +229,7 @@
   'removeBreakpoint': const ['Success'],
   'requestHeapSnapshot': const ['Success'],
   'resume': const ['Success'],
+  'setBreakpointState': const ['Breakpoint'],
   'setExceptionPauseMode': const ['Success'],
   'setFlag': const ['Success', 'Error'],
   'setLibraryDebuggable': const ['Success'],
@@ -1008,6 +1009,18 @@
   Future<Success> resume(String isolateId,
       {/*StepOption*/ String? step, int? frameIndex});
 
+  /// The `setBreakpointState` RPC allows for breakpoints to be enabled or
+  /// disabled, without requiring for the breakpoint to be completely removed.
+  ///
+  /// If `isolateId` refers to an isolate which has exited, then the `Collected`
+  /// [Sentinel] is returned.
+  ///
+  /// The returned [Breakpoint] is the updated breakpoint with its new values.
+  ///
+  /// See [Breakpoint].
+  Future<Breakpoint> setBreakpointState(
+      String isolateId, String breakpointId, bool enable);
+
   /// The `setExceptionPauseMode` RPC is used to control if an isolate pauses
   /// when an exception is thrown.
   ///
@@ -1136,7 +1149,7 @@
   /// IsolateReload, ServiceExtensionAdded
   /// Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted,
   /// PauseException, PausePostRequest, Resume, BreakpointAdded,
-  /// BreakpointResolved, BreakpointRemoved, Inspect, None
+  /// BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
   /// GC | GC
   /// Extension | Extension
   /// Timeline | TimelineEvents, TimelineStreamsSubscriptionUpdate
@@ -1471,6 +1484,13 @@
             frameIndex: params['frameIndex'],
           );
           break;
+        case 'setBreakpointState':
+          response = await _serviceImplementation.setBreakpointState(
+            params!['isolateId'],
+            params['breakpointId'],
+            params['enable'],
+          );
+          break;
         case 'setExceptionPauseMode':
           response = await _serviceImplementation.setExceptionPauseMode(
             params!['isolateId'],
@@ -1667,7 +1687,7 @@
   // IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, IsolateReload, ServiceExtensionAdded
   Stream<Event> get onIsolateEvent => _getEventController('Isolate').stream;
 
-  // PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, Inspect, None
+  // PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
   Stream<Event> get onDebugEvent => _getEventController('Debug').stream;
 
   // GC
@@ -1982,6 +2002,15 @@
       });
 
   @override
+  Future<Breakpoint> setBreakpointState(
+          String isolateId, String breakpointId, bool enable) =>
+      _call('setBreakpointState', {
+        'isolateId': isolateId,
+        'breakpointId': breakpointId,
+        'enable': enable
+      });
+
+  @override
   Future<Success> setExceptionPauseMode(
           String isolateId, /*ExceptionPauseMode*/ String mode) =>
       _call('setExceptionPauseMode', {'isolateId': isolateId, 'mode': mode});
@@ -2430,6 +2459,9 @@
   /// A breakpoint has been removed.
   static const String kBreakpointRemoved = 'BreakpointRemoved';
 
+  /// A breakpoint has been updated.
+  static const String kBreakpointUpdated = 'BreakpointUpdated';
+
   /// A garbage collection event.
   static const String kGC = 'GC';
 
@@ -2658,8 +2690,8 @@
                 as List? ??
             []);
     memoryUsage =
-        createServiceObject(json['memoryUsage']!, const ['MemoryUsage'])
-            as MemoryUsage;
+        createServiceObject(json['memoryUsage'], const ['MemoryUsage'])
+            as MemoryUsage?;
     dateLastAccumulatorReset = json['dateLastAccumulatorReset'] is String
         ? int.parse(json['dateLastAccumulatorReset'])
         : json['dateLastAccumulatorReset'];
@@ -2711,9 +2743,9 @@
   });
 
   BoundField._fromJson(Map<String, dynamic> json) {
-    decl = createServiceObject(json['decl']!, const ['FieldRef']) as FieldRef;
+    decl = createServiceObject(json['decl'], const ['FieldRef']) as FieldRef?;
     value =
-        createServiceObject(json['value']!, const ['InstanceRef', 'Sentinel'])
+        createServiceObject(json['value'], const ['InstanceRef', 'Sentinel'])
             as dynamic;
   }
 
@@ -2768,7 +2800,7 @@
 
   BoundVariable._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
-    value = createServiceObject(json['value']!,
+    value = createServiceObject(json['value'],
         const ['InstanceRef', 'TypeArgumentsRef', 'Sentinel']) as dynamic;
     declarationTokenPos = json['declarationTokenPos'] ?? -1;
     scopeStartTokenPos = json['scopeStartTokenPos'] ?? -1;
@@ -2810,6 +2842,9 @@
   /// A number identifying this breakpoint to the user.
   int? breakpointNumber;
 
+  /// Is this breakpoint enabled?
+  bool? enabled;
+
   /// Has this breakpoint been assigned to a specific program location?
   bool? resolved;
 
@@ -2826,6 +2861,7 @@
 
   Breakpoint({
     required this.breakpointNumber,
+    required this.enabled,
     required this.resolved,
     required this.location,
     required String id,
@@ -2836,9 +2872,10 @@
 
   Breakpoint._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     breakpointNumber = json['breakpointNumber'] ?? -1;
+    enabled = json['enabled'] ?? false;
     resolved = json['resolved'] ?? false;
     isSyntheticAsyncContinuation = json['isSyntheticAsyncContinuation'];
-    location = createServiceObject(json['location']!,
+    location = createServiceObject(json['location'],
         const ['SourceLocation', 'UnresolvedSourceLocation']) as dynamic;
   }
 
@@ -2851,6 +2888,7 @@
     json['type'] = type;
     json.addAll({
       'breakpointNumber': breakpointNumber,
+      'enabled': enabled,
       'resolved': resolved,
       'location': location?.toJson(),
     });
@@ -2864,8 +2902,8 @@
   operator ==(other) => other is Breakpoint && id == other.id;
 
   String toString() => '[Breakpoint ' //
-      'id: ${id}, breakpointNumber: ${breakpointNumber}, resolved: ${resolved}, ' //
-      'location: ${location}]';
+      'id: ${id}, breakpointNumber: ${breakpointNumber}, enabled: ${enabled}, ' //
+      'resolved: ${resolved}, location: ${location}]';
 }
 
 /// `ClassRef` is a reference to a `Class`.
@@ -2876,15 +2914,28 @@
   /// The name of this class.
   String? name;
 
+  /// The location of this class in the source code.
+  @optional
+  SourceLocation? location;
+
+  /// The library which contains this class.
+  LibraryRef? library;
+
   ClassRef({
     required this.name,
+    required this.library,
     required String id,
+    this.location,
   }) : super(
           id: id,
         );
 
   ClassRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
+    location = createServiceObject(json['location'], const ['SourceLocation'])
+        as SourceLocation?;
+    library = createServiceObject(json['library'], const ['LibraryRef'])
+        as LibraryRef?;
   }
 
   @override
@@ -2896,7 +2947,9 @@
     json['type'] = type;
     json.addAll({
       'name': name,
+      'library': library?.toJson(),
     });
+    _setIfNotNull(json, 'location', location?.toJson());
     return json;
   }
 
@@ -2904,7 +2957,8 @@
 
   operator ==(other) => other is ClassRef && id == other.id;
 
-  String toString() => '[ClassRef id: ${id}, name: ${name}]';
+  String toString() =>
+      '[ClassRef id: ${id}, name: ${name}, library: ${library}]';
 }
 
 /// A `Class` provides information about a Dart language class.
@@ -2915,6 +2969,13 @@
   /// The name of this class.
   String? name;
 
+  /// The location of this class in the source code.
+  @optional
+  SourceLocation? location;
+
+  /// The library which contains this class.
+  LibraryRef? library;
+
   /// The error which occurred during class finalization, if it exists.
   @optional
   ErrorRef? error;
@@ -2928,13 +2989,6 @@
   /// Are allocations of this class being traced?
   bool? traceAllocations;
 
-  /// The library which contains this class.
-  LibraryRef? library;
-
-  /// The location of this class in the source code.
-  @optional
-  SourceLocation? location;
-
   /// The superclass of this class, if any.
   @optional
   ClassRef? superClass;
@@ -2968,17 +3022,17 @@
 
   Class({
     required this.name,
+    required this.library,
     required this.isAbstract,
     required this.isConst,
     required this.traceAllocations,
-    required this.library,
     required this.interfaces,
     required this.fields,
     required this.functions,
     required this.subclasses,
     required String id,
-    this.error,
     this.location,
+    this.error,
     this.superClass,
     this.superType,
     this.mixin,
@@ -2988,14 +3042,14 @@
 
   Class._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
+    location = createServiceObject(json['location'], const ['SourceLocation'])
+        as SourceLocation?;
+    library = createServiceObject(json['library'], const ['LibraryRef'])
+        as LibraryRef?;
     error = createServiceObject(json['error'], const ['ErrorRef']) as ErrorRef?;
     isAbstract = json['abstract'] ?? false;
     isConst = json['const'] ?? false;
     traceAllocations = json['traceAllocations'] ?? false;
-    library = createServiceObject(json['library']!, const ['LibraryRef'])
-        as LibraryRef;
-    location = createServiceObject(json['location'], const ['SourceLocation'])
-        as SourceLocation?;
     superClass =
         createServiceObject(json['super'], const ['ClassRef']) as ClassRef?;
     superType = createServiceObject(json['superType'], const ['InstanceRef'])
@@ -3025,17 +3079,17 @@
     json['type'] = type;
     json.addAll({
       'name': name,
+      'library': library?.toJson(),
       'abstract': isAbstract,
       'const': isConst,
       'traceAllocations': traceAllocations,
-      'library': library?.toJson(),
       'interfaces': interfaces?.map((f) => f.toJson()).toList(),
       'fields': fields?.map((f) => f.toJson()).toList(),
       'functions': functions?.map((f) => f.toJson()).toList(),
       'subclasses': subclasses?.map((f) => f.toJson()).toList(),
     });
-    _setIfNotNull(json, 'error', error?.toJson());
     _setIfNotNull(json, 'location', location?.toJson());
+    _setIfNotNull(json, 'error', error?.toJson());
     _setIfNotNull(json, 'super', superClass?.toJson());
     _setIfNotNull(json, 'superType', superType?.toJson());
     _setIfNotNull(json, 'mixin', mixin?.toJson());
@@ -3080,7 +3134,7 @@
 
   ClassHeapStats._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     classRef =
-        createServiceObject(json['class']!, const ['ClassRef']) as ClassRef;
+        createServiceObject(json['class'], const ['ClassRef']) as ClassRef?;
     accumulatedSize = json['accumulatedSize'] ?? -1;
     bytesCurrent = json['bytesCurrent'] ?? -1;
     instancesAccumulated = json['instancesAccumulated'] ?? -1;
@@ -3280,7 +3334,7 @@
 
   /// The enclosing context for this context.
   @optional
-  Context? parent;
+  ContextRef? parent;
 
   /// The variables in this context object.
   List<ContextElement>? variables;
@@ -3296,7 +3350,8 @@
 
   Context._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     length = json['length'] ?? -1;
-    parent = createServiceObject(json['parent'], const ['Context']) as Context?;
+    parent = createServiceObject(json['parent'], const ['ContextRef'])
+        as ContextRef?;
     variables = List<ContextElement>.from(
         createServiceObject(json['variables'], const ['ContextElement'])
                 as List? ??
@@ -3339,7 +3394,7 @@
 
   ContextElement._fromJson(Map<String, dynamic> json) {
     value =
-        createServiceObject(json['value']!, const ['InstanceRef', 'Sentinel'])
+        createServiceObject(json['value'], const ['InstanceRef', 'Sentinel'])
             as dynamic;
   }
 
@@ -3685,6 +3740,7 @@
   ///  - BreakpointAdded
   ///  - BreakpointRemoved
   ///  - BreakpointResolved
+  ///  - BreakpointUpdated
   @optional
   Breakpoint? breakpoint;
 
@@ -3975,6 +4031,10 @@
   /// Is this field static?
   bool? isStatic;
 
+  /// The location of this field in the source code.
+  @optional
+  SourceLocation? location;
+
   FieldRef({
     required this.name,
     required this.owner,
@@ -3983,19 +4043,22 @@
     required this.isFinal,
     required this.isStatic,
     required String id,
+    this.location,
   }) : super(
           id: id,
         );
 
   FieldRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
-    owner = createServiceObject(json['owner']!, const ['ObjRef']) as ObjRef;
+    owner = createServiceObject(json['owner'], const ['ObjRef']) as ObjRef?;
     declaredType =
-        createServiceObject(json['declaredType']!, const ['InstanceRef'])
-            as InstanceRef;
+        createServiceObject(json['declaredType'], const ['InstanceRef'])
+            as InstanceRef?;
     isConst = json['const'] ?? false;
     isFinal = json['final'] ?? false;
     isStatic = json['static'] ?? false;
+    location = createServiceObject(json['location'], const ['SourceLocation'])
+        as SourceLocation?;
   }
 
   @override
@@ -4013,6 +4076,7 @@
       'final': isFinal,
       'static': isStatic,
     });
+    _setIfNotNull(json, 'location', location?.toJson());
     return json;
   }
 
@@ -4051,6 +4115,10 @@
   /// Is this field static?
   bool? isStatic;
 
+  /// The location of this field in the source code.
+  @optional
+  SourceLocation? location;
+
   /// The value of this field, if the field is static. If uninitialized, this
   /// will take the value of an uninitialized Sentinel.
   ///
@@ -4058,10 +4126,6 @@
   @optional
   dynamic staticValue;
 
-  /// The location of this field in the source code.
-  @optional
-  SourceLocation? location;
-
   Field({
     required this.name,
     required this.owner,
@@ -4070,25 +4134,25 @@
     required this.isFinal,
     required this.isStatic,
     required String id,
-    this.staticValue,
     this.location,
+    this.staticValue,
   }) : super(
           id: id,
         );
 
   Field._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
-    owner = createServiceObject(json['owner']!, const ['ObjRef']) as ObjRef;
+    owner = createServiceObject(json['owner'], const ['ObjRef']) as ObjRef?;
     declaredType =
-        createServiceObject(json['declaredType']!, const ['InstanceRef'])
-            as InstanceRef;
+        createServiceObject(json['declaredType'], const ['InstanceRef'])
+            as InstanceRef?;
     isConst = json['const'] ?? false;
     isFinal = json['final'] ?? false;
     isStatic = json['static'] ?? false;
-    staticValue = createServiceObject(
-        json['staticValue'], const ['InstanceRef', 'Sentinel']) as dynamic;
     location = createServiceObject(json['location'], const ['SourceLocation'])
         as SourceLocation?;
+    staticValue = createServiceObject(
+        json['staticValue'], const ['InstanceRef', 'Sentinel']) as dynamic;
   }
 
   @override
@@ -4106,8 +4170,8 @@
       'final': isFinal,
       'static': isStatic,
     });
-    _setIfNotNull(json, 'staticValue', staticValue?.toJson());
     _setIfNotNull(json, 'location', location?.toJson());
+    _setIfNotNull(json, 'staticValue', staticValue?.toJson());
     return json;
   }
 
@@ -4288,12 +4352,17 @@
   /// Is this function const?
   bool? isConst;
 
+  /// The location of this function in the source code.
+  @optional
+  SourceLocation? location;
+
   FuncRef({
     required this.name,
     required this.owner,
     required this.isStatic,
     required this.isConst,
     required String id,
+    this.location,
   }) : super(
           id: id,
         );
@@ -4301,9 +4370,11 @@
   FuncRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
     owner = createServiceObject(
-        json['owner']!, const ['LibraryRef', 'ClassRef', 'FuncRef']) as dynamic;
+        json['owner'], const ['LibraryRef', 'ClassRef', 'FuncRef']) as dynamic;
     isStatic = json['static'] ?? false;
     isConst = json['const'] ?? false;
+    location = createServiceObject(json['location'], const ['SourceLocation'])
+        as SourceLocation?;
   }
 
   @override
@@ -4319,6 +4390,7 @@
       'static': isStatic,
       'const': isConst,
     });
+    _setIfNotNull(json, 'location', location?.toJson());
     return json;
   }
 
@@ -4373,7 +4445,7 @@
   Func._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     name = json['name'] ?? '';
     owner = createServiceObject(
-        json['owner']!, const ['LibraryRef', 'ClassRef', 'FuncRef']) as dynamic;
+        json['owner'], const ['LibraryRef', 'ClassRef', 'FuncRef']) as dynamic;
     isStatic = json['static'] ?? false;
     isConst = json['const'] ?? false;
     location = createServiceObject(json['location'], const ['SourceLocation'])
@@ -4560,7 +4632,7 @@
     kind = json['kind'] ?? '';
     identityHashCode = json['identityHashCode'] ?? -1;
     classRef =
-        createServiceObject(json['class']!, const ['ClassRef']) as ClassRef;
+        createServiceObject(json['class'], const ['ClassRef']) as ClassRef?;
     valueAsString = json['valueAsString'];
     valueAsStringIsTruncated = json['valueAsStringIsTruncated'];
     length = json['length'];
@@ -4944,7 +5016,7 @@
     kind = json['kind'] ?? '';
     identityHashCode = json['identityHashCode'] ?? -1;
     classRef =
-        createServiceObject(json['class']!, const ['ClassRef']) as ClassRef;
+        createServiceObject(json['class'], const ['ClassRef']) as ClassRef?;
     valueAsString = json['valueAsString'];
     valueAsStringIsTruncated = json['valueAsStringIsTruncated'];
     length = json['length'];
@@ -5210,7 +5282,7 @@
     livePorts = json['livePorts'] ?? -1;
     pauseOnExit = json['pauseOnExit'] ?? false;
     pauseEvent =
-        createServiceObject(json['pauseEvent']!, const ['Event']) as Event;
+        createServiceObject(json['pauseEvent'], const ['Event']) as Event?;
     rootLib = createServiceObject(json['rootLib'], const ['LibraryRef'])
         as LibraryRef?;
     libraries = List<LibraryRef>.from(
@@ -5479,7 +5551,7 @@
   });
 
   InboundReference._fromJson(Map<String, dynamic> json) {
-    source = createServiceObject(json['source']!, const ['ObjRef']) as ObjRef;
+    source = createServiceObject(json['source'], const ['ObjRef']) as ObjRef?;
     parentListIndex = json['parentListIndex'];
     parentField = createServiceObject(json['parentField'], const ['FieldRef'])
         as FieldRef?;
@@ -5704,8 +5776,8 @@
     isImport = json['isImport'] ?? false;
     isDeferred = json['isDeferred'] ?? false;
     prefix = json['prefix'] ?? '';
-    target = createServiceObject(json['target']!, const ['LibraryRef'])
-        as LibraryRef;
+    target = createServiceObject(json['target'], const ['LibraryRef'])
+        as LibraryRef?;
   }
 
   Map<String, dynamic> toJson() {
@@ -5767,19 +5839,19 @@
   });
 
   LogRecord._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
-    message = createServiceObject(json['message']!, const ['InstanceRef'])
-        as InstanceRef;
+    message = createServiceObject(json['message'], const ['InstanceRef'])
+        as InstanceRef?;
     time = json['time'] ?? -1;
     level = json['level'] ?? -1;
     sequenceNumber = json['sequenceNumber'] ?? -1;
-    loggerName = createServiceObject(json['loggerName']!, const ['InstanceRef'])
-        as InstanceRef;
-    zone = createServiceObject(json['zone']!, const ['InstanceRef'])
-        as InstanceRef;
-    error = createServiceObject(json['error']!, const ['InstanceRef'])
-        as InstanceRef;
-    stackTrace = createServiceObject(json['stackTrace']!, const ['InstanceRef'])
-        as InstanceRef;
+    loggerName = createServiceObject(json['loggerName'], const ['InstanceRef'])
+        as InstanceRef?;
+    zone = createServiceObject(json['zone'], const ['InstanceRef'])
+        as InstanceRef?;
+    error = createServiceObject(json['error'], const ['InstanceRef'])
+        as InstanceRef?;
+    stackTrace = createServiceObject(json['stackTrace'], const ['InstanceRef'])
+        as InstanceRef?;
   }
 
   @override
@@ -5821,10 +5893,10 @@
   });
 
   MapAssociation._fromJson(Map<String, dynamic> json) {
-    key = createServiceObject(json['key']!, const ['InstanceRef', 'Sentinel'])
+    key = createServiceObject(json['key'], const ['InstanceRef', 'Sentinel'])
         as dynamic;
     value =
-        createServiceObject(json['value']!, const ['InstanceRef', 'Sentinel'])
+        createServiceObject(json['value'], const ['InstanceRef', 'Sentinel'])
             as dynamic;
   }
 
@@ -6011,6 +6083,11 @@
           kind: InstanceKind.kNull,
           classRef: ClassRef(
             id: 'class/null',
+            library: LibraryRef(
+              id: '',
+              name: 'dart:core',
+              uri: 'dart:core',
+            ),
             name: 'Null',
           ),
         );
@@ -6058,6 +6135,11 @@
           kind: InstanceKind.kNull,
           classRef: ClassRef(
             id: 'class/null',
+            library: LibraryRef(
+              id: '',
+              name: 'dart:core',
+              uri: 'dart:core',
+            ),
             name: 'Null',
           ),
         );
@@ -6283,7 +6365,7 @@
     exclusiveTicks = json['exclusiveTicks'] ?? -1;
     resolvedUrl = json['resolvedUrl'] ?? '';
     function =
-        createServiceObject(json['function']!, const ['dynamic']) as dynamic;
+        createServiceObject(json['function'], const ['dynamic']) as dynamic;
   }
 
   Map<String, dynamic> toJson() {
@@ -6393,8 +6475,8 @@
 
   ProcessMemoryUsage._fromJson(Map<String, dynamic> json)
       : super._fromJson(json) {
-    root = createServiceObject(json['root']!, const ['ProcessMemoryItem'])
-        as ProcessMemoryItem;
+    root = createServiceObject(json['root'], const ['ProcessMemoryItem'])
+        as ProcessMemoryItem?;
   }
 
   @override
@@ -6522,7 +6604,7 @@
   });
 
   RetainingObject._fromJson(Map<String, dynamic> json) {
-    value = createServiceObject(json['value']!, const ['ObjRef']) as ObjRef;
+    value = createServiceObject(json['value'], const ['ObjRef']) as ObjRef?;
     parentListIndex = json['parentListIndex'];
     parentMapKey =
         createServiceObject(json['parentMapKey'], const ['ObjRef']) as ObjRef?;
@@ -6770,8 +6852,8 @@
 
   Script._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     uri = json['uri'] ?? '';
-    library = createServiceObject(json['library']!, const ['LibraryRef'])
-        as LibraryRef;
+    library = createServiceObject(json['library'], const ['LibraryRef'])
+        as LibraryRef?;
     lineOffset = json['lineOffset'];
     columnOffset = json['columnOffset'];
     source = json['source'];
@@ -6892,7 +6974,7 @@
 
   SourceLocation._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
     script =
-        createServiceObject(json['script']!, const ['ScriptRef']) as ScriptRef;
+        createServiceObject(json['script'], const ['ScriptRef']) as ScriptRef?;
     tokenPos = json['tokenPos'] ?? -1;
     endTokenPos = json['endTokenPos'];
   }
@@ -7195,7 +7277,7 @@
   static Timeline? parse(Map<String, dynamic>? json) =>
       json == null ? null : Timeline._fromJson(json);
 
-  /// A list of timeline events. No order is guarenteed for these events; in
+  /// A list of timeline events. No order is guaranteed for these events; in
   /// particular, these events may be unordered with respect to their
   /// timestamps.
   List<TimelineEvent>? traceEvents;
diff --git a/vm_service/pubspec.yaml b/vm_service/pubspec.yaml
index 5ba220b..9ea0b77 100644
--- a/vm_service/pubspec.yaml
+++ b/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
   A library to communicate with a service implementing the Dart VM
   service protocol.
 
-version: 6.2.0
+version: 7.1.0
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
 
diff --git a/vm_service/tool/dart/generate_dart.dart b/vm_service/tool/dart/generate_dart.dart
index 5e348b1..0f9897f 100644
--- a/vm_service/tool/dart/generate_dart.dart
+++ b/vm_service/tool/dart/generate_dart.dart
@@ -1491,6 +1491,8 @@
       gen.writeln('identityHashCode: 0,');
       gen.writeln('kind: InstanceKind.kNull,');
       gen.writeln("classRef: ClassRef(id: 'class/null',");
+      gen.writeln("library: LibraryRef(id: '', name: 'dart:core',");
+      gen.writeln("uri: 'dart:core',),");
       gen.writeln("name: 'Null',),");
       gen.writeln(')');
     }
@@ -1640,10 +1642,9 @@
         }
       } else {
         String typesList = _typeRefListToString(field.type.types);
-        String nullable =
-            field.optional && field.type.name != 'dynamic' ? '?' : '';
+        String nullable = field.type.name != 'dynamic' ? '?' : '';
         gen.writeln("${field.generatableName} = "
-            "createServiceObject(json['${field.name}']${field.optional ? '' : '!'}, "
+            "createServiceObject(json['${field.name}'], "
             "$typesList) as ${field.type.name}$nullable;");
       }
     });
diff --git a/vm_snapshot_analysis/BUILD.gn b/vm_snapshot_analysis/BUILD.gn
index 2ee860a..16f05b4 100644
--- a/vm_snapshot_analysis/BUILD.gn
+++ b/vm_snapshot_analysis/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for vm_snapshot_analysis-0.7.0
+# This file is generated by package_importer.py for vm_snapshot_analysis-0.7.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/watcher/BUILD.gn b/watcher/BUILD.gn
index 8cda259..ffd1572 100644
--- a/watcher/BUILD.gn
+++ b/watcher/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for watcher-1.0.0
+# This file is generated by package_importer.py for watcher-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/web_socket_channel/BUILD.gn b/web_socket_channel/BUILD.gn
index 4faa74c..a5db48f 100644
--- a/web_socket_channel/BUILD.gn
+++ b/web_socket_channel/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for web_socket_channel-2.1.0
+# This file is generated by package_importer.py for web_socket_channel-2.1.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/webdriver/BUILD.gn b/webdriver/BUILD.gn
index fb36dd7..917f9e2 100644
--- a/webdriver/BUILD.gn
+++ b/webdriver/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for webdriver-3.0.0
+# This file is generated by package_importer.py for webdriver-3.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/webkit_inspection_protocol/BUILD.gn b/webkit_inspection_protocol/BUILD.gn
index dcf43c9..f33dce7 100644
--- a/webkit_inspection_protocol/BUILD.gn
+++ b/webkit_inspection_protocol/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for webkit_inspection_protocol-1.0.0
+# This file is generated by package_importer.py for webkit_inspection_protocol-1.0.0
 
 import("//build/dart/dart_library.gni")
 
diff --git a/webview_flutter/BUILD.gn b/webview_flutter/BUILD.gn
index 9e325c8..8055963 100644
--- a/webview_flutter/BUILD.gn
+++ b/webview_flutter/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for webview_flutter-2.0.8
+# This file is generated by package_importer.py for webview_flutter-2.0.9
 
 import("//build/dart/dart_library.gni")
 
diff --git a/webview_flutter/CHANGELOG.md b/webview_flutter/CHANGELOG.md
index 30f34ac..f43812d 100644
--- a/webview_flutter/CHANGELOG.md
+++ b/webview_flutter/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.0.9
+
+* Add iOS UI integration test target.
+* Suppress deprecation warning for iOS APIs deprecated in iOS 9.
+
 ## 2.0.8
 
 * Migrate maven repository from jcenter to mavenCentral.
diff --git a/webview_flutter/example/ios/Podfile b/webview_flutter/example/ios/Podfile
index ce7f8a5..66509fc 100644
--- a/webview_flutter/example/ios/Podfile
+++ b/webview_flutter/example/ios/Podfile
@@ -30,7 +30,7 @@
 target 'Runner' do
   flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
 
-  target 'RunnerUITests' do
+  target 'RunnerTests' do
     inherit! :search_paths
 
     # Matches test_spec dependency.
diff --git a/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj
index 30ce866..f75e71d 100644
--- a/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj
@@ -8,16 +8,17 @@
 
 /* Begin PBXBuildFile section */
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		334734012669319100DCC49E /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; };
+		334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
-		686B4BF92548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; };
-		68BDCAF623C3F97800D9C032 /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; };
 		978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
 		97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
 		9D26F6F82D91F92CC095EBA9 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */; };
-		EE189BB43C38EE5DE05135A7 /* libPods-RunnerUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EBE6A98F0F7B17A8E813670 /* libPods-RunnerUITests.a */; };
+		D9A9D48F1A75E5C682944DDD /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27CC950C9005575711528C12 /* libPods-RunnerTests.a */; };
+		F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F76266057800028CB91 /* FLTWebViewUITests.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -28,6 +29,13 @@
 			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
 			remoteInfo = Runner;
 		};
+		F7151F79266057800028CB91 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+			remoteInfo = Runner;
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -44,16 +52,15 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		06E410020C7D35382771541C /* Pods-RunnerUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.release.xcconfig"; sourceTree = "<group>"; };
-		0EBE6A98F0F7B17A8E813670 /* libPods-RunnerUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		127772EEA7782174BE0D74B5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		27CC950C9005575711528C12 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
-		686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLTWKNavigationDelegateTests.m; path = ../../../ios/Tests/FLTWKNavigationDelegateTests.m; sourceTree = "<group>"; };
-		68BDCAE923C3F7CB00D9C032 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWKNavigationDelegateTests.m; sourceTree = "<group>"; };
+		68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		68BDCAED23C3F7CB00D9C032 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FLTWebViewTests.m; path = ../../../ios/Tests/FLTWebViewTests.m; sourceTree = "<group>"; };
+		68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewTests.m; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
 		7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
 		7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -67,7 +74,11 @@
 		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		C475C484BD510DD9CB2E403C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
-		DA434001E038D9F8CFB0EDEC /* Pods-RunnerUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.debug.xcconfig"; sourceTree = "<group>"; };
+		E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
+		F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
+		F7151F74266057800028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		F7151F76266057800028CB91 /* FLTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewUITests.m; sourceTree = "<group>"; };
+		F7151F78266057800028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -75,7 +86,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EE189BB43C38EE5DE05135A7 /* libPods-RunnerUITests.a in Frameworks */,
+				D9A9D48F1A75E5C682944DDD /* libPods-RunnerTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -87,17 +98,24 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F7151F71266057800028CB91 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
-		68BDCAEA23C3F7CB00D9C032 /* RunnerUITests */ = {
+		68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = {
 			isa = PBXGroup;
 			children = (
 				686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */,
 				68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */,
 				68BDCAED23C3F7CB00D9C032 /* Info.plist */,
 			);
-			path = RunnerUITests;
+			path = RunnerTests;
 			sourceTree = "<group>";
 		};
 		9740EEB11CF90186004384FC /* Flutter */ = {
@@ -116,7 +134,8 @@
 			children = (
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
-				68BDCAEA23C3F7CB00D9C032 /* RunnerUITests */,
+				68BDCAEA23C3F7CB00D9C032 /* RunnerTests */,
+				F7151F75266057800028CB91 /* RunnerUITests */,
 				97C146EF1CF9000F007C117D /* Products */,
 				C6FFB52F5C2B8A41A7E39DE2 /* Pods */,
 				B6736FC417BDCCDA377E779D /* Frameworks */,
@@ -127,7 +146,8 @@
 			isa = PBXGroup;
 			children = (
 				97C146EE1CF9000F007C117D /* Runner.app */,
-				68BDCAE923C3F7CB00D9C032 /* RunnerUITests.xctest */,
+				68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */,
+				F7151F74266057800028CB91 /* RunnerUITests.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -160,7 +180,7 @@
 			isa = PBXGroup;
 			children = (
 				8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */,
-				0EBE6A98F0F7B17A8E813670 /* libPods-RunnerUITests.a */,
+				27CC950C9005575711528C12 /* libPods-RunnerTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -170,18 +190,27 @@
 			children = (
 				127772EEA7782174BE0D74B5 /* Pods-Runner.debug.xcconfig */,
 				C475C484BD510DD9CB2E403C /* Pods-Runner.release.xcconfig */,
-				DA434001E038D9F8CFB0EDEC /* Pods-RunnerUITests.debug.xcconfig */,
-				06E410020C7D35382771541C /* Pods-RunnerUITests.release.xcconfig */,
+				F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */,
+				E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */,
 			);
 			name = Pods;
 			sourceTree = "<group>";
 		};
+		F7151F75266057800028CB91 /* RunnerUITests */ = {
+			isa = PBXGroup;
+			children = (
+				F7151F76266057800028CB91 /* FLTWebViewUITests.m */,
+				F7151F78266057800028CB91 /* Info.plist */,
+			);
+			path = RunnerUITests;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		68BDCAE823C3F7CB00D9C032 /* RunnerUITests */ = {
+		68BDCAE823C3F7CB00D9C032 /* RunnerTests */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerUITests" */;
+			buildConfigurationList = 68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
 			buildPhases = (
 				53FD4CBDD9756D74B5A3B4C1 /* [CP] Check Pods Manifest.lock */,
 				68BDCAE523C3F7CB00D9C032 /* Sources */,
@@ -193,9 +222,9 @@
 			dependencies = (
 				68BDCAEF23C3F7CB00D9C032 /* PBXTargetDependency */,
 			);
-			name = RunnerUITests;
+			name = RunnerTests;
 			productName = webview_flutter_exampleTests;
-			productReference = 68BDCAE923C3F7CB00D9C032 /* RunnerUITests.xctest */;
+			productReference = 68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */;
 			productType = "com.apple.product-type.bundle.unit-test";
 		};
 		97C146ED1CF9000F007C117D /* Runner */ = {
@@ -219,6 +248,24 @@
 			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
 			productType = "com.apple.product-type.application";
 		};
+		F7151F73266057800028CB91 /* RunnerUITests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = F7151F7B266057800028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */;
+			buildPhases = (
+				F7151F70266057800028CB91 /* Sources */,
+				F7151F71266057800028CB91 /* Frameworks */,
+				F7151F72266057800028CB91 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				F7151F7A266057800028CB91 /* PBXTargetDependency */,
+			);
+			name = RunnerUITests;
+			productName = RunnerUITests;
+			productReference = F7151F74266057800028CB91 /* RunnerUITests.xctest */;
+			productType = "com.apple.product-type.bundle.ui-testing";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -235,6 +282,11 @@
 					97C146ED1CF9000F007C117D = {
 						CreatedOnToolsVersion = 7.3.1;
 					};
+					F7151F73266057800028CB91 = {
+						CreatedOnToolsVersion = 12.5;
+						ProvisioningStyle = Automatic;
+						TestTargetID = 97C146ED1CF9000F007C117D;
+					};
 				};
 			};
 			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
@@ -251,7 +303,8 @@
 			projectRoot = "";
 			targets = (
 				97C146ED1CF9000F007C117D /* Runner */,
-				68BDCAE823C3F7CB00D9C032 /* RunnerUITests */,
+				68BDCAE823C3F7CB00D9C032 /* RunnerTests */,
+				F7151F73266057800028CB91 /* RunnerUITests */,
 			);
 		};
 /* End PBXProject section */
@@ -275,6 +328,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F7151F72266057800028CB91 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
@@ -307,7 +367,7 @@
 			outputFileListPaths = (
 			);
 			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-RunnerUITests-checkManifestLockResult.txt",
+				"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
@@ -326,7 +386,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
 		};
 		B71376B4FB8384EF9D5F3F84 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
@@ -353,8 +413,8 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				68BDCAF623C3F97800D9C032 /* FLTWebViewTests.m in Sources */,
-				686B4BF92548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m in Sources */,
+				334734012669319100DCC49E /* FLTWebViewTests.m in Sources */,
+				334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -368,6 +428,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		F7151F70266057800028CB91 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -376,6 +444,11 @@
 			target = 97C146ED1CF9000F007C117D /* Runner */;
 			targetProxy = 68BDCAEE23C3F7CB00D9C032 /* PBXContainerItemProxy */;
 		};
+		F7151F7A266057800028CB91 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 97C146ED1CF9000F007C117D /* Runner */;
+			targetProxy = F7151F79266057800028CB91 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
@@ -400,47 +473,28 @@
 /* Begin XCBuildConfiguration section */
 		68BDCAF023C3F7CB00D9C032 /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = DA434001E038D9F8CFB0EDEC /* Pods-RunnerUITests.debug.xcconfig */;
+			baseConfigurationReference = F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_STYLE = Automatic;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				INFOPLIST_FILE = RunnerUITests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+				INFOPLIST_FILE = RunnerTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.google.webview-flutter-exampleTests";
+				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				TARGETED_DEVICE_FAMILY = "1,2";
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
 			};
 			name = Debug;
 		};
 		68BDCAF123C3F7CB00D9C032 /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 06E410020C7D35382771541C /* Pods-RunnerUITests.release.xcconfig */;
+			baseConfigurationReference = E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 				CODE_SIGN_STYLE = Automatic;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				INFOPLIST_FILE = RunnerUITests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+				INFOPLIST_FILE = RunnerTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MTL_FAST_MATH = YES;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.google.webview-flutter-exampleTests";
+				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				TARGETED_DEVICE_FAMILY = "1,2";
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
 			};
 			name = Release;
@@ -557,7 +611,6 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
-				DEVELOPMENT_TEAM = "";
 				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
@@ -569,7 +622,7 @@
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
 				);
-				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.webviewFlutterExample;
+				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.webviewFlutterExample;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				VERSIONING_SYSTEM = "apple-generic";
 			};
@@ -581,7 +634,6 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
-				DEVELOPMENT_TEAM = "";
 				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
@@ -593,16 +645,42 @@
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
 				);
-				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.webviewFlutterExample;
+				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.webviewFlutterExample;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				VERSIONING_SYSTEM = "apple-generic";
 			};
 			name = Release;
 		};
+		F7151F7C266057800028CB91 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				INFOPLIST_FILE = RunnerUITests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TEST_TARGET_NAME = Runner;
+			};
+			name = Debug;
+		};
+		F7151F7D266057800028CB91 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				INFOPLIST_FILE = RunnerUITests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TEST_TARGET_NAME = Runner;
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = {
+		68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				68BDCAF023C3F7CB00D9C032 /* Debug */,
@@ -629,6 +707,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		F7151F7B266057800028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				F7151F7C266057800028CB91 /* Debug */,
+				F7151F7D266057800028CB91 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 97C146E61CF9000F007C117D /* Project object */;
diff --git a/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index bcb1de6..d7453a8 100644
--- a/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -42,6 +42,16 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "68BDCAE823C3F7CB00D9C032"
+               BuildableName = "RunnerTests.xctest"
+               BlueprintName = "RunnerTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F7151F73266057800028CB91"
                BuildableName = "RunnerUITests.xctest"
                BlueprintName = "RunnerUITests"
                ReferencedContainer = "container:Runner.xcodeproj">
diff --git a/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme b/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme
deleted file mode 100644
index 917be4f..0000000
--- a/webview_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1240"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "68BDCAE823C3F7CB00D9C032"
-               BuildableName = "RunnerUITests.xctest"
-               BlueprintName = "RunnerUITests"
-               ReferencedContainer = "container:Runner.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/webview_flutter/ios/Tests/FLTWKNavigationDelegateTests.m b/webview_flutter/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m
similarity index 100%
rename from webview_flutter/ios/Tests/FLTWKNavigationDelegateTests.m
rename to webview_flutter/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m
diff --git a/webview_flutter/ios/Tests/FLTWebViewTests.m b/webview_flutter/example/ios/RunnerTests/FLTWebViewTests.m
similarity index 100%
rename from webview_flutter/ios/Tests/FLTWebViewTests.m
rename to webview_flutter/example/ios/RunnerTests/FLTWebViewTests.m
diff --git a/webview_flutter/example/ios/RunnerTests/Info.plist b/webview_flutter/example/ios/RunnerTests/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/webview_flutter/example/ios/RunnerTests/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m b/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m
new file mode 100644
index 0000000..d193be7
--- /dev/null
+++ b/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m
@@ -0,0 +1,101 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@import XCTest;
+@import os.log;
+
+@interface FLTWebViewUITests : XCTestCase
+@property(nonatomic, strong) XCUIApplication* app;
+@end
+
+@implementation FLTWebViewUITests
+
+- (void)setUp {
+  self.continueAfterFailure = NO;
+
+  self.app = [[XCUIApplication alloc] init];
+  [self.app launch];
+}
+
+- (void)testUserAgent {
+  XCUIApplication* app = self.app;
+  XCUIElement* menu = app.buttons[@"Show menu"];
+  if (![menu waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find menu");
+  }
+  [menu tap];
+
+  XCUIElement* userAgent = app.buttons[@"Show user agent"];
+  if (![userAgent waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find Show user agent");
+  }
+  NSPredicate* userAgentPredicate =
+      [NSPredicate predicateWithFormat:@"label BEGINSWITH 'User Agent: Mozilla/5.0 (iPhone; '"];
+  XCUIElement* userAgentPopUp = [app.otherElements elementMatchingPredicate:userAgentPredicate];
+  XCTAssertFalse(userAgentPopUp.exists);
+  [userAgent tap];
+  if (![userAgentPopUp waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find user agent pop up");
+  }
+}
+
+- (void)testCache {
+  XCUIApplication* app = self.app;
+  XCUIElement* menu = app.buttons[@"Show menu"];
+  if (![menu waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find menu");
+  }
+  [menu tap];
+
+  XCUIElement* clearCache = app.buttons[@"Clear cache"];
+  if (![clearCache waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find Clear cache");
+  }
+  [clearCache tap];
+
+  [menu tap];
+
+  XCUIElement* listCache = app.buttons[@"List cache"];
+  if (![listCache waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find List cache");
+  }
+  [listCache tap];
+
+  XCUIElement* emptyCachePopup = app.otherElements[@"{\"cacheKeys\":[],\"localStorage\":{}}"];
+  if (![emptyCachePopup waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find empty cache pop up");
+  }
+
+  [menu tap];
+  XCUIElement* addCache = app.buttons[@"Add to cache"];
+  if (![addCache waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find Add to cache");
+  }
+  [addCache tap];
+  [menu tap];
+
+  if (![listCache waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find List cache");
+  }
+  [listCache tap];
+
+  XCUIElement* cachePopup =
+      app.otherElements[@"{\"cacheKeys\":[\"test_caches_entry\"],\"localStorage\":{\"test_"
+                        @"localStorage\":\"dummy_entry\"}}"];
+  if (![cachePopup waitForExistenceWithTimeout:30.0]) {
+    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
+    XCTFail(@"Failed due to not able to find cache pop up");
+  }
+}
+
+@end
diff --git a/webview_flutter/ios/Classes/FlutterWebView.m b/webview_flutter/ios/Classes/FlutterWebView.m
index b6f0b26..c6d926d 100644
--- a/webview_flutter/ios/Classes/FlutterWebView.m
+++ b/webview_flutter/ios/Classes/FlutterWebView.m
@@ -391,15 +391,25 @@
     case 0:  // require_user_action_for_all_media_types
       if (@available(iOS 10.0, *)) {
         configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll;
+      } else if (@available(iOS 9.0, *)) {
+        configuration.requiresUserActionForMediaPlayback = true;
       } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         configuration.mediaPlaybackRequiresUserAction = true;
+#pragma clang diagnostic pop
       }
       break;
     case 1:  // always_allow
       if (@available(iOS 10.0, *)) {
         configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
+      } else if (@available(iOS 9.0, *)) {
+        configuration.requiresUserActionForMediaPlayback = false;
       } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         configuration.mediaPlaybackRequiresUserAction = false;
+#pragma clang diagnostic pop
       }
       break;
     default:
diff --git a/webview_flutter/ios/webview_flutter.podspec b/webview_flutter/ios/webview_flutter.podspec
index 066dfaa..1602f1c 100644
--- a/webview_flutter/ios/webview_flutter.podspec
+++ b/webview_flutter/ios/webview_flutter.podspec
@@ -20,9 +20,4 @@
 
   s.platform = :ios, '8.0'
   s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
-
-  s.test_spec 'Tests' do |test_spec|
-    test_spec.source_files = 'Tests/**/*'
-    test_spec.dependency 'OCMock','3.5'
-  end
 end
diff --git a/webview_flutter/pubspec.yaml b/webview_flutter/pubspec.yaml
index c062118..6acee01 100644
--- a/webview_flutter/pubspec.yaml
+++ b/webview_flutter/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A Flutter plugin that provides a WebView widget on Android and iOS.
 repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 2.0.8
+version: 2.0.9
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/xml/BUILD.gn b/xml/BUILD.gn
index 235f9a2..c152400 100644
--- a/xml/BUILD.gn
+++ b/xml/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for xml-5.1.2
+# This file is generated by package_importer.py for xml-5.1.2
 
 import("//build/dart/dart_library.gni")
 
diff --git a/yaml/BUILD.gn b/yaml/BUILD.gn
index bfb3466..a73af9c 100644
--- a/yaml/BUILD.gn
+++ b/yaml/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for yaml-3.1.0
+# This file is generated by package_importer.py for yaml-3.1.0
 
 import("//build/dart/dart_library.gni")