Version 2.17.0-44.0.dev

Merge commit '90542c2903fddc106fae87859157fd0caf007bc8' into 'dev'
diff --git a/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart b/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart
new file mode 100644
index 0000000..577aee6
--- /dev/null
+++ b/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart
@@ -0,0 +1,130 @@
+// 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.
+
+/// Command-line tool presenting combined information from dump-info and
+/// runtime coverage data.
+///
+/// This tool requires two input files an `.info.data` and a
+/// `.coverage.json` file. To produce these files you need to follow these
+/// steps:
+///
+///   * Compile an app with dart2js using --dump-info and save the .info.data
+///     file:
+///
+///      dart2js --dump-info main.dart
+///
+///   * Build the same app with dart2js using --experimental-track-allocations:
+///
+///      dart2js --experimental-track-allocations main.dart
+///
+///     This can be combined with the --dump-info step above.
+///
+///   * Load your app, exercise your code, then extract the runtime code
+///     coverage JSON blob by querying `$__dart_deferred_initializers__.allocations` in the page.
+///
+///   * Finally, run this tool.
+library compiler.tool.runtime_coverage_analysis;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:args/command_runner.dart';
+import 'package:collection/collection.dart';
+
+import 'package:dart2js_info/info.dart';
+import 'package:dart2js_info/src/io.dart';
+import 'package:dart2js_info/src/util.dart';
+
+import 'usage_exception.dart';
+
+class RuntimeCoverageAnalysisCommand extends Command<void>
+    with PrintUsageException {
+  @override
+  final String name = "runtime_coverage";
+  @override
+  final String description = "Analyze runtime coverage data";
+
+  RuntimeCoverageAnalysisCommand();
+
+  @override
+  void run() async {
+    var args = argResults.rest;
+    if (args.length < 2) {
+      usageException('Missing arguments, expected: info.data coverage.json');
+    }
+    await _runtimeCoverageAnalysis(args[0], args[1]);
+  }
+}
+
+Future<void> _runtimeCoverageAnalysis(infoFile, coverageFile) async {
+  var info = await infoFromFile(infoFile);
+  var coverageRaw = jsonDecode(File(coverageFile).readAsStringSync());
+  var coverage = <String, bool>{};
+  coverageRaw
+      .forEach((k, v) => coverage[k] = coverage[k] ?? false || v as bool);
+
+  int totalProgramSize = info.program.size;
+  int totalLibSize = info.libraries.fold(0, (n, lib) => n + lib.size);
+
+  int totalCode = 0;
+  int usedCode = 0;
+  var unused = PriorityQueue<Info>((a, b) => b.size.compareTo(a.size));
+
+  void tallyCode(Info i) {
+    totalCode += i.size;
+    var name = qualifiedName(i);
+    var used = coverage[name];
+
+    if (used != null) {
+      usedCode += i.size;
+    } else {
+      unused.add(i);
+    }
+  }
+
+  info.classes.forEach(tallyCode);
+  info.closures.forEach(tallyCode);
+
+  _section('Runtime Coverage Summary');
+  _showHeader('', 'bytes', '%');
+  _show('Program size', totalProgramSize, totalProgramSize);
+  _show('Libraries (excluding statics)', totalLibSize, totalProgramSize);
+  _show('Code (classes + closures)', totalCode, totalProgramSize);
+  _show('Used', usedCode, totalProgramSize);
+
+  print('');
+  _showHeader('', 'count', '%');
+  var total = info.classes.length + info.closures.length;
+  _show('Classes + closures', total, total);
+  _show('Used', total - unused.length, total);
+
+  print('');
+  var unusedTotal = totalCode - usedCode;
+  _section('Runtime Coverage Breakdown', size: unusedTotal);
+
+  // TODO(markzipan): support grouping results by package/library.
+  for (int i = 0; i < unused.length; i++) {
+    var item = unused.removeFirst();
+    var percent = (item.size * 100 / unusedTotal).toStringAsFixed(2);
+    print('${qualifiedName(item)}: ${item.size} bytes, $percent%');
+  }
+}
+
+void _section(String title, {int size}) {
+  if (size == null) {
+    print(title);
+  } else {
+    print('$title ($size bytes)');
+  }
+  print('=' * 72);
+}
+
+_showHeader(String msg, String header1, String header2) {
+  print(' ${pad(msg, 30, right: true)} ${pad(header1, 8)} ${pad(header2, 6)}');
+}
+
+_show(String msg, int size, int total) {
+  var percent = (size * 100 / total).toStringAsFixed(2);
+  print(' ${pad(msg, 30, right: true)} ${pad(size, 8)} ${pad(percent, 6)}%');
+}
diff --git a/pkg/dart2js_info/bin/tools.dart b/pkg/dart2js_info/bin/tools.dart
index 9e2cda8..7dc64bd 100644
--- a/pkg/dart2js_info/bin/tools.dart
+++ b/pkg/dart2js_info/bin/tools.dart
@@ -16,6 +16,7 @@
 import 'src/function_size_analysis.dart';
 import 'src/library_size_split.dart';
 import 'src/live_code_size_analysis.dart';
+import 'src/runtime_coverage_analysis.dart';
 import 'src/show_inferred_types.dart';
 import 'src/text_print.dart';
 
@@ -35,6 +36,7 @@
     ..addCommand(FunctionSizeCommand())
     ..addCommand(LibrarySizeCommand())
     ..addCommand(LiveCodeAnalysisCommand())
+    ..addCommand(RuntimeCoverageAnalysisCommand())
     ..addCommand(ShowInferredTypesCommand())
     ..addCommand(ShowCommand());
   commandRunner.run(args);
diff --git a/pkg/dart2js_info/lib/src/util.dart b/pkg/dart2js_info/lib/src/util.dart
index eebb1c7..9ee83cb 100644
--- a/pkg/dart2js_info/lib/src/util.dart
+++ b/pkg/dart2js_info/lib/src/util.dart
@@ -46,6 +46,26 @@
   return graph;
 }
 
+/// Provide a qualified name associated with [info]. Qualified names consist of
+/// the library's canonical URI concatenated with a library-unique kernel name.
+// See: https://github.com/dart-lang/sdk/blob/47eff41cdbfea4a178208dfc3137ba2b6bea0e36/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart#L978
+// TODO(sigmund): guarantee that the name is actually unique.
+String qualifiedName(Info f) {
+  assert(f is ClosureInfo || f is ClassInfo);
+  var element = f;
+  String name;
+  while (element != null) {
+    if (element is LibraryInfo) {
+      name = '${element.uri}:$name';
+      return name;
+    } else {
+      name = name ?? element.name;
+      element = element.parent;
+    }
+  }
+  return '';
+}
+
 /// Provide a unique long name associated with [info].
 // TODO(sigmund): guarantee that the name is actually unique.
 String longName(Info info, {bool useLibraryUri = false, bool forId = false}) {
diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc
index 9fb91ee..e411e07 100644
--- a/runtime/lib/string.cc
+++ b/runtime/lib/string.cc
@@ -343,8 +343,8 @@
   ASSERT(receiver.IsOneByteString());
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, index_obj, arguments->NativeArgAt(1));
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, code_point_obj, arguments->NativeArgAt(2));
-  ASSERT((0 <= code_point_obj.Value()) && (code_point_obj.Value() <= 0xFF));
-  OneByteString::SetCharAt(receiver, index_obj.Value(), code_point_obj.Value());
+  OneByteString::SetCharAt(receiver, index_obj.Value(),
+                           code_point_obj.Value() & 0xFF);
   return Object::null();
 }
 
@@ -353,8 +353,8 @@
   ASSERT(receiver.IsTwoByteString());
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, index_obj, arguments->NativeArgAt(1));
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, code_point_obj, arguments->NativeArgAt(2));
-  ASSERT((0 <= code_point_obj.Value()) && (code_point_obj.Value() <= 0xFFFF));
-  TwoByteString::SetCharAt(receiver, index_obj.Value(), code_point_obj.Value());
+  TwoByteString::SetCharAt(receiver, index_obj.Value(),
+                           code_point_obj.Value() & 0xFFFF);
   return Object::null();
 }
 
diff --git a/sdk/lib/_internal/vm/lib/convert_patch.dart b/sdk/lib/_internal/vm/lib/convert_patch.dart
index f0b84ad..2885579 100644
--- a/sdk/lib/_internal/vm/lib/convert_patch.dart
+++ b/sdk/lib/_internal/vm/lib/convert_patch.dart
@@ -1979,7 +1979,7 @@
         }
         byte = (byte << 6) | e;
       }
-      writeIntoOneByteString(result, j++, byte & 0xFF);
+      writeIntoOneByteString(result, j++, byte);
     }
     // Output size must match, unless we are doing single conversion and are
     // inside an unfinished sequence (which will trigger an error later).
diff --git a/sdk/lib/core/iterator.dart b/sdk/lib/core/iterator.dart
index 7ea322e..a33e273 100644
--- a/sdk/lib/core/iterator.dart
+++ b/sdk/lib/core/iterator.dart
@@ -49,6 +49,14 @@
   /// If that happens, the iterator may be in an inconsistent
   /// state, and any further behavior of the iterator is unspecified,
   /// including the effect of reading [current].
+  /// ```dart
+  /// final colors = ['blue', 'yellow', 'red'];
+  /// final colorsIterator = colors.iterator;
+  /// print(colorsIterator.moveNext()); // true
+  /// print(colorsIterator.moveNext()); // true
+  /// print(colorsIterator.moveNext()); // true
+  /// print(colorsIterator.moveNext()); // false
+  /// ```
   bool moveNext();
 
   /// The current element.
@@ -65,5 +73,18 @@
   /// [moveNext], even if an underlying collection changes.
   /// After a successful call to `moveNext`, the user doesn't need to cache
   /// the current value, but can keep reading it from the iterator.
+  /// ```dart
+  /// final colors = ['blue', 'yellow', 'red'];
+  /// var colorsIterator = colors.iterator;
+  /// while (colorsIterator.moveNext()) {
+  ///   print(colorsIterator.current);
+  /// }
+  /// ```
+  /// The output of the example is:
+  /// ```
+  /// blue
+  /// yellow
+  /// red
+  /// ```
   E get current;
 }
diff --git a/sdk/lib/core/regexp.dart b/sdk/lib/core/regexp.dart
index 993da0a5..c2020a6 100644
--- a/sdk/lib/core/regexp.dart
+++ b/sdk/lib/core/regexp.dart
@@ -19,15 +19,32 @@
 /// and returns the first [RegExpMatch].
 /// All other methods in [RegExp] can be build from that.
 ///
+/// The following example finds the first match of a regular expression in
+/// a string.
+/// ```dart
+/// RegExp exp = RegExp(r'(\w+)');
+/// String str = 'Parse my string';
+/// RegExpMatch? match = exp.firstMatch(str);
+/// print(match![0]); // "Parse"
+/// ```
 /// Use [allMatches] to look for all matches of a regular expression in
 /// a string.
 ///
 /// The following example finds all matches of a regular expression in
 /// a string.
 /// ```dart
-/// RegExp exp = RegExp(r"(\w+)");
-/// String str = "Parse my string";
+/// RegExp exp = RegExp(r'(\w+)');
+/// String str = 'Parse my string';
 /// Iterable<RegExpMatch> matches = exp.allMatches(str);
+/// for (final m in matches) {
+///   print(m[0]);
+/// }
+/// ```
+/// The output of the example is:
+/// ```
+/// Parse
+/// my
+/// string
 /// ```
 ///
 /// Note the use of a _raw string_ (a string prefixed with `r`)
@@ -54,8 +71,8 @@
   /// Example:
   ///
   /// ```dart
-  /// var wordPattern = RegExp(r"(\w+)");
-  /// var bracketedNumberValue = RegExp("$key: \\[\\d+\\]");
+  /// final wordPattern = RegExp(r'(\w+)');
+  /// final digitPattern = RegExp(r'(\d+)');
   /// ```
   ///
   /// Notice the use of a _raw string_ in the first example, and a regular
@@ -82,22 +99,55 @@
   /// larger regular expression. Since a [String] is itself a [Pattern]
   /// which matches itself, converting the string to a regular expression
   /// isn't needed in order to search for just that string.
+  /// ```dart
+  /// print(RegExp.escape('dash@example.com')); // dash@example\.com
+  /// print(RegExp.escape('a+b')); // a\+b
+  /// print(RegExp.escape('a*b')); // a\*b
+  /// print(RegExp.escape('{a-b}')); // \{a-b\}
+  /// print(RegExp.escape('a?')); // a\?
+  /// ```
   external static String escape(String text);
 
   /// Finds the first match of the regular expression in the string [input].
   ///
   /// Returns `null` if there is no match.
+  /// ```dart
+  /// final string = '[00:13.37] This is a chat message.';
+  /// final regExp = RegExp(r'c\w*');
+  /// final match = regExp.firstMatch(string)!;
+  /// print(match[0]); // chat
+  /// ```
   RegExpMatch? firstMatch(String input);
 
   Iterable<RegExpMatch> allMatches(String input, [int start = 0]);
 
   /// Whether the regular expression has a match in the string [input].
+  /// ```dart
+  /// var string = 'Dash is a bird';
+  /// var regExp = RegExp(r'(humming)?bird');
+  /// var match = regExp.hasMatch(string); // true
+  ///
+  /// regExp = RegExp(r'dog');
+  /// match = regExp.hasMatch(string); // false
+  /// ```
   bool hasMatch(String input);
 
   /// The substring of the first match of this regular expression in [input].
+  /// ```dart
+  /// var string = 'Dash is a bird';
+  /// var regExp = RegExp(r'(humming)?bird');
+  /// var match = regExp.stringMatch(string); // Match
+  ///
+  /// regExp = RegExp(r'dog');
+  /// match = regExp.stringMatch(string); // No match
+  /// ```
   String? stringMatch(String input);
 
   /// The source regular expression string used to create this `RegExp`.
+  /// ```dart
+  /// final regExp = RegExp(r'\p{L}');
+  /// print(regExp.pattern); // \p{L}
+  /// ```
   String get pattern;
 
   /// Whether this regular expression matches multiple lines.
@@ -112,6 +162,16 @@
   /// If the regular expression is not case sensitive, it will match an input
   /// letter with a pattern letter even if the two letters are different case
   /// versions of the same letter.
+  /// ```dart
+  /// final str = 'Parse my string';
+  /// var regExp = RegExp(r'STRING', caseSensitive: false);
+  /// final hasMatch = regExp.hasMatch(str); // Has matches.
+  /// print(regExp.isCaseSensitive); // false
+  ///
+  /// regExp = RegExp(r'STRING', caseSensitive: true);
+  /// final hasCaseSensitiveMatch = regExp.hasMatch(str); // No matches.
+  /// print(regExp.isCaseSensitive); // true
+  /// ```
   bool get isCaseSensitive;
 
   /// Whether this regular expression is in Unicode mode.
@@ -124,6 +184,19 @@
   /// In Unicode mode, the syntax of the RegExp pattern is more restricted, but
   /// some pattern features, like Unicode property escapes, are only available in
   /// this mode.
+  /// ```dart
+  /// var regExp = RegExp(r'^\p{L}$', unicode: true);
+  /// print(regExp.hasMatch('a')); // true
+  /// print(regExp.hasMatch('b')); // true
+  /// print(regExp.hasMatch('?')); // false
+  /// print(regExp.hasMatch(r'p{L}')); // false
+  ///
+  /// regExp = RegExp(r'^\p{L}$', unicode: false);
+  /// print(regExp.hasMatch('a')); // false
+  /// print(regExp.hasMatch('b')); // false
+  /// print(regExp.hasMatch('?')); // false
+  /// print(regExp.hasMatch(r'p{L}')); // true
+  /// ```
   @Since("2.4")
   bool get isUnicode;
 
@@ -145,6 +218,41 @@
 /// Regular expression matches are [Match]es, but also include the ability
 /// to retrieve the names for any named capture groups and to retrieve
 /// matches for named capture groups by name instead of their index.
+///
+/// Example:
+/// ```dart
+/// const pattern =
+///     r'^\[(?<Time>\s*((?<hour>\d+)):((?<minute>\d+))\.((?<second>\d+)))\]'
+///     r'\s(?<Message>\s*(.*)$)';
+///
+/// final regExp = RegExp(
+///   pattern,
+///   multiLine: true,
+/// );
+///
+/// const multilineText = '[00:13.37] This is a first message.\n'
+///     '[01:15.57] This is a second message.\n';
+///
+/// RegExpMatch regExpMatch = regExp.firstMatch(multilineText)!;
+/// print(regExpMatch.groupNames.join('-')); // hour-minute-second-Time-Message.
+/// final time = regExpMatch.namedGroup('Time'); // 00:13.37
+/// final hour = regExpMatch.namedGroup('hour'); // 00
+/// final minute = regExpMatch.namedGroup('minute'); // 13
+/// final second = regExpMatch.namedGroup('second'); // 37
+/// final message =
+///     regExpMatch.namedGroup('Message'); // This is a first message.
+/// final date = regExpMatch.namedGroup('Date'); // Undefined `Date`, throws.
+///
+/// Iterable<RegExpMatch> matches = regExp.allMatches(multilineText);
+/// for (final m in matches) {
+///   print(m.namedGroup('Time'));
+///   print(m.namedGroup('Message'));
+///   // 00:13.37
+///   // This is a first message.
+///   // 01:15.57
+///   // This is a second message.
+/// }
+/// ```
 @Since("2.3")
 abstract class RegExpMatch implements Match {
   /// The string matched by the group named [name].
diff --git a/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart b/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart
index 8e1f50d..5282193 100644
--- a/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart
+++ b/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart
@@ -7,6 +7,7 @@
 // VMOptions=--verify_after_gc
 // VMOptions=--verify_before_gc --verify_after_gc
 // VMOptions=--verify_store_buffer
+// VMOptions=--no_intrinsify
 
 import "package:expect/expect.dart";
 import 'dart:async';
diff --git a/tests/lib_2/convert/streamed_conversion_json_utf8_decode_test.dart b/tests/lib_2/convert/streamed_conversion_json_utf8_decode_test.dart
index 9faad73..19094bf 100644
--- a/tests/lib_2/convert/streamed_conversion_json_utf8_decode_test.dart
+++ b/tests/lib_2/convert/streamed_conversion_json_utf8_decode_test.dart
@@ -9,6 +9,7 @@
 // VMOptions=--verify_after_gc
 // VMOptions=--verify_before_gc --verify_after_gc
 // VMOptions=--verify_store_buffer
+// VMOptions=--no_intrinsify
 
 import "package:expect/expect.dart";
 import 'dart:async';
diff --git a/tools/VERSION b/tools/VERSION
index 9d1057b..663e7c4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 43
+PRERELEASE 44
 PRERELEASE_PATCH 0
\ No newline at end of file