Version 2.15.0-147.0.dev

Merge commit '1d8a9d54cd324d77b3c1a8e76782ae903dbf6e65' into 'dev'
diff --git a/DEPS b/DEPS
index c8e7820..f30b473 100644
--- a/DEPS
+++ b/DEPS
@@ -173,7 +173,7 @@
   "WebCore_rev": "bcb10901266c884e7b3740abc597ab95373ab55c",
   "webdev_rev": "832b096c0c24798d3df46faa7b7661fe930573c2",
   "webkit_inspection_protocol_rev": "dd6fb5d8b536e19cedb384d0bbf1f5631923f1e8",
-  "yaml_edit_rev": "ffcbbc22884f590663ec451f48fd8388ba7c49f4",
+  "yaml_edit_rev": "df1452bfe1653286277a1a8f34dddf3e4fbedd9e",
   "yaml_rev": "2af44871f684c89e973a96e39026b8b88dda1987",
   "zlib_rev": "bf44340d1b6be1af8950bbdf664fec0cf5a831cc",
   "crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
diff --git a/pkg/compiler/lib/src/kernel/transformations/clone_mixin_methods_with_super.dart b/pkg/compiler/lib/src/kernel/transformations/clone_mixin_methods_with_super.dart
index 80799f3..abefe2d 100644
--- a/pkg/compiler/lib/src/kernel/transformations/clone_mixin_methods_with_super.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/clone_mixin_methods_with_super.dart
@@ -61,7 +61,7 @@
         Procedure? existingGetter = existingNonSetters[field.name];
         Procedure? existingSetter = existingSetters[field.name];
         cls.addField(cloneVisitor.cloneField(
-            field, existingGetter?.reference, existingSetter?.reference));
+            field, null, existingGetter?.reference, existingSetter?.reference));
         if (existingGetter != null) {
           cls.procedures.remove(existingGetter);
         }
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index 1343db2..a66f36d 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -67,22 +67,17 @@
 /// Pattern for a trailing semicolon.
 final _trailingSemicolonPattern = RegExp(r';$');
 
-/// An implementation of [LaunchRequestArguments] that includes all fields used
+/// An implementation of [AttachRequestArguments] 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
+/// adapter in attachRequest, which is a request to start debugging an
 /// application.
 ///
-/// Specialised adapters (such as Flutter) will likely extend this class with
-/// their own additional fields.
+/// Specialised adapters (such as Flutter) will likely have their own versions
+/// of this class.
 class DartAttachRequestArguments extends DartCommonLaunchAttachRequestArguments
     implements AttachRequestArguments {
-  /// 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;
-
   /// The VM Service URI to attach to.
   ///
   /// Either this or [vmServiceInfoFile] must be supplied.
@@ -94,9 +89,9 @@
   final String? vmServiceInfoFile;
 
   DartAttachRequestArguments({
-    this.restart,
     this.vmServiceUri,
     this.vmServiceInfoFile,
+    Object? restart,
     String? name,
     String? cwd,
     List<String>? additionalProjectPaths,
@@ -108,6 +103,7 @@
   }) : super(
           name: name,
           cwd: cwd,
+          restart: restart,
           additionalProjectPaths: additionalProjectPaths,
           debugSdkLibraries: debugSdkLibraries,
           debugExternalPackageLibraries: debugExternalPackageLibraries,
@@ -117,15 +113,13 @@
         );
 
   DartAttachRequestArguments.fromMap(Map<String, Object?> obj)
-      : restart = obj['restart'],
-        vmServiceUri = obj['vmServiceUri'] as String?,
+      : vmServiceUri = obj['vmServiceUri'] as String?,
         vmServiceInfoFile = obj['vmServiceInfoFile'] as String?,
         super.fromMap(obj);
 
   @override
   Map<String, Object?> toJson() => {
         ...super.toJson(),
-        if (restart != null) 'restart': restart,
         if (vmServiceUri != null) 'vmServiceUri': vmServiceUri,
         if (vmServiceInfoFile != null) 'vmServiceInfoFile': vmServiceInfoFile,
       };
@@ -137,6 +131,11 @@
 /// A common base for [DartLaunchRequestArguments] and
 /// [DartAttachRequestArguments] for fields that are common to both.
 class DartCommonLaunchAttachRequestArguments 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;
+
   final String? name;
   final String? cwd;
 
@@ -187,6 +186,7 @@
   final bool? sendLogsToClient;
 
   DartCommonLaunchAttachRequestArguments({
+    required this.restart,
     required this.name,
     required this.cwd,
     required this.additionalProjectPaths,
@@ -198,7 +198,8 @@
   });
 
   DartCommonLaunchAttachRequestArguments.fromMap(Map<String, Object?> obj)
-      : name = obj['name'] as String?,
+      : restart = obj['restart'],
+        name = obj['name'] as String?,
         cwd = obj['cwd'] as String?,
         additionalProjectPaths =
             (obj['additionalProjectPaths'] as List?)?.cast<String>(),
@@ -212,6 +213,7 @@
         sendLogsToClient = obj['sendLogsToClient'] as bool?;
 
   Map<String, Object?> toJson() => {
+        if (restart != null) 'restart': restart,
         if (name != null) 'name': name,
         if (cwd != null) 'cwd': cwd,
         if (additionalProjectPaths != null)
@@ -263,8 +265,8 @@
 /// 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<TL extends DartLaunchRequestArguments,
-    TA extends DartAttachRequestArguments> extends BaseDebugAdapter<TL, TA> {
+abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
+    TA extends AttachRequestArguments> extends BaseDebugAdapter<TL, TA> {
   late final DartCommonLaunchAttachRequestArguments args;
   final _debuggerInitializedCompleter = Completer<void>();
   final _configurationDoneCompleter = Completer<void>();
@@ -378,6 +380,14 @@
   /// `null` if the `initialize` request has not yet been made.
   InitializeRequestArguments? get initializeArgs => _initializeArgs;
 
+  /// Whether or not this adapter can handle the restartRequest.
+  ///
+  /// If false, the editor will just terminate the debug session and start a new
+  /// one when the user asks to restart. If true, the adapter must implement
+  /// the [restartRequest] method and handle its own restart (for example the
+  /// Flutter adapter will perform a Hot Restart).
+  bool get supportsRestartRequest => false;
+
   /// Whether the VM Service closing should be used as a signal to terminate the
   /// debug session.
   ///
@@ -407,7 +417,7 @@
     TA args,
     void Function() sendResponse,
   ) async {
-    this.args = args;
+    this.args = args as DartCommonLaunchAttachRequestArguments;
     isAttach = true;
     _subscribeToOutputStreams = true;
 
@@ -480,7 +490,7 @@
       logger?.call('Starting a DDS instance for $uri');
       try {
         final dds = await DartDevelopmentService.startDartDevelopmentService(
-          uri,
+          vmServiceUriToHttp(uri),
           enableAuthCodes: enableAuthCodes,
           ipv6: ipv6,
         );
@@ -492,13 +502,13 @@
         // instance.
         if (e.errorCode ==
             DartDevelopmentServiceException.existingDdsInstanceError) {
-          uri = _cleanVmServiceUri(uri);
+          uri = vmServiceUriToWebSocket(uri);
         } else {
           rethrow;
         }
       }
     } else {
-      uri = _cleanVmServiceUri(uri);
+      uri = vmServiceUriToWebSocket(uri);
     }
 
     logger?.call('Connecting to debugger at $uri');
@@ -506,21 +516,25 @@
     final vmService = await _vmServiceConnectUri(uri.toString());
     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).
+    // Send a custom event with the VM Service URI as the editor might want to
+    // know about this (for example so it can connect an embedded DevTools to
+    // this app).
+    sendEvent(
+      RawEventBody({
+        'vmServiceUri': uri.toString(),
+      }),
+      eventType: 'dart.debuggerUris',
+    );
+
     this.vmService = vmService;
 
     unawaited(vmService.onDone.then((_) => _handleVmServiceClosed()));
     _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.onIsolateEvent.listen(handleIsolateEvent),
+      vmService.onDebugEvent.listen(handleDebugEvent),
+      vmService.onLoggingEvent.listen(handleLoggingEvent),
+      vmService.onExtensionEvent.listen(handleExtensionEvent),
+      vmService.onServiceEvent.listen(handleServiceEvent),
       if (_subscribeToOutputStreams)
         vmService.onStdoutEvent.listen(_handleStdoutEvent),
       if (_subscribeToOutputStreams)
@@ -530,8 +544,8 @@
       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.kExtension),
+      vmService.streamListen(vm.EventStreams.kService),
       vmService.streamListen(vm.EventStreams.kStdout),
       vmService.streamListen(vm.EventStreams.kStderr),
     ]);
@@ -633,6 +647,24 @@
         sendResponse(null);
         break;
 
+      /// Allows an editor to call a service/service extension that it was told
+      /// about via a custom 'dart.serviceRegistered' or
+      /// 'dart.serviceExtensionAdded' event.
+      case 'callService':
+        final method = args?.args['method'] as String?;
+        if (method == null) {
+          throw DebugAdapterException(
+            'Method is required to call services/service extensions',
+          );
+        }
+        final params = args?.args['params'] as Map<String, Object?>?;
+        final response = await vmService?.callServiceExtension(
+          method,
+          args: params,
+        );
+        sendResponse(response?.json);
+        break;
+
       default:
         await super.customRequest(request, args, sendResponse);
     }
@@ -848,6 +880,7 @@
       supportsDelayedStackTraceLoading: true,
       supportsEvaluateForHovers: true,
       supportsLogPoints: true,
+      supportsRestartRequest: supportsRestartRequest,
       // TODO(dantup): All of these...
       // supportsRestartFrame: true,
       supportsTerminateRequest: true,
@@ -903,7 +936,7 @@
     TL args,
     void Function() sendResponse,
   ) async {
-    this.args = args;
+    this.args = args as DartCommonLaunchAttachRequestArguments;
     isAttach = false;
 
     // Common setup.
@@ -947,6 +980,23 @@
   /// not in the package mapping file.
   String? resolvePackageUri(Uri uri) => _converter.resolvePackageUri(uri);
 
+  /// restart is called by the client when the user invokes a restart (for
+  /// example with the button on the debug toolbar).
+  ///
+  /// The base implementation of this method throws. It is up to a debug adapter
+  /// that advertises `supportsRestartRequest` to override this method.
+  @override
+  Future<void> restartRequest(
+    Request request,
+    RestartArguments? args,
+    void Function() sendResponse,
+  ) async {
+    throw DebugAdapterException(
+      'restartRequest was called on an adapter that '
+      'does not provide an implementation',
+    );
+  }
+
   /// [scopesRequest] is called by the client to request all of the variables
   /// scopes available for a given stack frame.
   @override
@@ -1417,11 +1467,25 @@
     sendResponse(VariablesResponseBody(variables: variables));
   }
 
+  /// Fixes up a VM Service WebSocket URI to not have a trailing /ws
+  /// and use the HTTP scheme which is what DDS expects.
+  Uri vmServiceUriToHttp(Uri uri) {
+    final isSecure = uri.isScheme('https') || uri.isScheme('wss');
+    uri = uri.replace(scheme: isSecure ? 'https' : 'http');
+
+    final segments = uri.pathSegments;
+    if (segments.isNotEmpty && segments.last == 'ws') {
+      uri = uri.replace(pathSegments: segments.take(segments.length - 1));
+    }
+
+    return uri;
+  }
+
   /// Fixes up an Observatory [uri] to a WebSocket URI with a trailing /ws
   /// for connecting when not using DDS.
   ///
   /// DDS does its own cleaning up of the URI.
-  Uri _cleanVmServiceUri(Uri uri) {
+  Uri vmServiceUriToWebSocket(Uri uri) {
     // 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');
@@ -1468,7 +1532,9 @@
     );
   }
 
-  Future<void> _handleDebugEvent(vm.Event event) async {
+  @protected
+  @mustCallSuper
+  Future<void> handleDebugEvent(vm.Event event) async {
     // 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.
@@ -1477,17 +1543,42 @@
     await _isolateManager.handleEvent(event);
   }
 
-  Future<void> _handleIsolateEvent(vm.Event event) async {
+  @protected
+  @mustCallSuper
+  Future<void> handleExtensionEvent(vm.Event event) async {
+    await debuggerInitialized;
+
+    // Base Dart does not do anything here, but other DAs (like Flutter) may
+    // override it to do their own handling.
+  }
+
+  @protected
+  @mustCallSuper
+  Future<void> handleIsolateEvent(vm.Event event) async {
     // 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 debuggerInitialized;
 
+    // Allow IsolateManager to handle any state-related events.
     await _isolateManager.handleEvent(event);
+
+    switch (event.kind) {
+      // Pass any Service Extension events on to the client so they can enable
+      // functionality based upon them.
+      case vm.EventKind.kServiceExtensionAdded:
+        this._sendServiceExtensionAdded(
+          event.extensionRPC!,
+          event.isolate!.id!,
+        );
+        break;
+    }
   }
 
   /// Handles a dart:developer log() event, sending output to the client.
-  Future<void> _handleLoggingEvent(vm.Event event) async {
+  @protected
+  @mustCallSuper
+  Future<void> handleLoggingEvent(vm.Event event) async {
     final record = event.logRecord;
     final thread = _isolateManager.threadForIsolate(event.isolate);
     if (record == null || thread == null) {
@@ -1501,7 +1592,8 @@
       if (ref == null || ref.kind == vm.InstanceKind.kNull) {
         return null;
       }
-      return _converter.convertVmInstanceRefToDisplayString(
+      return _converter
+          .convertVmInstanceRefToDisplayString(
         thread,
         ref,
         // Always allow calling toString() here as the user expects the full
@@ -1510,7 +1602,14 @@
         allowCallingToString: true,
         allowTruncatedValue: false,
         includeQuotesAroundString: false,
-      );
+      )
+          .catchError((e) {
+        // Fetching strings from the server may throw if they have been
+        // collected since (for example if a Hot Restart occurs while
+        // we're running this). Log the error and just return null so
+        // nothing is shown.
+        logger?.call('$e');
+      });
     }
 
     var loggerName = await asString(record.loggerName);
@@ -1534,6 +1633,23 @@
     }
   }
 
+  @protected
+  @mustCallSuper
+  Future<void> handleServiceEvent(vm.Event event) async {
+    await debuggerInitialized;
+
+    switch (event.kind) {
+      // Service registrations are passed to the client so they can toggle
+      // behaviour based on their presence.
+      case vm.EventKind.kServiceRegistered:
+        this._sendServiceRegistration(event.service!, event.method!);
+        break;
+      case vm.EventKind.kServiceUnregistered:
+        this._sendServiceUnregistration(event.service!, event.method!);
+        break;
+    }
+  }
+
   void _handleStderrEvent(vm.Event event) {
     _sendOutputStreamEvent('stderr', event);
   }
@@ -1585,6 +1701,27 @@
     sendOutput('stdout', message);
   }
 
+  void _sendServiceExtensionAdded(String extensionRPC, String isolateId) {
+    sendEvent(
+      RawEventBody({'extensionRPC': extensionRPC, 'isolateId': isolateId}),
+      eventType: 'dart.serviceExtensionAdded',
+    );
+  }
+
+  void _sendServiceRegistration(String service, String method) {
+    sendEvent(
+      RawEventBody({'service': service, 'method': method}),
+      eventType: 'dart.serviceRegistered',
+    );
+  }
+
+  void _sendServiceUnregistration(String service, String method) {
+    sendEvent(
+      RawEventBody({'service': service, 'method': method}),
+      eventType: 'dart.serviceUnregistered',
+    );
+  }
+
   /// Updates the current debug options for the session.
   ///
   /// Clients may not know about all debug options, so anything not included
@@ -1641,15 +1778,10 @@
 /// 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.
+/// Specialised adapters (such as Flutter) will likely have their own versions
+/// of this class.
 class DartLaunchRequestArguments extends DartCommonLaunchAttachRequestArguments
     implements LaunchRequestArguments {
-  /// 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;
@@ -1681,7 +1813,6 @@
   final String? console;
 
   DartLaunchRequestArguments({
-    this.restart,
     this.noDebug,
     required this.program,
     this.args,
@@ -1689,6 +1820,7 @@
     this.toolArgs,
     this.console,
     this.enableAsserts,
+    Object? restart,
     String? name,
     String? cwd,
     List<String>? additionalProjectPaths,
@@ -1698,6 +1830,7 @@
     bool? evaluateToStringInDebugViews,
     bool? sendLogsToClient,
   }) : super(
+          restart: restart,
           name: name,
           cwd: cwd,
           additionalProjectPaths: additionalProjectPaths,
@@ -1709,8 +1842,7 @@
         );
 
   DartLaunchRequestArguments.fromMap(Map<String, Object?> obj)
-      : restart = obj['restart'],
-        noDebug = obj['noDebug'] as bool?,
+      : noDebug = obj['noDebug'] as bool?,
         program = obj['program'] as String,
         args = (obj['args'] as List?)?.cast<String>(),
         toolArgs = (obj['toolArgs'] as List?)?.cast<String>(),
@@ -1722,7 +1854,6 @@
   @override
   Map<String, Object?> toJson() => {
         ...super.toJson(),
-        if (restart != null) 'restart': restart,
         if (noDebug != null) 'noDebug': noDebug,
         'program': program,
         if (args != null) 'args': args,
diff --git a/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart b/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart
index 2a3f351..a0d23d8 100644
--- a/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart
@@ -145,6 +145,11 @@
     } else {
       await launchAsProcess(vmPath, processArgs);
     }
+
+    // Delay responding until the debugger is connected.
+    if (debug) {
+      await debuggerInitialized;
+    }
   }
 
   /// Called by [attachRequest] to request that we actually connect to the app
@@ -249,7 +254,7 @@
   /// Called by [terminateRequest] to request that we gracefully shut down the
   /// app being run (or in the case of an attach, disconnect).
   Future<void> terminateImpl() async {
-    terminatePids(ProcessSignal.sigint);
+    terminatePids(ProcessSignal.sigterm);
     await _process?.exitCode;
   }
 
diff --git a/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart b/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart
index 534166d..0417245 100644
--- a/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart
@@ -159,7 +159,7 @@
   /// Called by [terminateRequest] to request that we gracefully shut down the
   /// app being run (or in the case of an attach, disconnect).
   Future<void> terminateImpl() async {
-    terminatePids(ProcessSignal.sigint);
+    terminatePids(ProcessSignal.sigterm);
     await _process?.exitCode;
   }
 
diff --git a/pkg/dds/lib/src/dap/base_debug_adapter.dart b/pkg/dds/lib/src/dap/base_debug_adapter.dart
index 3f64d98..2d82bbd 100644
--- a/pkg/dds/lib/src/dap/base_debug_adapter.dart
+++ b/pkg/dds/lib/src/dap/base_debug_adapter.dart
@@ -77,7 +77,7 @@
     RawRequestArguments? args,
     void Function(Object?) sendResponse,
   ) async {
-    throw DebugAdapterException('Unknown command  ${request.command}');
+    throw DebugAdapterException('Unknown command ${request.command}');
   }
 
   Future<void> disconnectRequest(
@@ -167,6 +167,12 @@
     void Function() sendResponse,
   );
 
+  Future<void> restartRequest(
+    Request request,
+    RestartArguments? args,
+    void Function() sendResponse,
+  );
+
   Future<void> scopesRequest(
     Request request,
     ScopesArguments args,
@@ -280,6 +286,12 @@
       handle(request, _withVoidResponse(launchRequest), parseLaunchArgs);
     } else if (request.command == 'attach') {
       handle(request, _withVoidResponse(attachRequest), parseAttachArgs);
+    } else if (request.command == 'restart') {
+      handle(
+        request,
+        _withVoidResponse(restartRequest),
+        _allowNullArg(RestartArguments.fromJson),
+      );
     } else if (request.command == 'terminate') {
       handle(
         request,
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index 6eb4a24..fd2d342 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -558,11 +558,18 @@
       // Set new breakpoints.
       final newBreakpoints = _clientBreakpointsByUri[uri] ?? const [];
       await Future.forEach<SourceBreakpoint>(newBreakpoints, (bp) async {
-        final vmBp = await service.addBreakpointWithScriptUri(
-            isolateId, uri, bp.line,
-            column: bp.column);
-        existingBreakpointsForIsolateAndUri.add(vmBp);
-        _clientBreakpointsByVmId[vmBp.id!] = bp;
+        try {
+          final vmBp = await service.addBreakpointWithScriptUri(
+              isolateId, uri, bp.line,
+              column: bp.column);
+          existingBreakpointsForIsolateAndUri.add(vmBp);
+          _clientBreakpointsByVmId[vmBp.id!] = bp;
+        } catch (e) {
+          // Swallow errors setting breakpoints rather than failing the whole
+          // request as it's very easy for editors to send us breakpoints that
+          // aren't valid any more.
+          _adapter.logger?.call('Failed to add breakpoint $e');
+        }
       });
     }
   }
diff --git a/pkg/dds/test/dap/integration/debug_services.dart b/pkg/dds/test/dap/integration/debug_services.dart
new file mode 100644
index 0000000..98f698e
--- /dev/null
+++ b/pkg/dds/test/dap/integration/debug_services.dart
@@ -0,0 +1,98 @@
+// 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:test/test.dart';
+import 'package:vm_service/vm_service_io.dart';
+
+import 'test_client.dart';
+import 'test_scripts.dart';
+import 'test_support.dart';
+
+main() {
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
+
+  group('debug mode', () {
+    test('reports the VM Service URI to the client', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, breakpointMarker);
+
+      await client.hitBreakpoint(testFile, breakpointLine);
+      final vmServiceUri = (await client.vmServiceUri)!;
+      expect(vmServiceUri.scheme, anyOf('ws', 'wss'));
+
+      await client.terminate();
+    });
+
+    test('exposes VM services to the client', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, breakpointMarker);
+
+      // Capture our test service registration.
+      final myServiceRegistrationFuture = client.serviceRegisteredEvents
+          .firstWhere((event) => event['service'] == 'myService');
+      await client.hitBreakpoint(testFile, breakpointLine);
+      final vmServiceUri = await client.vmServiceUri;
+
+      // Register a service that echos back its params.
+      final vmService = await vmServiceConnectUri(vmServiceUri.toString());
+      // A service seems mandatory for this to work, even though it's unused.
+      await vmService.registerService('myService', 'myServiceAlias');
+      vmService.registerServiceCallback('myService', (params) async {
+        return {'result': params};
+      });
+
+      // Ensure the service registration event is emitted and includes the
+      // method to call it.
+      final myServiceRegistration = await myServiceRegistrationFuture;
+      final myServiceRegistrationMethod =
+          myServiceRegistration['method'] as String;
+
+      // Call the method and expect it to return the same values.
+      final response = await client.callService(
+        myServiceRegistrationMethod,
+        {'foo': 'bar'},
+      );
+      final result = response.body as Map<String, Object?>;
+      expect(result['foo'], equals('bar'));
+
+      await vmService.dispose();
+      await client.terminate();
+    });
+
+    test('exposes VM service extensions to the client', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(serviceExtensionProgram);
+
+      // Capture our test service registration.
+      final serviceExtensionAddedFuture = client.serviceExtensionAddedEvents
+          .firstWhere(
+              (event) => event['extensionRPC'] == 'ext.service.extension');
+      await client.start(file: testFile);
+
+      // Ensure the service registration event is emitted and includes the
+      // method to call it.
+      final serviceExtensionAdded = await serviceExtensionAddedFuture;
+      final extensionRPC = serviceExtensionAdded['extensionRPC'] as String;
+      final isolateId = serviceExtensionAdded['isolateId'] as String;
+
+      // Call the method and expect it to return the same values.
+      final response = await client.callService(
+        extensionRPC,
+        {
+          'isolateId': isolateId,
+          'foo': 'bar',
+        },
+      );
+      final result = response.body as Map<String, Object?>;
+      expect(result['foo'], equals('bar'));
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
+}
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index 0850b5c4..e4e5010 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -36,11 +36,20 @@
   final _serverRequestHandlers =
       <String, FutureOr<Object?> Function(Object?)>{};
 
+  late final Future<Uri?> vmServiceUri;
+
   DapTestClient._(
     this._channel,
     this._logger, {
     this.captureVmServiceTraffic = false,
   }) {
+    // Set up a future that will complete when the 'dart.debuggerUris' event is
+    // emitted by the debug adapter so tests have easy access to it.
+    vmServiceUri = event('dart.debuggerUris').then<Uri?>((event) {
+      final body = event.body as Map<String, Object?>;
+      return Uri.parse(body['vmServiceUri'] as String);
+    }).catchError((e) => null);
+
     _subscription = _channel.listen(
       _handleMessage,
       onDone: () {
@@ -59,6 +68,16 @@
   Stream<OutputEventBody> get outputEvents => events('output')
       .map((e) => OutputEventBody.fromJson(e.body as Map<String, Object?>));
 
+  /// Returns a stream of custom 'dart.serviceExtensionAdded' events.
+  Stream<Map<String, Object?>> get serviceExtensionAddedEvents =>
+      events('dart.serviceExtensionAdded')
+          .map((e) => e.body as Map<String, Object?>);
+
+  /// Returns a stream of custom 'dart.serviceRegistered' events.
+  Stream<Map<String, Object?>> get serviceRegisteredEvents =>
+      events('dart.serviceRegistered')
+          .map((e) => e.body as Map<String, Object?>);
+
   /// Returns a stream of 'dart.testNotification' custom events from the
   /// package:test JSON reporter.
   Stream<Map<String, Object?>> get testNotificationEvents =>
@@ -116,6 +135,11 @@
     return attachResponse;
   }
 
+  /// Calls a service method via a custom request.
+  Future<Response> callService(String name, Object? params) {
+    return custom('callService', {'method': name, 'params': params});
+  }
+
   /// Sends a continue request for the given thread.
   ///
   /// Returns a Future that completes when the server returns a corresponding
diff --git a/pkg/dds/test/dap/integration/test_scripts.dart b/pkg/dds/test/dap/integration/test_scripts.dart
index 0b70626..a382781 100644
--- a/pkg/dds/test/dap/integration/test_scripts.dart
+++ b/pkg/dds/test/dap/integration/test_scripts.dart
@@ -20,6 +20,28 @@
   }
 ''';
 
+/// A simple Dart script that registers a simple service extension that returns
+/// its params and waits until it is called before exiting.
+const serviceExtensionProgram = '''
+  import 'dart:async';
+  import 'dart:convert';
+  import 'dart:developer';
+
+  void main(List<String> args) async {
+    // Using a completer here causes the VM to quit when the extension is called
+    // so use a flag.
+    // https://github.com/dart-lang/sdk/issues/47279
+    var wasCalled = false;
+    registerExtension('ext.service.extension', (method, params) async {
+      wasCalled = true;
+      return ServiceExtensionResponse.result(jsonEncode(params));
+    });
+    while (!wasCalled) {
+      await Future.delayed(const Duration(milliseconds: 100));
+    }
+  }
+''';
+
 /// A simple Dart script that prints its arguments.
 const simpleArgPrintingProgram = r'''
   void main(List<String> args) async {
diff --git a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
index 3c3b87c..5fff739 100644
--- a/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/enum_builder.dart
@@ -200,6 +200,7 @@
 
     Constructor? constructorReference;
     Reference? toStringReference;
+    Reference? valuesFieldReference;
     Reference? valuesGetterReference;
     Reference? valuesSetterReference;
     if (referencesFromIndexed != null) {
@@ -208,6 +209,8 @@
       toStringReference =
           referencesFromIndexed.lookupGetterReference(new Name("toString"));
       Name valuesName = new Name("values");
+      valuesFieldReference =
+          referencesFromIndexed.lookupFieldReference(valuesName);
       valuesGetterReference =
           referencesFromIndexed.lookupGetterReference(valuesName);
       valuesSetterReference =
@@ -244,6 +247,7 @@
         charOffset,
         charOffset,
         staticFieldNameScheme,
+        fieldReference: valuesFieldReference,
         fieldGetterReference: valuesGetterReference,
         fieldSetterReference: valuesSetterReference);
     members["values"] = valuesBuilder;
@@ -303,10 +307,12 @@
               name.length,
               parent.fileUri);
         }
+        Reference? fieldReference;
         Reference? getterReference;
         Reference? setterReference;
         if (referencesFromIndexed != null) {
           Name nameName = new Name(name, referencesFromIndexed.library);
+          fieldReference = referencesFromIndexed.lookupFieldReference(nameName);
           getterReference =
               referencesFromIndexed.lookupGetterReference(nameName);
           setterReference =
@@ -322,6 +328,7 @@
             enumConstantInfo.charOffset,
             enumConstantInfo.charOffset,
             staticFieldNameScheme,
+            fieldReference: fieldReference,
             fieldGetterReference: getterReference,
             fieldSetterReference: setterReference);
         members[name] = fieldBuilder..next = existing;
diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
index ca2aa43..df0d819 100644
--- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
@@ -120,8 +120,10 @@
       int charOffset,
       int charEndOffset,
       NameScheme fieldNameScheme,
-      {Reference? fieldGetterReference,
+      {Reference? fieldReference,
+      Reference? fieldGetterReference,
       Reference? fieldSetterReference,
+      Reference? lateIsSetFieldReference,
       Reference? lateIsSetGetterReference,
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
@@ -138,6 +140,8 @@
         late_lowering.computeIsSetStrategy(libraryBuilder);
 
     if (isAbstract || isExternal) {
+      assert(fieldReference == null);
+      assert(lateIsSetFieldReference == null);
       assert(lateIsSetGetterReference == null);
       assert(lateIsSetSetterReference == null);
       assert(lateGetterReference == null);
@@ -169,8 +173,10 @@
               fileUri,
               charOffset,
               charEndOffset,
+              fieldReference,
               fieldGetterReference,
               fieldSetterReference,
+              lateIsSetFieldReference,
               lateIsSetGetterReference,
               lateIsSetSetterReference,
               lateGetterReference,
@@ -184,8 +190,10 @@
               fileUri,
               charOffset,
               charEndOffset,
+              fieldReference,
               fieldGetterReference,
               fieldSetterReference,
+              lateIsSetFieldReference,
               lateIsSetGetterReference,
               lateIsSetSetterReference,
               lateGetterReference,
@@ -201,8 +209,10 @@
               fileUri,
               charOffset,
               charEndOffset,
+              fieldReference,
               fieldGetterReference,
               fieldSetterReference,
+              lateIsSetFieldReference,
               lateIsSetGetterReference,
               lateIsSetSetterReference,
               lateGetterReference,
@@ -216,8 +226,10 @@
               fileUri,
               charOffset,
               charEndOffset,
+              fieldReference,
               fieldGetterReference,
               fieldSetterReference,
+              lateIsSetFieldReference,
               lateIsSetGetterReference,
               lateIsSetSetterReference,
               lateGetterReference,
@@ -238,8 +250,10 @@
             fileUri,
             charOffset,
             charEndOffset,
+            fieldReference,
             fieldGetterReference,
             fieldSetterReference,
+            lateIsSetFieldReference,
             lateIsSetGetterReference,
             lateIsSetSetterReference,
             lateGetterReference,
@@ -253,8 +267,10 @@
             fileUri,
             charOffset,
             charEndOffset,
+            fieldReference,
             fieldGetterReference,
             fieldSetterReference,
+            lateIsSetFieldReference,
             lateIsSetGetterReference,
             lateIsSetSetterReference,
             lateGetterReference,
@@ -263,6 +279,7 @@
             isSetStrategy);
       }
     } else {
+      assert(lateIsSetFieldReference == null);
       assert(lateIsSetGetterReference == null);
       assert(lateIsSetSetterReference == null);
       assert(lateGetterReference == null);
@@ -274,6 +291,7 @@
           isLate: isLate,
           hasInitializer: hasInitializer,
           isNonNullableByDefault: library.isNonNullableByDefault,
+          fieldReference: fieldReference,
           getterReference: fieldGetterReference,
           setterReference: fieldSetterReference);
     }
@@ -599,6 +617,7 @@
       required bool isLate,
       required bool hasInitializer,
       required bool isNonNullableByDefault,
+      required Reference? fieldReference,
       required Reference? getterReference,
       required Reference? setterReference}) {
     // ignore: unnecessary_null_comparison
@@ -619,6 +638,7 @@
             isConst: isConst,
             isLate: isLate,
             fileUri: fileUri,
+            fieldReference: fieldReference,
             getterReference: getterReference)
         : new Field.mutable(
             nameScheme.getFieldName(FieldNameType.Field, name,
@@ -626,6 +646,7 @@
             isFinal: isFinal,
             isLate: isLate,
             fileUri: fileUri,
+            fieldReference: fieldReference,
             getterReference: getterReference,
             setterReference: setterReference);
     _field
@@ -808,8 +829,10 @@
       Uri fileUri,
       int charOffset,
       int charEndOffset,
+      Reference? fieldReference,
       Reference? fieldGetterReference,
       Reference? fieldSetterReference,
+      Reference? lateIsSetFieldReference,
       Reference? lateIsSetGetterReference,
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
@@ -824,6 +847,7 @@
     _field = new Field.mutable(
         nameScheme.getFieldName(FieldNameType.Field, name, isSynthesized: true),
         fileUri: fileUri,
+        fieldReference: fieldReference,
         getterReference: fieldGetterReference,
         setterReference: fieldSetterReference)
       ..fileOffset = charOffset
@@ -841,6 +865,7 @@
             nameScheme.getFieldName(FieldNameType.IsSetField, name,
                 isSynthesized: true),
             fileUri: fileUri,
+            fieldReference: lateIsSetFieldReference,
             getterReference: lateIsSetGetterReference,
             setterReference: lateIsSetSetterReference)
           ..fileOffset = charOffset
@@ -1191,8 +1216,10 @@
       Uri fileUri,
       int charOffset,
       int charEndOffset,
+      Reference? fieldReference,
       Reference? fieldGetterReference,
       Reference? fieldSetterReference,
+      Reference? lateIsSetFieldReference,
       Reference? lateIsSetGetterReference,
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
@@ -1205,8 +1232,10 @@
             fileUri,
             charOffset,
             charEndOffset,
+            fieldReference,
             fieldGetterReference,
             fieldSetterReference,
+            lateIsSetFieldReference,
             lateIsSetGetterReference,
             lateIsSetSetterReference,
             lateGetterReference,
@@ -1223,8 +1252,10 @@
       Uri fileUri,
       int charOffset,
       int charEndOffset,
+      Reference? fieldReference,
       Reference? fieldGetterReference,
       Reference? fieldSetterReference,
+      Reference? lateIsSetFieldReference,
       Reference? lateIsSetGetterReference,
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
@@ -1237,8 +1268,10 @@
             fileUri,
             charOffset,
             charEndOffset,
+            fieldReference,
             fieldGetterReference,
             fieldSetterReference,
+            lateIsSetFieldReference,
             lateIsSetGetterReference,
             lateIsSetSetterReference,
             lateGetterReference,
@@ -1270,8 +1303,10 @@
       Uri fileUri,
       int charOffset,
       int charEndOffset,
+      Reference? fieldReference,
       Reference? fieldGetterReference,
       Reference? fieldSetterReference,
+      Reference? lateIsSetFieldReference,
       Reference? lateIsSetGetterReference,
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
@@ -1284,8 +1319,10 @@
             fileUri,
             charOffset,
             charEndOffset,
+            fieldReference,
             fieldGetterReference,
             fieldSetterReference,
+            lateIsSetFieldReference,
             lateIsSetGetterReference,
             lateIsSetSetterReference,
             lateGetterReference,
@@ -1318,8 +1355,10 @@
       Uri fileUri,
       int charOffset,
       int charEndOffset,
+      Reference? fieldReference,
       Reference? fieldGetterReference,
       Reference? fieldSetterReference,
+      Reference? lateIsSetFieldReference,
       Reference? lateIsSetGetterReference,
       Reference? lateIsSetSetterReference,
       Reference? lateGetterReference,
@@ -1332,8 +1371,10 @@
             fileUri,
             charOffset,
             charEndOffset,
+            fieldReference,
             fieldGetterReference,
             fieldSetterReference,
+            lateIsSetFieldReference,
             lateIsSetGetterReference,
             lateIsSetSetterReference,
             lateGetterReference,
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index d012efa..13f9ead 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -4083,7 +4083,7 @@
     final Map<Reference, Constant> fieldValues = <Reference, Constant>{};
     fields.forEach((Field field, Constant value) {
       assert(value is! UnevaluatedConstant);
-      fieldValues[field.getterReference] = value;
+      fieldValues[field.fieldReference] = value;
     });
     assert(unusedArguments.isEmpty);
     return new InstanceConstant(klass.reference, typeArguments, fieldValues);
@@ -4092,7 +4092,7 @@
   InstanceCreation buildUnevaluatedInstance() {
     final Map<Reference, Expression> fieldValues = <Reference, Expression>{};
     fields.forEach((Field field, Constant value) {
-      fieldValues[field.getterReference] = evaluator.extract(value);
+      fieldValues[field.fieldReference] = evaluator.extract(value);
     });
     return new InstanceCreation(
         klass.reference, typeArguments, fieldValues, asserts, unusedArguments);
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
index 57d4f7c..4ebcb03 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
@@ -331,7 +331,7 @@
       if (field.isStatic) continue;
       if (!first) result.add(", ");
       result.add("${field.name}: ");
-      node.fieldValues[field.getterReference]!.accept(this);
+      node.fieldValues[field.fieldReference]!.accept(this);
       first = false;
     }
     result.add("}");
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index b1ed2fa..448fe67 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -864,8 +864,11 @@
     procedure.stubTarget = null;
   }
 
-  void _addRedirectingConstructor(SourceFactoryBuilder constructorBuilder,
-      SourceLibraryBuilder library, Reference? getterReference) {
+  void _addRedirectingConstructor(
+      SourceFactoryBuilder constructorBuilder,
+      SourceLibraryBuilder library,
+      Reference? fieldReference,
+      Reference? getterReference) {
     // Add a new synthetic field to this class for representing factory
     // constructors. This is used to support resolving such constructors in
     // source code.
@@ -888,6 +891,7 @@
           isFinal: true,
           initializer: literal,
           fileUri: cls.fileUri,
+          fieldReference: fieldReference,
           getterReference: getterReference)
         ..fileOffset = cls.fileOffset;
       cls.addField(field);
@@ -930,11 +934,18 @@
                 // is actually in the kernel tree. This call creates a StaticGet
                 // to [declaration.target] in a field `_redirecting#` which is
                 // only legal to do to things in the kernel tree.
-                Reference? getterReference =
-                    referencesFromIndexed?.lookupGetterReference(new Name(
-                        "_redirecting#", referencesFromIndexed!.library));
+                Reference? fieldReference;
+                Reference? getterReference;
+                if (referencesFromIndexed != null) {
+                  Name name =
+                      new Name(redirectingName, referencesFromIndexed!.library);
+                  fieldReference =
+                      referencesFromIndexed!.lookupFieldReference(name);
+                  getterReference =
+                      referencesFromIndexed!.lookupGetterReference(name);
+                }
                 _addRedirectingConstructor(
-                    declaration, library, getterReference);
+                    declaration, library, fieldReference, getterReference);
               }
               Member? targetNode;
               if (targetBuilder is FunctionBuilder) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
index 763ce00..4760e58 100644
--- a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
@@ -175,7 +175,7 @@
               Reference memberReference;
               if (member is Field) {
                 libraryBuilder.library.addField(member);
-                memberReference = member.getterReference;
+                memberReference = member.fieldReference;
               } else if (member is Procedure) {
                 libraryBuilder.library.addProcedure(member);
                 memberReference = member.reference;
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 1c83138..480fb3a 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -1023,12 +1023,15 @@
 
     if (unserializableExports != null) {
       Name fieldName = new Name("_exports#", library);
+      Reference? fieldReference =
+          referencesFromIndexed?.lookupFieldReference(fieldName);
       Reference? getterReference =
           referencesFromIndexed?.lookupGetterReference(fieldName);
       library.addField(new Field.immutable(fieldName,
           initializer: new StringLiteral(jsonEncode(unserializableExports)),
           isStatic: true,
           isConst: true,
+          fieldReference: fieldReference,
           getterReference: getterReference,
           fileUri: library.fileUri));
     }
@@ -2246,8 +2249,10 @@
       extensionName = currentTypeParameterScopeBuilder.name;
     }
 
+    Reference? fieldReference;
     Reference? fieldGetterReference;
     Reference? fieldSetterReference;
+    Reference? lateIsSetFieldReference;
     Reference? lateIsSetGetterReference;
     Reference? lateIsSetSetterReference;
     Reference? lateGetterReference;
@@ -2264,18 +2269,21 @@
           (_currentClassReferencesFromIndexed ?? referencesFromIndexed)!;
       Name nameToLookupName = nameScheme.getFieldName(FieldNameType.Field, name,
           isSynthesized: fieldIsLateWithLowering);
+      fieldReference = indexedContainer.lookupFieldReference(nameToLookupName);
       fieldGetterReference =
           indexedContainer.lookupGetterReference(nameToLookupName);
       fieldSetterReference =
           indexedContainer.lookupSetterReference(nameToLookupName);
       if (fieldIsLateWithLowering) {
-        Name lateIsSetNameName = nameScheme.getFieldName(
+        Name lateIsSetName = nameScheme.getFieldName(
             FieldNameType.IsSetField, name,
             isSynthesized: fieldIsLateWithLowering);
+        lateIsSetFieldReference =
+            indexedContainer.lookupFieldReference(lateIsSetName);
         lateIsSetGetterReference =
-            indexedContainer.lookupGetterReference(lateIsSetNameName);
+            indexedContainer.lookupGetterReference(lateIsSetName);
         lateIsSetSetterReference =
-            indexedContainer.lookupSetterReference(lateIsSetNameName);
+            indexedContainer.lookupSetterReference(lateIsSetName);
         lateGetterReference = indexedContainer.lookupGetterReference(
             nameScheme.getFieldName(FieldNameType.Getter, name,
                 isSynthesized: fieldIsLateWithLowering));
@@ -2295,8 +2303,10 @@
         charOffset,
         charEndOffset,
         nameScheme,
+        fieldReference: fieldReference,
         fieldGetterReference: fieldGetterReference,
         fieldSetterReference: fieldSetterReference,
+        lateIsSetFieldReference: lateIsSetFieldReference,
         lateIsSetGetterReference: lateIsSetGetterReference,
         lateIsSetSetterReference: lateIsSetSetterReference,
         lateGetterReference: lateGetterReference,
diff --git a/pkg/front_end/test/incremental_suite.dart b/pkg/front_end/test/incremental_suite.dart
index 4b4f060..30745c3 100644
--- a/pkg/front_end/test/incremental_suite.dart
+++ b/pkg/front_end/test/incremental_suite.dart
@@ -1970,9 +1970,12 @@
     Name fieldName = new Name("unique_SimulateTransformer");
     Field field = new Field.immutable(fieldName,
         isFinal: true,
-        getterReference: lib.reference.canonicalName
+        fieldReference: lib.reference.canonicalName
             ?.getChildFromFieldWithName(fieldName)
             .reference,
+        getterReference: lib.reference.canonicalName
+            ?.getChildFromFieldGetterWithName(fieldName)
+            .reference,
         fileUri: lib.fileUri);
     lib.addField(field);
     for (Class c in lib.classes) {
@@ -1983,9 +1986,12 @@
       fieldName = new Name("unique_SimulateTransformer");
       field = new Field.immutable(fieldName,
           isFinal: true,
-          getterReference: c.reference.canonicalName
+          fieldReference: lib.reference.canonicalName
               ?.getChildFromFieldWithName(fieldName)
               .reference,
+          getterReference: c.reference.canonicalName
+              ?.getChildFromFieldGetterWithName(fieldName)
+              .reference,
           fileUri: c.fileUri);
       c.addField(field);
     }
diff --git a/pkg/front_end/test/type_labeler_test.dart b/pkg/front_end/test/type_labeler_test.dart
index 8681a9f..ec09139 100644
--- a/pkg/front_end/test/type_labeler_test.dart
+++ b/pkg/front_end/test/type_labeler_test.dart
@@ -240,20 +240,20 @@
   check({symConst: "#foo", symLibConst: "#dart:core::bar"}, 0);
 
   Constant fooConst = new InstanceConstant(
-      fooClass.reference, [], {booField.getterReference: trueConst});
+      fooClass.reference, [], {booField.fieldReference: trueConst});
   check({fooConst: "Foo {boo: true}"}, 1);
 
   Constant foo2Const = new InstanceConstant(foo2Class.reference, [], {
-    nextField.getterReference: nullConst,
-    valueField.getterReference: intConst
+    nextField.fieldReference: nullConst,
+    valueField.fieldReference: intConst
   });
   check({foo2Const: "Foo {value: 2, next: null}"}, 1);
 
   Constant foo2nConst = new InstanceConstant(foo2Class.reference, [], {
-    valueField.getterReference: intConst,
-    nextField.getterReference: new InstanceConstant(foo2Class.reference, [], {
-      valueField.getterReference: intConst,
-      nextField.getterReference: nullConst
+    valueField.fieldReference: intConst,
+    nextField.fieldReference: new InstanceConstant(foo2Class.reference, [], {
+      valueField.fieldReference: intConst,
+      nextField.fieldReference: nullConst
     }),
   });
   check({foo2nConst: "Foo {value: 2, next: Foo {value: 2, next: null}}"}, 1);
@@ -261,7 +261,7 @@
   Constant bazFooFoo2Const = new InstanceConstant(
       bazClass.reference,
       [foo, foo2],
-      {xField.getterReference: fooConst, yField.getterReference: foo2Const});
+      {xField.fieldReference: fooConst, yField.fieldReference: foo2Const});
   check({
     bazFooFoo2Const: "Baz<Foo/*1*/, Foo/*2*/> "
         "{x: Foo/*1*/ {boo: true}, y: Foo/*2*/ {value: 2, next: null}}"
diff --git a/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect
index 08204a3..d87cbdc 100644
--- a/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect
@@ -28,19 +28,19 @@
 
 
 Extra constant evaluation status:
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> SymbolConstant(#catchError)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> ListConstant(const <Type*>[])
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> SymbolConstant(#test)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> SymbolConstant(#whenComplete)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> ListConstant(const <Type*>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> MapConstant(const <Symbol*, dynamic>{})
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> SymbolConstant(#timeout)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> ListConstant(const <Type*>[])
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> SymbolConstant(#onTimeout)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:602:13 -> SymbolConstant(#then)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:602:13 -> SymbolConstant(#onError)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> SymbolConstant(#asStream)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> ListConstant(const <Type*>[])
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> ListConstant(const <dynamic>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> MapConstant(const <Symbol*, dynamic>{})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:717:13 -> SymbolConstant(#catchError)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:717:13 -> ListConstant(const <Type*>[])
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:717:13 -> SymbolConstant(#test)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:753:13 -> SymbolConstant(#whenComplete)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:753:13 -> ListConstant(const <Type*>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:753:13 -> MapConstant(const <Symbol*, dynamic>{})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:776:13 -> SymbolConstant(#timeout)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:776:13 -> ListConstant(const <Type*>[])
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:776:13 -> SymbolConstant(#onTimeout)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:680:13 -> SymbolConstant(#then)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:680:13 -> SymbolConstant(#onError)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> SymbolConstant(#asStream)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> ListConstant(const <Type*>[])
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> ListConstant(const <dynamic>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> MapConstant(const <Symbol*, dynamic>{})
 Extra constant evaluation: evaluated: 61, effectively constant: 15
diff --git a/pkg/front_end/tool/ast_model.dart b/pkg/front_end/tool/ast_model.dart
index 7ea5230..834aa12 100644
--- a/pkg/front_end/tool/ast_model.dart
+++ b/pkg/front_end/tool/ast_model.dart
@@ -115,7 +115,7 @@
     'typeParameters': FieldRule(isDeclaration: true),
   },
   'Field': {
-    'reference': FieldRule(name: 'getterReference'),
+    'reference': FieldRule(name: 'fieldReference'),
   },
   'TypeParameter': {
     '_variance': FieldRule(name: 'variance'),
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 1a0a3a2..8c4fccf 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 72;
+  UInt32 formatVersion = 73;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -376,6 +376,7 @@
 
 type Field extends Member {
   Byte tag = 4;
+  CanonicalNameReference canonicalNameField;
   CanonicalNameReference canonicalNameGetter;
   CanonicalNameReference canonicalNameSetter;
   // An absolute path URI to the .dart file from which the field was created.
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 14dabff..ee30a76 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -216,11 +216,7 @@
 
   NamedNode(Reference? reference)
       : this.reference = reference ?? new Reference() {
-    if (this is Field) {
-      (this as Field).getterReference.node = this;
-    } else {
-      this.reference.node = this;
-    }
+    this.reference.node = this;
   }
 
   /// This is an advanced feature.
@@ -493,7 +489,10 @@
     }
     for (int i = 0; i < fields.length; ++i) {
       Field field = fields[i];
-      canonicalName.getChildFromField(field).bindTo(field.getterReference);
+      canonicalName.getChildFromField(field).bindTo(field.fieldReference);
+      canonicalName
+          .getChildFromFieldGetter(field)
+          .bindTo(field.getterReference);
       if (field.hasSetter) {
         canonicalName
             .getChildFromFieldSetter(field)
@@ -1262,7 +1261,10 @@
     if (!dirty) return;
     for (int i = 0; i < fields.length; ++i) {
       Field member = fields[i];
-      canonicalName.getChildFromField(member).bindTo(member.getterReference);
+      canonicalName.getChildFromField(member).bindTo(member.fieldReference);
+      canonicalName
+          .getChildFromFieldGetter(member)
+          .bindTo(member.getterReference);
       if (member.hasSetter) {
         canonicalName
             .getChildFromFieldSetter(member)
@@ -2049,13 +2051,28 @@
   DartType type; // Not null. Defaults to DynamicType.
   int flags = 0;
   Expression? initializer; // May be null.
+
+  /// Reference used for reading from this field.
+  ///
+  /// This should be used as the target in [StaticGet], [InstanceGet], and
+  /// [SuperPropertyGet].
+  final Reference getterReference;
+
+  /// Reference used for writing to this field.
+  ///
+  /// This should be used as the target in [StaticSet], [InstanceSet], and
+  /// [SuperPropertySet].
   final Reference? setterReference;
 
   @override
   @Deprecated("Use the specific getterReference/setterReference instead")
   Reference get reference => super.reference;
 
-  Reference get getterReference => super.reference;
+  /// Reference used for initializing this field.
+  ///
+  /// This should be used as the target in [FieldInitializer] and as the key
+  /// in the field values of [InstanceConstant].
+  Reference get fieldReference => super.reference;
 
   Field.mutable(Name name,
       {this.type: const DynamicType(),
@@ -2066,10 +2083,13 @@
       bool isLate: false,
       int transformerFlags: 0,
       required Uri fileUri,
+      Reference? fieldReference,
       Reference? getterReference,
       Reference? setterReference})
-      : this.setterReference = setterReference ?? new Reference(),
-        super(name, fileUri, getterReference) {
+      : this.getterReference = getterReference ?? new Reference(),
+        this.setterReference = setterReference ?? new Reference(),
+        super(name, fileUri, fieldReference) {
+    this.getterReference.node = this;
     this.setterReference!.node = this;
     // ignore: unnecessary_null_comparison
     assert(type != null);
@@ -2091,9 +2111,12 @@
       bool isLate: false,
       int transformerFlags: 0,
       required Uri fileUri,
+      Reference? fieldReference,
       Reference? getterReference})
-      : this.setterReference = null,
-        super(name, fileUri, getterReference) {
+      : this.getterReference = getterReference ?? new Reference(),
+        this.setterReference = null,
+        super(name, fileUri, fieldReference) {
+    this.getterReference.node = this;
     // ignore: unnecessary_null_comparison
     assert(type != null);
     initializer?.parent = this;
@@ -2107,7 +2130,8 @@
 
   @override
   void _relinkNode() {
-    super._relinkNode();
+    this.fieldReference.node = this;
+    this.getterReference.node = this;
     if (hasSetter) {
       this.setterReference!.node = this;
     }
@@ -2268,7 +2292,7 @@
 
   @override
   void toTextInternal(AstPrinter printer) {
-    printer.writeMemberName(getterReference);
+    printer.writeMemberName(fieldReference);
   }
 }
 
@@ -3162,10 +3186,7 @@
   Expression value;
 
   FieldInitializer(Field field, Expression value)
-      : this.byReference(
-            // getterReference is used since this refers to the field itself
-            field.getterReference,
-            value);
+      : this.byReference(field.fieldReference, value);
 
   FieldInitializer.byReference(this.fieldReference, this.value) {
     value.parent = this;
@@ -3174,7 +3195,7 @@
   Field get field => fieldReference.asField;
 
   void set field(Field field) {
-    fieldReference = field.getterReference;
+    fieldReference = field.fieldReference;
   }
 
   @override
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 1423d0d..12c33df 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1578,11 +1578,13 @@
   Field readField() {
     int tag = readByte();
     assert(tag == Tag.Field);
+    CanonicalName fieldCanonicalName = readNonNullCanonicalNameReference();
+    Reference fieldReference = fieldCanonicalName.reference;
     CanonicalName getterCanonicalName = readNonNullCanonicalNameReference();
     Reference getterReference = getterCanonicalName.reference;
     CanonicalName? setterCanonicalName = readNullableCanonicalNameReference();
     Reference? setterReference = setterCanonicalName?.reference;
-    Field? node = getterReference.node as Field?;
+    Field? node = fieldReference.node as Field?;
     if (alwaysCreateNewNamedNodes) {
       node = null;
     }
@@ -1594,12 +1596,15 @@
     if (node == null) {
       if (setterReference != null) {
         node = new Field.mutable(name,
+            fieldReference: fieldReference,
             getterReference: getterReference,
             setterReference: setterReference,
             fileUri: fileUri);
       } else {
         node = new Field.immutable(name,
-            getterReference: getterReference, fileUri: fileUri);
+            fieldReference: fieldReference,
+            getterReference: getterReference,
+            fileUri: fileUri);
       }
     }
     List<Expression> annotations = readAnnotationList(node);
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 6cb1bd7..a6d04bd 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1326,6 +1326,24 @@
 
   @override
   void visitField(Field node) {
+    CanonicalName? fieldCanonicalName = node.fieldReference.canonicalName;
+    if (fieldCanonicalName == null) {
+      throw new ArgumentError('Missing canonical name for $node');
+    }
+    String? fieldOrphancy = node.fieldReference.getOrphancyDescription(node);
+    if (fieldOrphancy != null) {
+      throw new ArgumentError('Trying to serialize orphaned field reference.\n'
+          '${fieldOrphancy}');
+    }
+    fieldOrphancy =
+        fieldCanonicalName.getOrphancyDescription(node, node.fieldReference);
+    if (fieldOrphancy != null) {
+      throw new ArgumentError(
+          'Trying to serialize orphaned field canonical name.\n'
+          '(${node.runtimeType}:${node.hashCode})\n'
+          '${fieldOrphancy}');
+    }
+
     CanonicalName? getterCanonicalName = node.getterReference.canonicalName;
     if (getterCanonicalName == null) {
       throw new ArgumentError('Missing canonical name for $node');
@@ -1367,6 +1385,7 @@
     }
     enterScope(memberScope: true);
     writeByte(Tag.Field);
+    writeNonNullCanonicalNameReference(fieldCanonicalName);
     writeNonNullCanonicalNameReference(getterCanonicalName);
     writeNullAllowedCanonicalNameReference(setterCanonicalName);
     writeUriReference(node.fileUri);
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index b628f87..8fb7add 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -176,7 +176,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 72;
+  static const int BinaryFormatVersion = 73;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/canonical_name.dart b/pkg/kernel/lib/canonical_name.dart
index bd151c1..651c2d6 100644
--- a/pkg/kernel/lib/canonical_name.dart
+++ b/pkg/kernel/lib/canonical_name.dart
@@ -30,7 +30,12 @@
 ///         "@constructors"
 ///         Qualified name
 ///
-///      Field or the implicit getter of a field:
+///      Field:
+///         Canonical name of enclosing class or library
+///         "@fields"
+///         Qualified name
+///
+///      Implicit getter of a field:
 ///         Canonical name of enclosing class or library
 ///         "@getters"
 ///         Qualified name
@@ -137,6 +142,10 @@
   }
 
   CanonicalName getChildFromField(Field field) {
+    return getChild(fieldsName).getChildFromQualifiedName(field.name);
+  }
+
+  CanonicalName getChildFromFieldGetter(Field field) {
     return getChild(gettersName).getChildFromQualifiedName(field.name);
   }
 
@@ -156,6 +165,10 @@
   }
 
   CanonicalName getChildFromFieldWithName(Name name) {
+    return getChild(fieldsName).getChildFromQualifiedName(name);
+  }
+
+  CanonicalName getChildFromFieldGetterWithName(Name name) {
     return getChild(gettersName).getChildFromQualifiedName(name);
   }
 
@@ -339,6 +352,10 @@
   /// within a library or a class.
   static const String methodsName = '@methods';
 
+  /// Symbolic name used for the [CanonicalName] node that holds all fields
+  /// within a library or class.
+  static const String fieldsName = '@fields';
+
   /// Symbolic name used for the [CanonicalName] node that holds all getters and
   /// readable fields within a library or class.
   static const String gettersName = '@getters';
@@ -355,6 +372,7 @@
     constructorsName,
     factoriesName,
     methodsName,
+    fieldsName,
     gettersName,
     settersName,
     typedefsName,
@@ -513,12 +531,15 @@
   bool get isConsistent {
     NamedNode? node = _node;
     if (node != null) {
-      if (node.reference != this &&
-          (node is! Field || node.setterReference != this)) {
-        // The reference of a [NamedNode] must point to this reference, or
-        // if the node is a [Field] the setter reference must point to this
-        // reference.
-        return false;
+      if (node is Field) {
+        // The field, getter or setter reference of the [Field] must point to
+        // this reference.
+        return node.fieldReference == this ||
+            node.getterReference == this ||
+            node.setterReference == this;
+      } else {
+        // The reference of the [NamedNode] must point to this reference.
+        return node.reference == this;
       }
     }
     if (canonicalName != null && canonicalName!._reference != this) {
@@ -529,12 +550,16 @@
 
   String getInconsistency() {
     StringBuffer sb = new StringBuffer();
-    sb.write('Reference ${this} (${hashCode}):');
+    sb.write('Reference ${toStringInternal()} (${hashCode}):');
     NamedNode? node = _node;
     if (node != null) {
       if (node is Field) {
-        if (node.getterReference != this && node.setterReference != this) {
+        if (node.fieldReference != this &&
+            node.getterReference != this &&
+            node.setterReference != this) {
           sb.write(' _node=${node} (${node.runtimeType}:${node.hashCode})');
+          sb.write(' _node.fieldReference='
+              '${node.fieldReference} (${node.fieldReference.hashCode})');
           sb.write(' _node.getterReference='
               '${node.getterReference} (${node.getterReference.hashCode})');
           sb.write(' _node.setterReference='
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index fec13cc..a407a13 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -903,8 +903,8 @@
     return result;
   }
 
-  Field cloneField(
-      Field node, Reference? getterReference, Reference? setterReference) {
+  Field cloneField(Field node, Reference? fieldReference,
+      Reference? getterReference, Reference? setterReference) {
     final Uri? activeFileUriSaved = _activeFileUri;
     _activeFileUri = node.fileUri;
 
@@ -915,6 +915,7 @@
           initializer: cloneOptional(node.initializer),
           transformerFlags: node.transformerFlags,
           fileUri: node.fileUri,
+          fieldReference: fieldReference,
           getterReference: getterReference,
           setterReference: setterReference);
     } else {
@@ -927,6 +928,7 @@
           initializer: cloneOptional(node.initializer),
           transformerFlags: node.transformerFlags,
           fileUri: node.fileUri,
+          fieldReference: fieldReference,
           getterReference: getterReference);
     }
     result
diff --git a/pkg/kernel/lib/external_name.dart b/pkg/kernel/lib/external_name.dart
index 367b5d2..c93f975 100644
--- a/pkg/kernel/lib/external_name.dart
+++ b/pkg/kernel/lib/external_name.dart
@@ -44,11 +44,11 @@
         return (constant.fieldValues.values.single as StringConstant).value;
       } else if (_isPragma(constant.classNode)) {
         final String pragmaName =
-            (constant.fieldValues[coreTypes.pragmaName.getterReference]
+            (constant.fieldValues[coreTypes.pragmaName.fieldReference]
                     as StringConstant)
                 .value;
         final Constant? pragmaOptionsValue =
-            constant.fieldValues[coreTypes.pragmaOptions.getterReference];
+            constant.fieldValues[coreTypes.pragmaOptions.fieldReference];
         final String? pragmaOptions = pragmaOptionsValue is StringConstant
             ? pragmaOptionsValue.value
             : null;
diff --git a/pkg/kernel/lib/reference_from_index.dart b/pkg/kernel/lib/reference_from_index.dart
index 1c93f7c..6182a4b 100644
--- a/pkg/kernel/lib/reference_from_index.dart
+++ b/pkg/kernel/lib/reference_from_index.dart
@@ -31,9 +31,11 @@
 }
 
 abstract class IndexedContainer {
+  final Map<Name, Reference> _fieldReferences = new Map<Name, Reference>();
   final Map<Name, Reference> _getterReferences = new Map<Name, Reference>();
   final Map<Name, Reference> _setterReferences = new Map<Name, Reference>();
 
+  Reference? lookupFieldReference(Name name) => _fieldReferences[name];
   Reference? lookupGetterReference(Name name) => _getterReferences[name];
   Reference? lookupSetterReference(Name name) => _setterReferences[name];
 
@@ -63,6 +65,8 @@
     for (int i = 0; i < fields.length; i++) {
       Field field = fields[i];
       Name name = field.name;
+      assert(_fieldReferences[name] == null);
+      _fieldReferences[name] = field.fieldReference;
       assert(_getterReferences[name] == null);
       _getterReferences[name] = field.getterReference;
       if (field.hasSetter) {
diff --git a/pkg/kernel/lib/src/equivalence.dart b/pkg/kernel/lib/src/equivalence.dart
index b307532..4a163b9 100644
--- a/pkg/kernel/lib/src/equivalence.dart
+++ b/pkg/kernel/lib/src/equivalence.dart
@@ -1642,6 +1642,9 @@
     if (!checkField_initializer(visitor, node, other)) {
       result = visitor.resultOnInequivalence;
     }
+    if (!checkField_getterReference(visitor, node, other)) {
+      result = visitor.resultOnInequivalence;
+    }
     if (!checkField_setterReference(visitor, node, other)) {
       result = visitor.resultOnInequivalence;
     }
@@ -1660,7 +1663,7 @@
     if (!checkField_transformerFlags(visitor, node, other)) {
       result = visitor.resultOnInequivalence;
     }
-    if (!checkField_getterReference(visitor, node, other)) {
+    if (!checkField_fieldReference(visitor, node, other)) {
       result = visitor.resultOnInequivalence;
     }
     if (!checkField_fileOffset(visitor, node, other)) {
@@ -4915,6 +4918,12 @@
         node.initializer, other.initializer, 'initializer');
   }
 
+  bool checkField_getterReference(
+      EquivalenceVisitor visitor, Field node, Field other) {
+    return visitor.checkReferences(
+        node.getterReference, other.getterReference, 'getterReference');
+  }
+
   bool checkField_setterReference(
       EquivalenceVisitor visitor, Field node, Field other) {
     return visitor.checkReferences(
@@ -4971,10 +4980,10 @@
     return checkMember_transformerFlags(visitor, node, other);
   }
 
-  bool checkField_getterReference(
+  bool checkField_fieldReference(
       EquivalenceVisitor visitor, Field node, Field other) {
     return visitor.checkReferences(
-        node.getterReference, other.getterReference, 'getterReference');
+        node.fieldReference, other.fieldReference, 'fieldReference');
   }
 
   bool checkMember_fileOffset(
diff --git a/pkg/kernel/lib/transformations/mixin_full_resolution.dart b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
index d05be90..fee2089 100644
--- a/pkg/kernel/lib/transformations/mixin_full_resolution.dart
+++ b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
@@ -120,6 +120,8 @@
       }
 
       for (var field in class_.mixin.fields) {
+        Reference? fieldReference =
+            indexedClass?.lookupFieldReference(field.name);
         Reference? getterReference =
             indexedClass?.lookupGetterReference(field.name);
         Reference? setterReference =
@@ -132,8 +134,8 @@
           setterReference = setters[field.name]?.reference;
           setterReference?.canonicalName?.unbind();
         }
-        Field clone =
-            cloner.cloneField(field, getterReference, setterReference);
+        Field clone = cloner.cloneField(
+            field, fieldReference, getterReference, setterReference);
         Procedure? setter = setters[field.name];
         if (setter != null) {
           setters.remove(field.name);
diff --git a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
index cc4d1387..318b720 100644
--- a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
+++ b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
@@ -352,9 +352,12 @@
         type:
             new InterfaceType(_locationClass, clazz.enclosingLibrary.nullable),
         isFinal: true,
-        getterReference: clazz.reference.canonicalName
+        fieldReference: clazz.reference.canonicalName
             ?.getChildFromFieldWithName(fieldName)
             .reference,
+        getterReference: clazz.reference.canonicalName
+            ?.getChildFromFieldGetterWithName(fieldName)
+            .reference,
         fileUri: clazz.fileUri);
     clazz.addField(locationField);
 
diff --git a/pkg/kernel/test/clone_test.dart b/pkg/kernel/test/clone_test.dart
index 2a8cfb6..5501551 100644
--- a/pkg/kernel/test/clone_test.dart
+++ b/pkg/kernel/test/clone_test.dart
@@ -96,7 +96,7 @@
   void testFields(Iterable<Field> fields) {
     testMembers<Field>(
         fields,
-        (cloner, field) => cloner.cloneField(field, null, null),
+        (cloner, field) => cloner.cloneField(field, null, null, null),
         (field) => "${field.runtimeType}(${field.name}):"
             "${field.initializer}");
   }
@@ -198,6 +198,8 @@
   bool checkField(EquivalenceVisitor visitor, Field? node, Object? other) {
     if (node is Field && other is Field) {
       assumeClonedReferences(
+          visitor, node, node.fieldReference, other, other.fieldReference);
+      assumeClonedReferences(
           visitor, node, node.getterReference, other, other.getterReference);
       assumeClonedReferences(
           visitor, node, node.setterReference, other, other.setterReference);
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index d184cb0..a780bfa 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -124,7 +124,8 @@
   /// Informative message: there is no valid migration for `null` in a
   /// non-nullable context.
   static const noValidMigrationForNull = NullabilityFixDescription._(
-      appliedMessage: 'No valid migration for `null` in a non-nullable context',
+      appliedMessage: 'No valid migration for expression with type `Null` in '
+          'a non-nullable context',
       kind: NullabilityFixKind.noValidMigrationForNull);
 
   /// Informative message: a null-aware access won't be necessary in strong
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 21b38d4..7524133 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -635,7 +635,7 @@
     var resultType =
         _fixBuilder!._typeSystem.promoteToNonNull(type as TypeImpl);
     _flowAnalysis!.nonNullAssert_end(node);
-    return node is NullLiteral && hint == null
+    return type.isDartCoreNull && hint == null
         ? NoValidMigrationChange(resultType)
         : NullCheckChange(resultType, hint: hint);
   }
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 9815198..7e35d77 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -6746,6 +6746,24 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_null_typed_expression_wiithout_valid_migration() async {
+    var content = '''
+void f(int/*!*/ x) {}
+void g() {
+  f(h());
+}
+Null h() => null;
+''';
+    var expected = '''
+void f(int x) {}
+void g() {
+  f(h());
+}
+Null h() => null;
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_nullable_use_of_typedef() async {
     var content = '''
 typedef F<T> = int Function(T);
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 242c69f..76c2bca 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -2307,6 +2307,22 @@
         changes: {findNode.simple('x &&'): isNullCheck});
   }
 
+  Future<void> test_nullExpression_noValidMigration() async {
+    await analyze('''
+int/*!*/ f() => g();
+Null g() => null;
+''');
+    var invocation = findNode.methodInvocation('g();');
+    // Note: in spite of the fact that we leave the method invocation alone, we
+    // analyze it as though it has type `Never`, because it's in a context where
+    // `null` doesn't work.
+    visitSubexpression(invocation, 'Never', changes: {
+      invocation: isNodeChangeForExpression.havingNoValidMigrationWithInfo(
+          isInfo(NullabilityFixDescription.noValidMigrationForNull,
+              {FixReasonTarget.root: TypeMatcher<NullabilityEdge>()}))
+    });
+  }
+
   Future<void> test_nullLiteral() async {
     await analyze('''
 f() => null;
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 58aae19..25e2ac8 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -467,8 +467,8 @@
             'dart:ffi',
             'DynamicLibraryExtension',
             LibraryIndex.tearoffPrefix + 'lookupFunction'),
-        getNativeFieldFunction =
-            index.getTopLevelProcedure('dart:nativewrappers', 'getNativeField'),
+        getNativeFieldFunction = index.getTopLevelProcedure(
+            'dart:nativewrappers', '_getNativeField'),
         reachabilityFenceFunction =
             index.getTopLevelProcedure('dart:_internal', 'reachabilityFence') {
     nativeFieldWrapperClassType =
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index 7157845..b0aa9a5 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -696,9 +696,9 @@
         final constant = annotation.constant;
         if (constant is InstanceConstant &&
             constant.classNode == pragmaClass &&
-            constant.fieldValues[pragmaName.getterReference] ==
+            constant.fieldValues[pragmaName.fieldReference] ==
                 StringConstant(vmFfiStructFields)) {
-          return constant.fieldValues[pragmaOptions.getterReference]
+          return constant.fieldValues[pragmaOptions.fieldReference]
               as InstanceConstant?;
         }
       }
@@ -708,7 +708,7 @@
 
   Set<Class> _compoundAnnotatedDependencies(InstanceConstant layoutConstant) {
     final fieldTypes = layoutConstant
-        .fieldValues[ffiStructLayoutTypesField.getterReference] as ListConstant;
+        .fieldValues[ffiStructLayoutTypesField.fieldReference] as ListConstant;
     final result = <Class>{};
     for (final fieldType in fieldTypes.entries) {
       if (fieldType is TypeLiteralConstant) {
@@ -726,7 +726,7 @@
   CompoundNativeTypeCfe _compoundAnnotatedNativeTypeCfe(Class compoundClass) {
     final layoutConstant = _compoundAnnotatedFields(compoundClass)!;
     final fieldTypes = layoutConstant
-        .fieldValues[ffiStructLayoutTypesField.getterReference] as ListConstant;
+        .fieldValues[ffiStructLayoutTypesField.fieldReference] as ListConstant;
     final members = <NativeTypeCfe>[];
     for (final fieldType in fieldTypes.entries) {
       if (fieldType is TypeLiteralConstant) {
@@ -734,14 +734,14 @@
         members
             .add(NativeTypeCfe(this, dartType, compoundCache: compoundCache));
       } else if (fieldType is InstanceConstant) {
-        final singleElementConstant = fieldType
-                .fieldValues[ffiInlineArrayElementTypeField.getterReference]
-            as TypeLiteralConstant;
+        final singleElementConstant =
+            fieldType.fieldValues[ffiInlineArrayElementTypeField.fieldReference]
+                as TypeLiteralConstant;
         final singleElementType = NativeTypeCfe(
             this, singleElementConstant.type,
             compoundCache: compoundCache);
         final arrayLengthConstant =
-            fieldType.fieldValues[ffiInlineArrayLengthField.getterReference]
+            fieldType.fieldValues[ffiInlineArrayLengthField.fieldReference]
                 as IntConstant;
         final arrayLength = arrayLengthConstant.value;
         members.add(ArrayNativeTypeCfe(singleElementType, arrayLength));
@@ -749,7 +749,7 @@
     }
     if (compoundClass.superclass == structClass) {
       final packingConstant = layoutConstant
-          .fieldValues[ffiStructLayoutPackingField.getterReference];
+          .fieldValues[ffiStructLayoutPackingField.fieldReference];
       if (packingConstant is IntConstant) {
         return StructNativeTypeCfe(compoundClass, members,
             packing: packingConstant.value);
@@ -767,12 +767,12 @@
 
     node.addAnnotation(ConstantExpression(
         InstanceConstant(pragmaClass.reference, [], {
-          pragmaName.getterReference: StringConstant(vmFfiStructFields),
-          pragmaOptions.getterReference:
+          pragmaName.fieldReference: StringConstant(vmFfiStructFields),
+          pragmaOptions.fieldReference:
               InstanceConstant(ffiStructLayoutClass.reference, [], {
-            ffiStructLayoutTypesField.getterReference: ListConstant(
+            ffiStructLayoutTypesField.fieldReference: ListConstant(
                 InterfaceType(typeClass, Nullability.nonNullable), constants),
-            ffiStructLayoutPackingField.getterReference:
+            ffiStructLayoutPackingField.fieldReference:
                 packing == null ? NullConstant() : IntConstant(packing)
           })
         }),
@@ -850,8 +850,8 @@
       ..isNonNullableByDefault = true
       ..addAnnotation(ConstantExpression(
           InstanceConstant(pragmaClass.reference, /*type_arguments=*/ [], {
-        pragmaName.getterReference: StringConstant("vm:prefer-inline"),
-        pragmaOptions.getterReference: NullConstant(),
+        pragmaName.fieldReference: StringConstant("vm:prefer-inline"),
+        pragmaOptions.fieldReference: NullConstant(),
       })));
 
     compound.addProcedure(getter);
@@ -887,7 +887,7 @@
 
   List<int> _arraySize(InstanceConstant constant) {
     final dimensions =
-        constant.fieldValues[arraySizeDimensionsField.getterReference];
+        constant.fieldValues[arraySizeDimensionsField.fieldReference];
     if (dimensions != null) {
       if (dimensions is ListConstant) {
         final result = dimensions.entries
@@ -905,7 +905,7 @@
       arraySizeDimension5Field
     ];
     final result = dimensionFields
-        .map((f) => constant.fieldValues[f.getterReference])
+        .map((f) => constant.fieldValues[f.fieldReference])
         .whereType<IntConstant>()
         .map((c) => c.value)
         .toList();
@@ -1414,9 +1414,9 @@
   @override
   Constant generateConstant(FfiTransformer transformer) =>
       InstanceConstant(transformer.ffiInlineArrayClass.reference, [], {
-        transformer.ffiInlineArrayElementTypeField.getterReference:
+        transformer.ffiInlineArrayElementTypeField.fieldReference:
             singleElementType.generateConstant(transformer),
-        transformer.ffiInlineArrayLengthField.getterReference:
+        transformer.ffiInlineArrayLengthField.fieldReference:
             IntConstant(dimensionsFlattened)
       });
 
diff --git a/pkg/vm/lib/transformations/ffi_native.dart b/pkg/vm/lib/transformations/ffi_native.dart
index 3d44d46..0752ed2 100644
--- a/pkg/vm/lib/transformations/ffi_native.dart
+++ b/pkg/vm/lib/transformations/ffi_native.dart
@@ -94,9 +94,9 @@
     assert(currentLibrary != null);
     final params = node.function.positionalParameters;
     final functionName = annotationConst
-        .fieldValues[ffiNativeNameField.getterReference] as StringConstant;
+        .fieldValues[ffiNativeNameField.fieldReference] as StringConstant;
     final isLeaf = annotationConst
-        .fieldValues[ffiNativeIsLeafField.getterReference] as BoolConstant;
+        .fieldValues[ffiNativeIsLeafField.fieldReference] as BoolConstant;
 
     // double Function(double)
     final DartType dartType =
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 312c1d6..a90f1ec 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -372,12 +372,12 @@
         // overhead of converting Handles.
         // If we find an NFWC1 object being passed to an FfiNative signature
         // taking a Pointer, we automatically wrap the argument in a call to
-        // `Pointer.fromAddress(getNativeField(obj))`.
+        // `Pointer.fromAddress(_getNativeField(obj))`.
         // Example:
         //   passAsPointer(ClassWithNativeField());
         // Becomes, roughly:
         //   #t0 = PointerClassWithNativeField();
-        //   passAsPointer(Pointer.fromAddress(getNativeField(#t0)));
+        //   passAsPointer(Pointer.fromAddress(_getNativeField(#t0)));
         //   reachabilityFence(#t0);
         final ffiNativeAnn = _tryGetFfiNativeAnnotation(target);
         if (ffiNativeAnn != null) {
@@ -404,7 +404,7 @@
                     isFinal: true);
                 tmpsArgs.add(tmpPtr);
 
-                // Pointer.fromAddress(getNativeField(#t1)).
+                // Pointer.fromAddress(_getNativeField(#t1)).
                 final ptr = StaticInvocation(
                     fromAddressInternal,
                     Arguments([
@@ -430,7 +430,7 @@
             // {
             //   T #t0;
             //   final NativeFieldWrapperClass1 #t1 = MyNFWC1();
-            //   #t0 = foo(Pointer.fromAddress(getNativeField(#t1)));
+            //   #t0 = foo(Pointer.fromAddress(_getNativeField(#t1)));
             //   reachabilityFence(#t1);
             // } => #t0
             final tmpResult =
diff --git a/pkg/vm/lib/transformations/pragma.dart b/pkg/vm/lib/transformations/pragma.dart
index dfac713..2fabcf4 100644
--- a/pkg/vm/lib/transformations/pragma.dart
+++ b/pkg/vm/lib/transformations/pragma.dart
@@ -78,7 +78,7 @@
 
     String pragmaName;
     Constant? name =
-        pragmaConstant.fieldValues[coreTypes.pragmaName.getterReference];
+        pragmaConstant.fieldValues[coreTypes.pragmaName.fieldReference];
     if (name is StringConstant) {
       pragmaName = name.value;
     } else {
@@ -86,7 +86,7 @@
     }
 
     Constant options =
-        pragmaConstant.fieldValues[coreTypes.pragmaOptions.getterReference]!;
+        pragmaConstant.fieldValues[coreTypes.pragmaOptions.fieldReference]!;
 
     switch (pragmaName) {
       case kEntryPointPragmaName:
diff --git a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
index c0175a4..ffc4b1e 100644
--- a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
+++ b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
@@ -116,7 +116,7 @@
         if (constant is InstanceConstant &&
             constant.classReference == _tagNumberClass.reference) {
           if (messageClass._usedTags.add((constant
-                  .fieldValues[_tagNumberField.getterReference] as IntConstant)
+                  .fieldValues[_tagNumberField.fieldReference] as IntConstant)
               .value)) {
             _invalidatedClasses.add(messageClass);
           }
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index f37aa3a..1868a4d 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -1757,6 +1757,11 @@
       // write a dangling reference to the deleted member.
       if (node is Field) {
         assert(
+            node.fieldReference.node == node,
+            "Trying to remove canonical name from field reference on $node "
+            "which has been repurposed for ${node.fieldReference.node}.");
+        node.fieldReference.canonicalName?.unbind();
+        assert(
             node.getterReference.node == node,
             "Trying to remove canonical name from getter reference on $node "
             "which has been repurposed for ${node.getterReference.node}.");
diff --git a/pkg/vm/test/incremental_compiler_test.dart b/pkg/vm/test/incremental_compiler_test.dart
index 1623621..b24cb9e 100644
--- a/pkg/vm/test/incremental_compiler_test.dart
+++ b/pkg/vm/test/incremental_compiler_test.dart
@@ -1045,9 +1045,9 @@
       }
     });
 
-    /// This test basicaly verifies that components `relink` method is correctly
-    /// called when rejecting (i.e. logically going back in time to before a
-    /// rejected compilation).
+    /// This test basically verifies that components `relink` method is
+    /// correctly called when rejecting (i.e. logically going back in time to
+    /// before a rejected compilation).
     test('check links after reject', () async {
       final Uri fooUri = Uri.file('${mytest.path}/foo.dart');
       new File.fromUri(fooUri).writeAsStringSync("""
diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc
index 2ed7d14..a6bc6e5 100644
--- a/runtime/vm/compiler/frontend/constant_reader.cc
+++ b/runtime/vm/compiler/frontend/constant_reader.cc
@@ -403,8 +403,7 @@
       Field& field = Field::Handle(Z);
       Instance& constant = Instance::Handle(Z);
       for (intptr_t j = 0; j < number_of_fields; ++j) {
-        field = H.LookupFieldByKernelGetterOrSetter(
-            reader.ReadCanonicalNameReference());
+        field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference());
         // Recurse into lazily evaluating all "sub" constants
         // needed to evaluate the current constant.
         const intptr_t entry_index = reader.ReadUInt();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index d74fc3d..8c1f3bc 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -238,7 +238,7 @@
         ReadBool();
         const NameIndex field_name = ReadCanonicalNameReference();
         const Field& field =
-            Field::Handle(Z, H.LookupFieldByKernelGetterOrSetter(field_name));
+            Field::Handle(Z, H.LookupFieldByKernelField(field_name));
         initializer_fields[i] = &field;
         SkipExpression();
         continue;
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 7406e5a..db4e074 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -316,8 +316,22 @@
   return StringEquals(CanonicalNameString(kind), "@factories");
 }
 
+bool TranslationHelper::IsField(NameIndex name) {
+  // Fields with private names have the import URI of the library where they
+  // are visible as the parent and the string "@fields" as the parent's parent.
+  // Fields with non-private names have the string "@fields" as the parent.
+  if (IsRoot(name)) {
+    return false;
+  }
+  NameIndex kind = CanonicalNameParent(name);
+  if (IsPrivate(name)) {
+    kind = CanonicalNameParent(kind);
+  }
+  return StringEquals(CanonicalNameString(kind), "@fields");
+}
+
 NameIndex TranslationHelper::EnclosingName(NameIndex name) {
-  ASSERT(IsConstructor(name) || IsProcedure(name));
+  ASSERT(IsConstructor(name) || IsProcedure(name) || IsField(name));
   NameIndex enclosing = CanonicalNameParent(CanonicalNameParent(name));
   if (IsPrivate(name)) {
     enclosing = CanonicalNameParent(enclosing);
@@ -574,6 +588,27 @@
   return info_.InsertClass(thread_, name_index_handle_, klass);
 }
 
+FieldPtr TranslationHelper::LookupFieldByKernelField(NameIndex kernel_field) {
+  ASSERT(IsField(kernel_field));
+  NameIndex enclosing = EnclosingName(kernel_field);
+
+  Class& klass = Class::Handle(Z);
+  if (IsLibrary(enclosing)) {
+    Library& library =
+        Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing));
+    klass = library.toplevel_class();
+    CheckStaticLookup(klass);
+  } else {
+    ASSERT(IsClass(enclosing));
+    klass = LookupClassByKernelClass(enclosing);
+  }
+  Field& field = Field::Handle(
+      Z, klass.LookupFieldAllowPrivate(
+             DartSymbolObfuscate(CanonicalNameString(kernel_field))));
+  CheckStaticLookup(field);
+  return field.ptr();
+}
+
 FieldPtr TranslationHelper::LookupFieldByKernelGetterOrSetter(
     NameIndex kernel_field,
     bool required) {
@@ -1006,6 +1041,11 @@
       if (++next_read_ == field) return;
     }
       FALL_THROUGH;
+    case kCanonicalNameField:
+      canonical_name_field_ =
+          helper_->ReadCanonicalNameReference();  // read canonical_name_field.
+      if (++next_read_ == field) return;
+      FALL_THROUGH;
     case kCanonicalNameGetter:
       canonical_name_getter_ =
           helper_->ReadCanonicalNameReference();  // read canonical_name_getter.
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index d7e5542..c854d07 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -109,6 +109,7 @@
   bool IsGetter(NameIndex name);
   bool IsSetter(NameIndex name);
   bool IsFactory(NameIndex name);
+  bool IsField(NameIndex name);
 
   // For a member (field, constructor, or procedure) return the canonical name
   // of the enclosing class or library.
@@ -165,6 +166,7 @@
   virtual LibraryPtr LookupLibraryByKernelLibrary(NameIndex library);
   virtual ClassPtr LookupClassByKernelClass(NameIndex klass);
 
+  FieldPtr LookupFieldByKernelField(NameIndex field);
   FieldPtr LookupFieldByKernelGetterOrSetter(NameIndex field,
                                              bool required = true);
   FunctionPtr LookupStaticMethodByKernelProcedure(NameIndex procedure,
@@ -444,6 +446,7 @@
  public:
   enum Field {
     kStart,  // tag.
+    kCanonicalNameField,
     kCanonicalNameGetter,
     kCanonicalNameSetter,
     kSourceUriIndex,
@@ -491,6 +494,7 @@
   bool IsLate() const { return (flags_ & kIsLate) != 0; }
   bool IsExtensionMember() const { return (flags_ & kExtensionMember) != 0; }
 
+  NameIndex canonical_name_field_;
   NameIndex canonical_name_getter_;
   NameIndex canonical_name_setter_;
   TokenPosition position_ = TokenPosition::kNoSource;
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index bc55064..862b338 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -221,7 +221,7 @@
   V(::, _storePointer, FfiStorePointer, 0xea6b7751)                            \
   V(::, _fromAddress, FfiFromAddress, 0xfd8cb1cc)                              \
   V(Pointer, get:address, FfiGetAddress, 0x7cde87be)                           \
-  V(::, getNativeField, GetNativeField, 0x95b4ec94)                            \
+  V(::, _getNativeField, GetNativeField, 0xa0139b85)                           \
   V(::, reachabilityFence, ReachabilityFence, 0x619235c1)                      \
   V(_Utf8Decoder, _scan, Utf8DecoderScan, 0x1dcaf73d)                          \
   V(_Future, timeout, FutureTimeout, 0x73041520)                               \
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 4d9d1df..dada22e 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -4241,20 +4241,39 @@
   return reinterpret_cast<Dart_NativeFunction>(Builtin_SecretKeeper_KeepSecret);
 }
 
+static intptr_t ReturnPtrAsInt(void* ptr) {
+  return reinterpret_cast<intptr_t>(ptr);
+}
+
+static void* SecretKeeperFfiNativeResolver(const char* name, uintptr_t argn) {
+  if (strcmp(name, "returnPtrAsInt") == 0 && argn == 1) {
+    return reinterpret_cast<void*>(&ReturnPtrAsInt);
+  }
+  return nullptr;
+}
+
 TEST_CASE(DartAPI_NativeFieldAccess) {
   const char* kScriptChars = R"(
+    import 'dart:ffi';
     import 'dart:nativewrappers';
     class SecretKeeper extends NativeFieldWrapperClass1 {
       SecretKeeper(int secret) { _keepSecret(secret); }
       @pragma("vm:external-name", "SecretKeeper_KeepSecret")
       external void _keepSecret(int secret);
     }
-    main() => getNativeField(SecretKeeper(321));
+    // Argument auto-conversion will wrap `o` in `_getNativeField()`.
+    @FfiNative<IntPtr Function(Pointer<Void>)>('returnPtrAsInt')
+    external int returnPtrAsInt(NativeFieldWrapperClass1 o);
+    main() => returnPtrAsInt(SecretKeeper(321));
   )";
 
   Dart_Handle result;
   Dart_Handle lib =
       TestCase::LoadTestScript(kScriptChars, SecretKeeperNativeResolver);
+
+  result = Dart_SetFfiNativeResolver(lib, &SecretKeeperFfiNativeResolver);
+  EXPECT_VALID(result);
+
   result = Dart_Invoke(lib, NewString("main"), 0, nullptr);
 
   EXPECT_VALID(result);
@@ -4265,19 +4284,28 @@
   EXPECT_EQ(321, value);
 }
 
+// Test that trying to access an unset native field (internally through
+// _getNativeField(..)) will result in a Dart exception (and not crash).
 TEST_CASE(DartAPI_NativeFieldAccess_Throws) {
   const char* kScriptChars = R"(
+    import 'dart:ffi';
     import 'dart:nativewrappers';
     class ForgetfulSecretKeeper extends NativeFieldWrapperClass1 {
       ForgetfulSecretKeeper(int secret) { /* Forget to init. native field. */ }
     }
-    main() => getNativeField(ForgetfulSecretKeeper(321));
+    // Argument auto-conversion will wrap `o` in `_getNativeField()`.
+    @FfiNative<IntPtr Function(Pointer<Void>)>('returnPtrAsInt')
+    external int returnPtrAsInt(NativeFieldWrapperClass1 o);
+    main() => returnPtrAsInt(ForgetfulSecretKeeper(321));
   )";
 
   Dart_Handle result;
   Dart_Handle lib =
       TestCase::LoadTestScript(kScriptChars, SecretKeeperNativeResolver);
 
+  result = Dart_SetFfiNativeResolver(lib, &SecretKeeperFfiNativeResolver);
+  EXPECT_VALID(result);
+
   result = Dart_Invoke(lib, NewString("main"), 0, nullptr);
 
   EXPECT(Dart_IsError(result));
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 13f777c..65b7bd5 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
 
 // Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 72;
-static const uint32_t kMaxSupportedKernelFormatVersion = 72;
+static const uint32_t kMinSupportedKernelFormatVersion = 73;
+static const uint32_t kMaxSupportedKernelFormatVersion = 73;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 555bc3d..e43b92a 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -7,8 +7,8 @@
 /// A type representing values that are either `Future<T>` or `T`.
 ///
 /// This class declaration is a public stand-in for an internal
-/// future-or-value generic type. References to this class are resolved to the
-/// internal type.
+/// future-or-value generic type, which is not a class type.
+/// References to this class are resolved to the internal type.
 ///
 /// It is a compile-time error for any class to extend, mix in or implement
 /// `FutureOr`.
@@ -43,35 +43,109 @@
   }
 }
 
-/// An object representing a delayed computation.
+/// The result of an asynchronous computation.
 ///
-/// A [Future] is used to represent a potential value, or error,
-/// that will be available at some time in the future.
-/// Receivers of a [Future] can register callbacks
-/// that handle the value or error once it is available.
+/// An _asynchronous computation_ cannot provide a result immediately
+/// when it is started, unlike a synchronous computation which does compute
+/// a result immediately by either returning a value or by throwing.
+/// An asynchronous computation may need to wait for something external
+/// to the program (reading a file, querying a database, fetching a web page)
+/// which takes time.
+/// Instead of blocking all computation until the result is available,
+/// the asynchronous computation immediately returns a `Future`
+/// which will *eventually* "complete" with the result.
+///
+/// ### Asynchronous programming
+///
+/// To perform an asynchronous computation, you use an `async` function
+/// which always produces a future.
+/// Inside such an asynchronous function, you can use the `await` operation
+/// to delay execution until another asyncronous computation has a result.
+/// While execution of the awaiting function is delayed,
+/// the program is not blocked, and can continue doing other things.
+///
+/// Example:
+/// ```dart
+/// import "dart:io";
+/// Future<bool> fileContains(String path, String needle) async {
+///    var haystack = await File(path).readAsString();
+///    return haystack.contains(needle);
+/// }
+/// ```
+/// Here the `File.readAsString` method from `dart:io` is an asychronous
+/// function returning a `Future<String>`.
+/// The `fileContains` function is marked with `async` right before its body,
+/// which means that you can use `await` insider it,
+/// and that it must return a future.
+/// The call to `File(path).readAsString()` initiates reading the file into
+/// a string and produces a `Future<String>` which will eventually contain the
+/// result.
+/// The `await` then waits for that future to complete with a string
+/// (or an error, if reading the file fails).
+/// While waiting, the program can do other things.
+/// When the future completes with a string, the `fileContains` function
+/// computes a boolean and returns it, which then completes the original
+/// future that it returned when first called.
+///
+/// If a future completes with an *error*, awaiting that future will
+/// (re-)throw that error. In the example here, we can add error checking:
+/// ```dart
+/// import "dart:io";
+/// Future<bool> fileContains(String path, String needle) async {
+///   try {
+///     var haystack = await File(path).readAsString();
+///     return haystack.contains(needle);
+///   } on FileSystemException catch (exception, stack) {
+///     _myLog.logError(exception, stack);
+///     return false;
+///   }
+/// }
+/// ```
+/// You use a normal `try`/`catch` to catch the failures of awaited
+/// asynchronous computations.
+///
+/// In general, when writing asynchronous code, you should always await a
+/// future when it is produced, and not wait until after another asynchronous
+/// delay. That ensures that you are ready to receive any error that the
+/// future might produce, which is important because an asynchronous error
+/// that no-one is awaiting is an *uncaught* error and may terminate
+/// the running program.
+///
+/// ### Programming with the `Future` API.
+///
+/// The `Future` class also provides a more direct, low-level functionality
+/// for accessing the result that it completes with.
+/// The `async` and `await` language features are built on top of this
+/// functionality, and it sometimes makes sense to use it directly.
+/// There are things that you cannot do by just `await`ing one future at
+/// a time.
+///
+/// With a [Future], you can manually register callbacks
+/// that handle the value, or error, once it is available.
 /// For example:
 /// ```dart
 /// Future<int> future = getFuture();
 /// future.then((value) => handleValue(value))
 ///       .catchError((error) => handleError(error));
 /// ```
-/// A [Future] can be completed in two ways:
-/// with a value ("the future succeeds")
-/// or with an error ("the future fails").
-/// Users can install callbacks for each case.
+/// Since a [Future] can be completed in two ways,
+/// either with a value (if the asynchronous computation succeeded)
+/// or with an error (if the computation failed),
+/// you can install callbacks for either or both cases.
 ///
-/// In some cases we say that a future is completed with another future.
+/// In some cases we say that a future is completed *with another future*.
 /// This is a short way of stating that the future is completed in the same way,
 /// with the same value or error,
-/// as the other future once that completes.
-/// Whenever a function in the core library may complete a future
+/// as the other future once that other future itself completes.
+/// Most functions in the platform libraries that complete a future
 /// (for example [Completer.complete] or [Future.value]),
-/// then it also accepts another future and does this work for the developer.
+/// also accepts another future, and automatically handles forwarding
+/// the result to the future being completed.
 ///
-/// The result of registering a pair of callbacks is a Future (the
-/// "successor") which in turn is completed with the result of invoking the
-/// corresponding callback.
-/// The successor is completed with an error if the invoked callback throws.
+/// The result of registering callbacks is itself a `Future`,
+/// which in turn is completed with the result of invoking the
+/// corresponding callback with the original future's result.
+/// The new future is completed with an error if the invoked callback throws.
 /// For example:
 /// ```dart
 /// Future<int> successor = future.then((int value) {
@@ -88,9 +162,9 @@
 ///   });
 /// ```
 ///
-/// If a future does not have a successor when it completes with an error,
-/// it forwards the error message to an uncaught-error handler.
-/// This behavior makes sure that no error is silently dropped.
+/// If a future does not have any registered handler when it completes
+/// with an error, it forwards the error to an "uncaught-error handler".
+/// This behavior ensures that no error is silently dropped.
 /// However, it also means that error handlers should be installed early,
 /// so that they are present as soon as a future is completed with an error.
 /// The following example demonstrates this potential bug:
@@ -128,9 +202,12 @@
 ///
 /// Equivalent asynchronous code, based on futures:
 /// ```dart
-/// Future<int> future = Future(foo);  // Result of foo() as a future.
-/// future.then((int value) => bar(value))
-///       .catchError((e) => 499);
+/// Future<int> asyncValue = Future(foo);  // Result of foo() as a future.
+/// asyncValue.then((int value) {
+///   return bar(value);
+/// }).catchError((e) {
+///   return 499;
+/// });
 /// ```
 ///
 /// Similar to the synchronous code, the error handler (registered with
@@ -142,7 +219,8 @@
 /// treated independently and is handled as if it was the only successor.
 ///
 /// A future may also fail to ever complete. In that case, no callbacks are
-/// called.
+/// called. That situation should generally be avoided if possible, unless
+/// it's very clearly documented.
 abstract class Future<T> {
   /// A `Future<Null>` completed with `null`.
   ///
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 7a3c80d..2083510 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -18,9 +18,61 @@
 /// When a stream has emitted all its event,
 /// a single "done" event will notify the listener that the end has been reached.
 ///
-/// You [listen] on a stream to make it start generating events,
-/// and to set up listeners that receive the events.
-/// When you listen, you receive a [StreamSubscription] object
+/// You produce a stream by calling an `async*` function, which then returns
+/// a stream. Consuming that stream will lead the function to emit events
+/// until it ends, and the stream closes.
+/// You consume a stream either using an `await for` loop, which is available
+/// inside an `async` or `async*` function, or forwards its events directly
+/// using `yield*` inside an `async*` function.
+/// Example:
+/// ```dart
+/// Stream<T> optionalMap<T>(
+///     Stream<T> source , [T Function(T)? convert]) async* {
+///   if (convert == null) {
+///     yield* source;
+///   } else {
+///     await for (var event in source) {
+///       yield convert(event);
+///     }
+///   }
+/// }
+/// ```
+/// When this function is called, it immediately returns a `Stream<T>` object.
+/// Then nothing further happens until someone tries to consume that stream.
+/// At that point, the body of the `async*` function starts running.
+/// If the `convert` function was omitted, the `yield*` will listen to the
+/// `source` stream and forward all events, date and errors, to the returned
+/// stream. When the `source` stream closes, the `yield*` is done,
+/// and the `optionalMap` function body ends too. This closes the returned
+/// stream.
+/// If a `convert` *is* supplied, the function instead listens on the source
+/// stream and enters an `await for` loop which
+/// repeatedly waits for the next data event.
+/// On a data event, it calls `convert` with the value and emits the result
+/// on the returned stream.
+/// If no error events are emitted by the `source` stream,
+/// the loop ends when the `source` stream does,
+/// then the `optionalMap` function body completes,
+/// which closes the returned stream.
+/// On an error event from the `source` stream,
+/// the `await for` that error is (re-)thrown which breaks the loop.
+/// The error then reaches the end of the `optionalMap` function body,
+/// since it's not caught.
+/// That makes the error be emitted on the returned stream, which then closes.
+///
+/// The `Stream` class also provides functionality which allows you to
+/// manually listen for events from a stream, or to convert a stream
+/// into another stream or into a future.
+///
+/// The [forEach] function corresponds to the `await for` loop,
+/// just as [Iterable.forEach] corresponds to a normal `for`/`in` loop.
+/// Like the loop, it will call a function for each data event and break on an
+/// error.
+///
+/// The more low-level [listen] method is what every other method is based on.
+/// You call `listen` on a stream to tell it that you want to receive
+/// events, and to registers the callbacks which will receive those events.
+/// When you call `listen`, you receive a [StreamSubscription] object
 /// which is the active object providing the events,
 /// and which can be used to stop listening again,
 /// or to temporarily pause events from the subscription.
@@ -33,19 +85,21 @@
 /// It doesn't start generating events until it has a listener,
 /// and it stops sending events when the listener is unsubscribed,
 /// even if the source of events could still provide more.
+/// The stream created by an `async*` function is a single-subscription stream,
+/// but each call to the function creates a new such stream.
 ///
 /// Listening twice on a single-subscription stream is not allowed, even after
 /// the first subscription has been canceled.
 ///
 /// Single-subscription streams are generally used for streaming chunks of
-/// larger contiguous data like file I/O.
+/// larger contiguous data, like file I/O.
 ///
 /// *A broadcast stream* allows any number of listeners, and it fires
 /// its events when they are ready, whether there are listeners or not.
 ///
 /// Broadcast streams are used for independent events/observers.
 ///
-/// If several listeners want to listen to a single subscription stream,
+/// If several listeners want to listen to a single-subscription stream,
 /// use [asBroadcastStream] to create a broadcast stream on top of the
 /// non-broadcast stream.
 ///
@@ -77,7 +131,8 @@
 ///
 /// The default implementation of [isBroadcast] returns false.
 /// A broadcast stream inheriting from [Stream] must override [isBroadcast]
-/// to return `true`.
+/// to return `true` if it wants to signal that it behaves like a broadcast
+/// stream.
 abstract class Stream<T> {
   Stream();
 
@@ -85,6 +140,7 @@
   ///
   /// If mixins become compatible with const constructors, we may use a
   /// stream mixin instead of extending Stream from a const class.
+  /// (They now are compatible. We still consider, but it's not urgent.)
   const Stream._internal();
 
   /// Creates an empty broadcast stream.
@@ -93,10 +149,10 @@
   /// when it's listened to.
   const factory Stream.empty() = _EmptyStream<T>;
 
-  /// Creates a stream which emits a single data event before completing.
+  /// Creates a stream which emits a single data event before closing.
   ///
   /// This stream emits a single data event of [value]
-  /// and then completes with a done event.
+  /// and then closes with a done event.
   ///
   /// Example:
   /// ```dart
diff --git a/sdk/lib/html/dartium/nativewrappers.dart b/sdk/lib/html/dartium/nativewrappers.dart
index 39004b7..6aa47cd 100644
--- a/sdk/lib/html/dartium/nativewrappers.dart
+++ b/sdk/lib/html/dartium/nativewrappers.dart
@@ -20,4 +20,4 @@
 /// future.
 @pragma("vm:recognized", "other")
 @pragma("vm:external-name", "FullyRecognizedMethod_NoNative")
-external int getNativeField(NativeFieldWrapperClass1 object);
+external int _getNativeField(NativeFieldWrapperClass1 object);
diff --git a/tests/ffi/vmspecific_ffi_native_test.dart b/tests/ffi/vmspecific_ffi_native_test.dart
index 5264901..f9e1e39 100644
--- a/tests/ffi/vmspecific_ffi_native_test.dart
+++ b/tests/ffi/vmspecific_ffi_native_test.dart
@@ -122,7 +122,7 @@
   Expect.equals(123456, passAsPointer(cwnf));
 
   // Test that the transform to wrap NativeFieldWrapperClass1 objects in
-  // getNativeField(..) doesn't violate the original argument's liveness.
+  // _getNativeField(..) doesn't violate the original argument's liveness.
   Expect.equals(
       314159,
       passAsPointerAndValue(
diff --git a/tests/standalone/check_for_aot_snapshot_jit_test.dart b/tests/standalone/check_for_aot_snapshot_jit_test.dart
index d8b817c..92f39cd 100644
--- a/tests/standalone/check_for_aot_snapshot_jit_test.dart
+++ b/tests/standalone/check_for_aot_snapshot_jit_test.dart
@@ -33,8 +33,8 @@
   Expect.isTrue(File(powTest).existsSync(),
       "Can't locate dart$_execSuffix on this platform");
   final d = Directory.systemTemp.createTempSync('aot_tmp');
-  final kernelOutput = d.uri.resolve('pow_test.dill').path;
-  final aotOutput = d.uri.resolve('pow_test.aot').path;
+  final kernelOutput = File.fromUri(d.uri.resolve('pow_test.dill')).path;
+  final aotOutput = File.fromUri(d.uri.resolve('pow_test.aot')).path;
 
   final genKernelResult = runAndPrintOutput(
     genKernel,
diff --git a/tests/standalone/io/process_run_test.dart b/tests/standalone/io/process_run_test.dart
index a4a470a..c229570 100644
--- a/tests/standalone/io/process_run_test.dart
+++ b/tests/standalone/io/process_run_test.dart
@@ -49,8 +49,7 @@
     result = Process.runSync('${processTest.path}', []);
     Process.killPid(result.pid);
   } catch (e) {
-    Expect.fail('System should find process_run_test executable');
-    print(e);
+    Expect.fail('System should find process_run_test executable ($e)');
   } finally {
     // Clean up the temp files and directory
     dir.deleteSync(recursive: true);
diff --git a/tests/standalone_2/check_for_aot_snapshot_jit_test.dart b/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
index e9c3e24..d446a7b 100644
--- a/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
+++ b/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
@@ -33,8 +33,8 @@
   Expect.isTrue(File(powTest).existsSync(),
       "Can't locate dart$_execSuffix on this platform");
   final d = Directory.systemTemp.createTempSync('aot_tmp');
-  final kernelOutput = d.uri.resolve('pow_test.dill').path;
-  final aotOutput = d.uri.resolve('pow_test.aot').path;
+  final kernelOutput = File.fromUri(d.uri.resolve('pow_test.dill')).path;
+  final aotOutput = File.fromUri(d.uri.resolve('pow_test.aot')).path;
 
   final genKernelResult = runAndPrintOutput(
     genKernel,
diff --git a/tests/standalone_2/io/process_run_test.dart b/tests/standalone_2/io/process_run_test.dart
index 5dfe13c..342b3c5 100644
--- a/tests/standalone_2/io/process_run_test.dart
+++ b/tests/standalone_2/io/process_run_test.dart
@@ -51,8 +51,7 @@
     result = Process.runSync('${processTest.path}', []);
     Process.killPid(result.pid);
   } catch (e) {
-    Expect.fail('System should find process_run_test executable');
-    print(e);
+    Expect.fail('System should find process_run_test executable ($e)');
   } finally {
     // Clean up the temp files and directory
     dir.deleteSync(recursive: true);
diff --git a/tools/VERSION b/tools/VERSION
index b5b8743..7776bae 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 146
+PRERELEASE 147
 PRERELEASE_PATCH 0
\ No newline at end of file