Version 2.18.0-73.0.dev

Merge commit 'a0b35de326441b462f6fe0fb3a2b18bb10a40390' into 'dev'
diff --git a/pkg/compiler/lib/src/io/source_file.dart b/pkg/compiler/lib/src/io/source_file.dart
index 489f6c6..5fe6a18 100644
--- a/pkg/compiler/lib/src/io/source_file.dart
+++ b/pkg/compiler/lib/src/io/source_file.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.10
-
 library dart2js.io.source_file;
 
 import 'dart:convert' show utf8;
@@ -25,12 +23,12 @@
   @override
   api.InputKind get inputKind => api.InputKind.UTF8;
 
-  kernel.Source cachedKernelSource;
+  kernel.Source? _cachedKernelSource;
 
   kernel.Source get kernelSource {
     // TODO(johnniwinther): Instead of creating a new Source object,
     // we should use the one provided by the front-end.
-    return cachedKernelSource ??= kernel.Source(
+    return _cachedKernelSource ??= kernel.Source(
         lineStarts,
         slowUtf8ZeroTerminatedBytes(),
         uri /* TODO(jensj): What is the import URI? */,
@@ -62,12 +60,10 @@
   /// A map from line numbers to offsets in the string text representation of
   /// this source file.
   List<int> get lineStarts {
-    if (lineStartsCache == null) {
-      // When reporting errors during scanning, the line numbers are not yet
-      // available and need to be computed using this slow path.
-      lineStartsCache = lineStartsFromString(slowText());
-    }
-    return lineStartsCache;
+    // When reporting errors during scanning, the line numbers are not yet
+    // available and need to be computed using this slow path.
+    // TODO(sra): Scanning is now done entirely by the CFE.
+    return _lineStartsCache ??= _lineStartsFromString(slowText());
   }
 
   /// Sets the line numbers map for this source file. This map is computed and
@@ -76,11 +72,11 @@
   /// The map contains one additional entry at the end of the file, as if the
   /// source file had one more empty line at the end. This simplifies the binary
   /// search in [getLocation].
-  set lineStarts(List<int> v) => lineStartsCache = v;
+  set lineStarts(List<int> v) => _lineStartsCache = v;
 
-  List<int> lineStartsCache;
+  List<int>? _lineStartsCache;
 
-  List<int> lineStartsFromString(String text) {
+  static List<int> _lineStartsFromString(String text) {
     var starts = [0];
     var index = 0;
     while (index < text.length) {
@@ -94,7 +90,7 @@
 
   @override
   kernel.Location getLocation(int offset) {
-    return kernelSource.getLocation(null, offset);
+    return kernelSource.getLocation(uri, offset);
   }
 
   String slowSubstring(int start, int end);
@@ -109,13 +105,13 @@
   /// Use [colorize] to wrap source code text and marker characters in color
   /// escape codes.
   String getLocationMessage(String message, int start, int end,
-      {bool includeSourceLine = true, String colorize(String text)}) {
+      {bool includeSourceLine = true, String colorize(String text)?}) {
     if (colorize == null) {
       colorize = (text) => text;
     }
 
-    kernel.Location startLocation = kernelSource.getLocation(null, start);
-    kernel.Location endLocation = kernelSource.getLocation(null, end);
+    kernel.Location startLocation = kernelSource.getLocation(uri, start);
+    kernel.Location endLocation = kernelSource.getLocation(uri, end);
     int lineStart = startLocation.line - 1;
     int columnStart = startLocation.column - 1;
     int lineEnd = endLocation.line - 1;
@@ -130,7 +126,7 @@
 
     if (start != end && includeSourceLine) {
       if (lineStart == lineEnd) {
-        String textLine = kernelSource.getTextLine(startLocation.line);
+        String textLine = kernelSource.getTextLine(startLocation.line)!;
 
         int toColumn = min(columnStart + (end - start), textLine.length);
         buf.write(textLine.substring(0, columnStart));
@@ -147,7 +143,7 @@
         }
       } else {
         for (int line = lineStart; line <= lineEnd; line++) {
-          String textLine = kernelSource.getTextLine(line + 1);
+          String textLine = kernelSource.getTextLine(line + 1)!;
           if (line == lineStart) {
             if (columnStart > textLine.length) {
               columnStart = textLine.length;
@@ -233,7 +229,7 @@
 }
 
 class CachingUtf8BytesSourceFile extends Utf8BytesSourceFile {
-  String cachedText;
+  String? _cachedText;
   @override
   final String filename;
 
@@ -242,15 +238,12 @@
 
   @override
   String slowText() {
-    if (cachedText == null) {
-      cachedText = super.slowText();
-    }
-    return cachedText;
+    return _cachedText ??= super.slowText();
   }
 
   @override
   void release() {
-    cachedText = null;
+    _cachedText = null;
     super.release();
   }
 }
@@ -297,13 +290,13 @@
 class Binary implements api.Input<List<int>> {
   @override
   final Uri uri;
-  List<int> /*?*/ _data;
+  List<int>? _data;
 
   Binary(this.uri, List<int> data) : _data = data;
 
   @override
   List<int> get data {
-    if (_data != null) return _data;
+    if (_data != null) return _data!;
     throw StateError("'get data' after 'release()'");
   }
 
diff --git a/pkg/compiler/lib/src/js/js_source_mapping.dart b/pkg/compiler/lib/src/js/js_source_mapping.dart
index d5b4383..fc932c3 100644
--- a/pkg/compiler/lib/src/js/js_source_mapping.dart
+++ b/pkg/compiler/lib/src/js/js_source_mapping.dart
@@ -10,7 +10,7 @@
     show BufferedCodeOutput, SourceLocations, SourceLocationsProvider;
 import '../io/source_information.dart'
     show SourceLocation, SourceInformation, SourceInformationStrategy;
-import 'js.dart';
+import 'js.dart' show Node;
 
 /// [SourceInformationStrategy] that can associate source information with
 /// JavaScript output.
diff --git a/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
index fe6ef0c..6098493 100644
--- a/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
@@ -6,6 +6,7 @@
 
 library dart2js.js_emitter.main_call_stub_generator;
 
+import 'package:compiler/src/options.dart';
 import 'package:js_runtime/shared/embedded_names.dart' as embeddedNames;
 
 import '../common/elements.dart';
@@ -16,8 +17,12 @@
 import 'code_emitter_task.dart' show Emitter;
 
 class MainCallStubGenerator {
-  static jsAst.Statement generateInvokeMain(CommonElements commonElements,
-      Emitter emitter, FunctionEntity main, bool requiresStartupMetrics) {
+  static jsAst.Statement generateInvokeMain(
+      CommonElements commonElements,
+      Emitter emitter,
+      FunctionEntity main,
+      bool requiresStartupMetrics,
+      CompilerOptions options) {
     jsAst.Expression mainAccess = emitter.staticFunctionAccess(main);
     jsAst.Expression currentScriptAccess =
         emitter.generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT);
@@ -96,6 +101,10 @@
         if (#startupMetrics) {
           init.#startupMetricsEmbeddedGlobal.add('callMainMs');
         }
+        if (#isCollectingRuntimeMetrics) {
+          self.#runtimeMetricsContainer = self.#runtimeMetricsContainer || Object.create(null);
+          self.#runtimeMetricsContainer[currentScript.src] = init.#runtimeMetricsEmbeddedGlobal;
+        }
         var callMain = #mainCallClosure;
         if (typeof dartMainRunner === "function") {
           dartMainRunner(callMain, []);
@@ -105,6 +114,9 @@
       })''', {
       'currentScript': currentScriptAccess,
       'mainCallClosure': mainCallClosure,
+      'isCollectingRuntimeMetrics': options.experimentalTrackAllocations,
+      'runtimeMetricsContainer': embeddedNames.RUNTIME_METRICS_CONTAINER,
+      'runtimeMetricsEmbeddedGlobal': embeddedNames.RUNTIME_METRICS,
       'startupMetrics': requiresStartupMetrics,
       'startupMetricsEmbeddedGlobal': embeddedNames.STARTUP_METRICS,
     });
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index 5068dda..b9f1535 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -296,8 +296,12 @@
   }
 
   js.Statement _buildInvokeMain() {
-    return MainCallStubGenerator.generateInvokeMain(_commonElements,
-        _task.emitter, _mainFunction, _backendUsage.requiresStartupMetrics);
+    return MainCallStubGenerator.generateInvokeMain(
+        _commonElements,
+        _task.emitter,
+        _mainFunction,
+        _backendUsage.requiresStartupMetrics,
+        _options);
   }
 
   DeferredFragment _buildDeferredFragment(LibrariesMap librariesMap) {
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 66db107..fc83adb 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -46,6 +46,11 @@
       .add("dartProgramMs");
 }
 
+if (#isCollectingRuntimeMetrics) {
+  dartProgram.$RUNTIME_METRICS = Object.create(null);
+  var allocations = dartProgram.$RUNTIME_METRICS['allocations'] = Object.create(null);
+}
+
 // Copies the own properties from [from] to [to].
 function copyProperties(from, to) {
   var keys = Object.keys(from);
@@ -420,10 +425,6 @@
   hunk(hunkHelpers, #embeddedGlobalsObject, holders, #staticState);
 }
 
-if (#isTrackingAllocations) {
-  var allocations = #deferredGlobal['allocations'] = {};
-}
-
 // Creates the holders.
 #holders;
 
@@ -523,7 +524,9 @@
 /// This template is used for Dart 2.
 const String _deferredBoilerplate = '''
 function(hunkHelpers, #embeddedGlobalsObject, holdersList, #staticState) {
-
+if (#isCollectingRuntimeMetrics) {
+  var allocations = #embeddedGlobalsObject.#runtimeMetrics['allocations'];
+}
 // Builds the holders. They only contain the data for new holders.
 // If names are not set on functions, we do it now. Finally, updates the
 // holders of the main-fragment. Uses the provided holdersList to access the
@@ -751,8 +754,7 @@
       //'stubName': js.string(_namer.stubNameField),
       //'argumentCount': js.string(_namer.fixedNames.requiredParameterField),
       //'defaultArgumentValues': js.string(_namer.fixedNames.defaultValuesField),
-      'deferredGlobal': ModelEmitter.deferredInitializersGlobal,
-      'isTrackingAllocations': _options.experimentalTrackAllocations,
+      'isCollectingRuntimeMetrics': _options.experimentalTrackAllocations,
       'prototypes': emitPrototypes(fragment),
       'inheritance': emitInheritance(fragment),
       'aliases': emitInstanceMethodAliases(fragment),
@@ -808,7 +810,9 @@
     js.Expression code = js.js(_deferredBoilerplate, {
       // TODO(floitsch): don't just reference 'init'.
       'embeddedGlobalsObject': js.Parameter('init'),
+      'isCollectingRuntimeMetrics': _options.experimentalTrackAllocations,
       'staticState': DeferredHolderParameter(),
+      'runtimeMetrics': RUNTIME_METRICS,
       'updateHolders': updateHolders,
       'prototypes': fragment.classPrototypes,
       'closures': fragment.closurePrototypes,
@@ -1875,6 +1879,12 @@
           js.string(STARTUP_METRICS), js.js('dartProgram.$STARTUP_METRICS')));
     }
 
+    if (_options.experimentalTrackAllocations) {
+      // Copy the metrics object that was stored on the main unit IIFE.
+      globals.add(js.Property(
+          js.string(RUNTIME_METRICS), js.js('dartProgram.$RUNTIME_METRICS')));
+    }
+
     js.ObjectInitializer globalsObject =
         js.ObjectInitializer(globals, isOneLiner: false);
 
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 242dc31..b5bf78d 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -27,6 +27,7 @@
         NATIVE_SUPERCLASS_TAG_NAME,
         RTI_UNIVERSE,
         RtiUniverseFieldNames,
+        RUNTIME_METRICS,
         STARTUP_METRICS,
         TearOffParametersPropertyNames,
         TYPE_TO_INTERCEPTOR_MAP,
@@ -368,19 +369,11 @@
     return js.Comment(generatedBy(_options, flavor: '$flavor'));
   }
 
-  List<js.Statement> buildDeferredInitializerGlobal() {
-    return [
-      js.js.statement(
-          'self.#deferredInitializers = '
-          'self.#deferredInitializers || Object.create(null);',
-          {'deferredInitializers': deferredInitializersGlobal}),
-      if (_options.experimentalTrackAllocations)
-        js.js.statement(
-            'self.#deferredInitializers["allocations"] = '
-            'self.#deferredInitializers["allocations"] '
-            '|| Object.create(null)',
-            {'deferredInitializers': deferredInitializersGlobal})
-    ];
+  js.Statement buildDeferredInitializerGlobal() {
+    return js.js.statement(
+        'self.#deferredInitializers = '
+        'self.#deferredInitializers || Object.create(null);',
+        {'deferredInitializers': deferredInitializersGlobal});
   }
 
   js.Statement buildStartupMetrics() {
@@ -431,7 +424,7 @@
     js.Program program = js.Program([
       buildGeneratedBy(),
       js.Comment(HOOKS_API_USAGE),
-      if (isSplit) ...buildDeferredInitializerGlobal(),
+      if (isSplit) buildDeferredInitializerGlobal(),
       if (_closedWorld.backendUsage.requiresStartupMetrics)
         buildStartupMetrics(),
       code
@@ -566,10 +559,7 @@
 
     js.Program program = js.Program([
       if (isFirst) buildGeneratedBy(),
-      if (isFirst) ...buildDeferredInitializerGlobal(),
-      if (_options.experimentalTrackAllocations)
-        js.js.statement("var allocations = #deferredGlobal['allocations']",
-            {'deferredGlobal': deferredInitializersGlobal}),
+      if (isFirst) buildDeferredInitializerGlobal(),
       js.js.statement('$deferredInitializersGlobal.current = #', code)
     ]);
 
diff --git a/pkg/dart2js_runtime_metrics/CHANGELOG.md b/pkg/dart2js_runtime_metrics/CHANGELOG.md
index a0712a7..b7d9e52 100644
--- a/pkg/dart2js_runtime_metrics/CHANGELOG.md
+++ b/pkg/dart2js_runtime_metrics/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.0
+
+- Adding `runtimeMetrics`.
+
 ## 0.1.0
 
 - Initial version.
diff --git a/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_dart2js.dart b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_dart2js.dart
new file mode 100644
index 0000000..3aa7cbb
--- /dev/null
+++ b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_dart2js.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+export 'dart:_dart2js_runtime_metrics' show runtimeMetrics;
diff --git a/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_dartdevc.dart b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_dartdevc.dart
new file mode 100644
index 0000000..43382b8
--- /dev/null
+++ b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_dartdevc.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A collection of metrics collected during the runtime of a Dart app.
+///
+/// The contents of the map depend on the platform. The map values are simple
+/// objects (strings, numbers, Booleans). There is always an entry for the key
+/// `'runtime'` with a [String] value.
+Map<String, Object> get runtimeMetrics {
+  return {'runtime': 'dartdevc'};
+}
diff --git a/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_unknown.dart b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_unknown.dart
new file mode 100644
index 0000000..41659d2
--- /dev/null
+++ b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_unknown.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A collection of metrics collected during the runtime of a Dart app.
+///
+/// The contents of the map depend on the platform. The map values are simple
+/// objects (strings, numbers, Booleans). There is always an entry for the key
+/// `'runtime'` with a [String] value.
+Map<String, Object> get runtimeMetrics {
+  return {'runtime': 'unknown'};
+}
diff --git a/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_vm.dart b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_vm.dart
new file mode 100644
index 0000000..8c4a779
--- /dev/null
+++ b/pkg/dart2js_runtime_metrics/lib/_runtime_metrics_vm.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A collection of metrics collected during the runtime of a Dart app.
+///
+/// The contents of the map depend on the platform. The map values are simple
+/// objects (strings, numbers, Booleans). There is always an entry for the key
+/// `'runtime'` with a [String] value.
+Map<String, Object> get runtimeMetrics {
+  return {'runtime': 'vm'};
+}
diff --git a/pkg/dart2js_runtime_metrics/lib/runtime_metrics.dart b/pkg/dart2js_runtime_metrics/lib/runtime_metrics.dart
new file mode 100644
index 0000000..1ae29fd
--- /dev/null
+++ b/pkg/dart2js_runtime_metrics/lib/runtime_metrics.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+export '_runtime_metrics_unknown.dart'
+    if (dart.library._dart2js_runtime_metrics) '_runtime_metrics_dart2js.dart'
+    if (dart.library.ffi) '_runtime_metrics_vm.dart'
+    if (dart.library.js) '_runtime_metrics_dartdevc.dart';
diff --git a/pkg/dart2js_runtime_metrics/pubspec.yaml b/pkg/dart2js_runtime_metrics/pubspec.yaml
index 7b9b439..31c227f5 100644
--- a/pkg/dart2js_runtime_metrics/pubspec.yaml
+++ b/pkg/dart2js_runtime_metrics/pubspec.yaml
@@ -1,7 +1,7 @@
 name: dart2js_runtime_metrics
 # This package is not intended for consumption on pub.dev. DO NOT publish.
 publish_to: none
-version: 0.1.0
+version: 0.2.0
 description: >-
   `dart2js` can generate extra code to measure certain activities.
   This library provides access to the measurements at runtime.
diff --git a/pkg/dart2js_runtime_metrics/test/runtime_metrics_test.dart b/pkg/dart2js_runtime_metrics/test/runtime_metrics_test.dart
new file mode 100644
index 0000000..8b0d0b5
--- /dev/null
+++ b/pkg/dart2js_runtime_metrics/test/runtime_metrics_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// dart2jsOptions=--experimental-track-allocations
+
+import 'package:dart2js_runtime_metrics/runtime_metrics.dart';
+import 'package:expect/expect.dart';
+
+void main() {
+  Map<String, Object> metrics = runtimeMetrics;
+
+  print('metrics: $metrics');
+
+  String expectedRuntime;
+  if (1.0 is! int) {
+    expectedRuntime = 'vm';
+  } else if (ClassWithLongName().toString().contains('minified:')) {
+    // dart2js minified: "Instance of 'minified:xy'".
+    expectedRuntime = 'dart2js';
+  } else if ('$main' == "Closure 'main'") {
+    // dart2js non-minified.
+    expectedRuntime = 'dart2js';
+  } else if ('$main'.startsWith('Closure: () => void from: function main()')) {
+    expectedRuntime = 'dartdevc';
+  } else {
+    throw 'Cannot feature-test current runtime:'
+        '\nmetrics = $metrics\n main = $main';
+  }
+
+  Expect.isTrue(metrics.containsKey('runtime'), "Has 'runtime' key: $metrics");
+  Expect.equals(expectedRuntime, metrics['runtime'],
+      "Expected 'runtime: $expectedRuntime': $metrics");
+
+  if (expectedRuntime == 'dart2js') {
+    Expect.isTrue(metrics.containsKey('allocations'));
+    return;
+  }
+
+  if (expectedRuntime == 'dartdevc') {
+    return;
+  }
+
+  if (expectedRuntime == 'vm') {
+    return;
+  }
+
+  throw 'Should not get here.';
+}
+
+class ClassWithLongName {}
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 415fe83..7da2ade 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -2462,8 +2462,12 @@
             "invocation.");
         continue;
       }
+      Expression unparenthesizedExpression = argumentExpression;
+      while (unparenthesizedExpression is ParenthesizedExpression) {
+        unparenthesizedExpression = unparenthesizedExpression.expression;
+      }
       if (isInferenceUpdate1Enabled &&
-          argumentExpression is FunctionExpression) {
+          unparenthesizedExpression is FunctionExpression) {
         (deferredFunctionLiterals ??= []).add(new _DeferredParamInfo(
             formalType: formalType,
             argumentExpression: argumentExpression,
@@ -5942,8 +5946,9 @@
   /// argument.
   final DartType formalType;
 
-  /// The function literal expression.
-  final FunctionExpression argumentExpression;
+  /// The argument expression (possibly wrapped in an arbitrary number of
+  /// ParenthesizedExpressions).
+  final Expression argumentExpression;
 
   /// Indicates whether this is a named argument.
   final bool isNamed;
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 103c5d1..a7fc5e8 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -1430,6 +1430,7 @@
 unneeded
 unordered
 unpaired
+unparenthesized
 unparsed
 unpleasant
 unqualified
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart
index 454d46f..d3cb021 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart
@@ -17,6 +17,24 @@
   }
 }
 
+withUnnamedArgumentsParenthesized(int? i, void Function(void Function(), Object?) f) {
+  if (i != null) {
+    f((() {
+      i = null;
+    }), i);
+    i;
+  }
+}
+
+withUnnamedArgumentsParenthesizedTwice(int? i, void Function(void Function(), Object?) f) {
+  if (i != null) {
+    f(((() {
+      i = null;
+    })), i);
+    i;
+  }
+}
+
 withNamedArguments(
     int? i, void Function({required void Function() g, Object? x}) f) {
   if (i != null) {
@@ -29,6 +47,30 @@
   }
 }
 
+withNamedArgumentsParenthesized(
+    int? i, void Function({required void Function() g, Object? x}) f) {
+  if (i != null) {
+    f(
+        g: (() {
+          i = null;
+        }),
+        x: i);
+    i;
+  }
+}
+
+withNamedArgumentsParenthesizedTwice(
+    int? i, void Function({required void Function() g, Object? x}) f) {
+  if (i != null) {
+    f(
+        g: ((() {
+          i = null;
+        })),
+        x: i);
+    i;
+  }
+}
+
 withIdentical_lhs(int? i) {
   if (i != null) {
     i;
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline.expect b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline.expect
index b846752..b9ed055 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline.expect
@@ -1,6 +1,14 @@
 withUnnamedArguments(int? i, void Function(void Function(), Object?) f) {}
+withUnnamedArgumentsParenthesized(
+    int? i, void Function(void Function(), Object?) f) {}
+withUnnamedArgumentsParenthesizedTwice(
+    int? i, void Function(void Function(), Object?) f) {}
 withNamedArguments(
     int? i, void Function({required void Function() g, Object? x}) f) {}
+withNamedArgumentsParenthesized(
+    int? i, void Function({required void Function() g, Object? x}) f) {}
+withNamedArgumentsParenthesizedTwice(
+    int? i, void Function({required void Function() g, Object? x}) f) {}
 withIdentical_lhs(int? i) {}
 withIdentical_rhs(int? i) {}
 
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline_modelled.expect
index f4cf2ee..1d74f38 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.textual_outline_modelled.expect
@@ -18,4 +18,12 @@
 withIdentical_rhs(int? i) {}
 withNamedArguments(
     int? i, void Function({required void Function() g, Object? x}) f) {}
+withNamedArgumentsParenthesized(
+    int? i, void Function({required void Function() g, Object? x}) f) {}
+withNamedArgumentsParenthesizedTwice(
+    int? i, void Function({required void Function() g, Object? x}) f) {}
 withUnnamedArguments(int? i, void Function(void Function(), Object?) f) {}
+withUnnamedArgumentsParenthesized(
+    int? i, void Function(void Function(), Object?) f) {}
+withUnnamedArgumentsParenthesizedTwice(
+    int? i, void Function(void Function(), Object?) f) {}
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.expect b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.expect
index 7e1a0ea..e0d8d30 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.expect
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.expect
@@ -27,6 +27,22 @@
     i;
   }
 }
+static method withUnnamedArgumentsParenthesized(core::int? i, (() → void, core::Object?) → void f) → dynamic {
+  if(!(i == null)) {
+    f(() → void {
+      i = null;
+    }, i{core::int}){(() → void, core::Object?) → void};
+    i;
+  }
+}
+static method withUnnamedArgumentsParenthesizedTwice(core::int? i, (() → void, core::Object?) → void f) → dynamic {
+  if(!(i == null)) {
+    f(() → void {
+      i = null;
+    }, i{core::int}){(() → void, core::Object?) → void};
+    i;
+  }
+}
 static method withNamedArguments(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
   if(!(i == null)) {
     f(g: () → void {
@@ -35,6 +51,22 @@
     i;
   }
 }
+static method withNamedArgumentsParenthesized(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
+  if(!(i == null)) {
+    f(g: () → void {
+      i = null;
+    }, x: i{core::int}){({required g: () → void, x: core::Object?}) → void};
+    i;
+  }
+}
+static method withNamedArgumentsParenthesizedTwice(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
+  if(!(i == null)) {
+    f(g: () → void {
+      i = null;
+    }, x: i{core::int}){({required g: () → void, x: core::Object?}) → void};
+    i;
+  }
+}
 static method withIdentical_lhs(core::int? i) → dynamic {
   if(!(i == null)) {
     i{core::int};
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.modular.expect b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.modular.expect
index 7e1a0ea..e0d8d30 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.modular.expect
@@ -27,6 +27,22 @@
     i;
   }
 }
+static method withUnnamedArgumentsParenthesized(core::int? i, (() → void, core::Object?) → void f) → dynamic {
+  if(!(i == null)) {
+    f(() → void {
+      i = null;
+    }, i{core::int}){(() → void, core::Object?) → void};
+    i;
+  }
+}
+static method withUnnamedArgumentsParenthesizedTwice(core::int? i, (() → void, core::Object?) → void f) → dynamic {
+  if(!(i == null)) {
+    f(() → void {
+      i = null;
+    }, i{core::int}){(() → void, core::Object?) → void};
+    i;
+  }
+}
 static method withNamedArguments(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
   if(!(i == null)) {
     f(g: () → void {
@@ -35,6 +51,22 @@
     i;
   }
 }
+static method withNamedArgumentsParenthesized(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
+  if(!(i == null)) {
+    f(g: () → void {
+      i = null;
+    }, x: i{core::int}){({required g: () → void, x: core::Object?}) → void};
+    i;
+  }
+}
+static method withNamedArgumentsParenthesizedTwice(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
+  if(!(i == null)) {
+    f(g: () → void {
+      i = null;
+    }, x: i{core::int}){({required g: () → void, x: core::Object?}) → void};
+    i;
+  }
+}
 static method withIdentical_lhs(core::int? i) → dynamic {
   if(!(i == null)) {
     i{core::int};
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.outline.expect b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.outline.expect
index f98b778..734591f 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.outline.expect
@@ -14,8 +14,16 @@
 }
 static method withUnnamedArguments(core::int? i, (() → void, core::Object?) → void f) → dynamic
   ;
+static method withUnnamedArgumentsParenthesized(core::int? i, (() → void, core::Object?) → void f) → dynamic
+  ;
+static method withUnnamedArgumentsParenthesizedTwice(core::int? i, (() → void, core::Object?) → void f) → dynamic
+  ;
 static method withNamedArguments(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic
   ;
+static method withNamedArgumentsParenthesized(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic
+  ;
+static method withNamedArgumentsParenthesizedTwice(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic
+  ;
 static method withIdentical_lhs(core::int? i) → dynamic
   ;
 static method withIdentical_rhs(core::int? i) → dynamic
diff --git a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.transformed.expect b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.transformed.expect
index 7e1a0ea..e0d8d30 100644
--- a/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference_update_1/write_capture_deferral.dart.weak.transformed.expect
@@ -27,6 +27,22 @@
     i;
   }
 }
+static method withUnnamedArgumentsParenthesized(core::int? i, (() → void, core::Object?) → void f) → dynamic {
+  if(!(i == null)) {
+    f(() → void {
+      i = null;
+    }, i{core::int}){(() → void, core::Object?) → void};
+    i;
+  }
+}
+static method withUnnamedArgumentsParenthesizedTwice(core::int? i, (() → void, core::Object?) → void f) → dynamic {
+  if(!(i == null)) {
+    f(() → void {
+      i = null;
+    }, i{core::int}){(() → void, core::Object?) → void};
+    i;
+  }
+}
 static method withNamedArguments(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
   if(!(i == null)) {
     f(g: () → void {
@@ -35,6 +51,22 @@
     i;
   }
 }
+static method withNamedArgumentsParenthesized(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
+  if(!(i == null)) {
+    f(g: () → void {
+      i = null;
+    }, x: i{core::int}){({required g: () → void, x: core::Object?}) → void};
+    i;
+  }
+}
+static method withNamedArgumentsParenthesizedTwice(core::int? i, ({required g: () → void, x: core::Object?}) → void f) → dynamic {
+  if(!(i == null)) {
+    f(g: () → void {
+      i = null;
+    }, x: i{core::int}){({required g: () → void, x: core::Object?}) → void};
+    i;
+  }
+}
 static method withIdentical_lhs(core::int? i) → dynamic {
   if(!(i == null)) {
     i{core::int};
diff --git a/pkg/js_runtime/lib/shared/embedded_names.dart b/pkg/js_runtime/lib/shared/embedded_names.dart
index 0334810..9d48e1a 100644
--- a/pkg/js_runtime/lib/shared/embedded_names.dart
+++ b/pkg/js_runtime/lib/shared/embedded_names.dart
@@ -179,6 +179,12 @@
 /// This embedded global is used for --experiment-new-rti.
 const RTI_UNIVERSE = 'typeUniverse';
 
+/// An embedded global used to collect and access runtime metrics.
+const RUNTIME_METRICS = 'rm';
+
+/// Global name that holds runtime metrics Dart2JS apps.
+const RUNTIME_METRICS_CONTAINER = 'runtimeMetrics';
+
 /// An embedded global used to collect and access startup metrics.
 const STARTUP_METRICS = 'sm';
 
diff --git a/sdk/lib/_internal/js_runtime/lib/dart2js_runtime_metrics.dart b/sdk/lib/_internal/js_runtime/lib/dart2js_runtime_metrics.dart
index 637d2c7..7077d0a 100644
--- a/sdk/lib/_internal/js_runtime/lib/dart2js_runtime_metrics.dart
+++ b/sdk/lib/_internal/js_runtime/lib/dart2js_runtime_metrics.dart
@@ -4,7 +4,12 @@
 
 library dart2js_runtime_metrics;
 
-import 'dart:_js_helper' show fillLiteralMap, rawStartupMetrics;
+import 'dart:_js_helper'
+    show
+        copyAndStringifyProperties,
+        fillLiteralMap,
+        rawRuntimeMetrics,
+        rawStartupMetrics;
 
 /// A collection of metrics for events that happen before `main()` is entered.
 ///
@@ -35,3 +40,23 @@
   fillLiteralMap(raw, result);
   return result;
 }
+
+/// A collection of metrics collected during the runtime of a Dart app.
+///
+/// The contents of the map depend on the platform. The map values are simple
+/// objects (strings, numbers, Booleans). There is always an entry for the key
+/// `'runtime'` with a [String] value.
+///
+/// This implementation for dart2js has the content (subject to change):
+///
+/// - `runtime`: `'dart2js'`
+///
+/// - `allocations`: A string representation of a Json Map<String, Object>,
+///   which holds every class or closure created at runtime. The key contains
+///   a resolved path of the class or closure. The value is currently unused.
+Map<String, Object> get runtimeMetrics {
+  final Map<String, Object> result = {'runtime': 'dart2js'};
+  final raw = rawRuntimeMetrics();
+  copyAndStringifyProperties(raw, result);
+  return result;
+}
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index ae676e0..50f9c4f 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -21,6 +21,7 @@
         JsGetName,
         LEAF_TAGS,
         NATIVE_SUPERCLASS_TAG_NAME,
+        RUNTIME_METRICS,
         STARTUP_METRICS,
         STATIC_FUNCTION_NAME_PROPERTY_NAME,
         TearOffParametersPropertyNames;
@@ -1801,6 +1802,21 @@
   return result;
 }
 
+/// Called by generated code to move and stringify properties from an object
+/// to a map literal.
+copyAndStringifyProperties(from, Map to) {
+  if (JS('bool', '!#', from)) return to;
+  List keys = JS('JSArray', r'Object.keys(#)', from);
+  int index = 0;
+  int length = getLength(keys);
+  while (index < length) {
+    var key = getIndex(keys, index++);
+    var value = JS('String', r'JSON.stringify(#[#])', from, key);
+    to[key] = value;
+  }
+  return to;
+}
+
 /// Returns the property [index] of the JavaScript array [array].
 getIndex(var array, int index) {
   return JS('var', r'#[#]', array, index);
@@ -3064,6 +3080,10 @@
   return JS('JSArray', '#.a', JS_EMBEDDED_GLOBAL('', STARTUP_METRICS));
 }
 
+Object? rawRuntimeMetrics() {
+  return JS('', '#', JS_EMBEDDED_GLOBAL('', RUNTIME_METRICS));
+}
+
 /// Wraps the given [callback] within the current Zone.
 void Function(T)? wrapZoneUnaryCallback<T>(void Function(T)? callback) {
   // For performance reasons avoid wrapping if we are in the root zone.
diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
index 0334810..9d48e1a 100644
--- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
+++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
@@ -179,6 +179,12 @@
 /// This embedded global is used for --experiment-new-rti.
 const RTI_UNIVERSE = 'typeUniverse';
 
+/// An embedded global used to collect and access runtime metrics.
+const RUNTIME_METRICS = 'rm';
+
+/// Global name that holds runtime metrics Dart2JS apps.
+const RUNTIME_METRICS_CONTAINER = 'runtimeMetrics';
+
 /// An embedded global used to collect and access startup metrics.
 const STARTUP_METRICS = 'sm';
 
diff --git a/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart b/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
index 1a9a79f6..36a8824 100644
--- a/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
+++ b/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
@@ -21,6 +21,26 @@
   }
 }
 
+withUnnamedArgumentsParenthesized(
+    int? i, void Function(void Function(), Object?) f) {
+  if (i != null) {
+    f((() {
+      i = null;
+    }), i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withUnnamedArgumentsParenthesizedTwice(
+    int? i, void Function(void Function(), Object?) f) {
+  if (i != null) {
+    f(((() {
+      i = null;
+    })), i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
 withNamedArguments(
     int? i, void Function({required void Function() g, Object? x}) f) {
   if (i != null) {
@@ -33,6 +53,30 @@
   }
 }
 
+withNamedArgumentsParenthesized(
+    int? i, void Function({required void Function() g, Object? x}) f) {
+  if (i != null) {
+    f(
+        g: (() {
+          i = null;
+        }),
+        x: i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withNamedArgumentsParenthesizedTwice(
+    int? i, void Function({required void Function() g, Object? x}) f) {
+  if (i != null) {
+    f(
+        g: ((() {
+          i = null;
+        })),
+        x: i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
 withIdentical_lhs(int? i) {
   if (i != null) {
     i..expectStaticType<Exactly<int>>();
diff --git a/tools/VERSION b/tools/VERSION
index 7dda1ca..9d184e4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 72
+PRERELEASE 73
 PRERELEASE_PATCH 0
\ No newline at end of file