Version 2.15.0-219.0.dev

Merge commit '31437a699b077ef626ec572d2c00def104d60921' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index ca074dc..d041f09 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-10-04T10:29:25.433041",
+  "generated": "2021-10-13T14:43:47.733371",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -470,7 +470,7 @@
       "name": "native_stack_traces",
       "rootUri": "../pkg/native_stack_traces",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.14"
     },
     {
       "name": "nnbd_migration",
diff --git a/DEPS b/DEPS
index 90ee138..ec82ec1 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
   # has.
-  "co19_rev": "3f7916727cf025bdedbe2b49625a8c403bead66b",
+  "co19_rev": "cd6510c8346444792b37f6f26feffcc234a44dd1",
   # This line prevents conflicts when both packages are rolled simultaneously.
   "co19_2_rev": "13344ad01472df9badfa78fd6aa411f042e13354",
 
@@ -163,7 +163,7 @@
   "test_reflective_loader_rev": "fcfce37666672edac849d2af6dffc0f8df236a94",
   "test_rev": "099dcc4d052a30c6921489cfbefa1c8531d12975",
   "typed_data_rev": "29ce5a92b03326d0b8035916ac04f528874994bd",
-  "usage_rev": "ac53249e5230a77624f46c07c2ed965efcfc0c59",
+  "usage_rev": "e0780cd8b2f8af69a28dc52678ffe8492da27d06",
   "vector_math_rev": "0c9f5d68c047813a6dcdeb88ba7a42daddf25025",
   "watcher_rev": "3924194385fb215cef483193ed2879a618a3d69c",
   "webdriver_rev": "ff5ccb1522edf4bed578ead4d65e0cbc1f2c4f02",
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index 7a0f2b5..12bf080 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,4 +1,6 @@
-# Changelog
+## 0.4.5-dev
+
+- Require Dart >= 2.14
 
 ## 0.4.4
 
diff --git a/pkg/native_stack_traces/analysis_options.yaml b/pkg/native_stack_traces/analysis_options.yaml
index 486705d..d0f68fe 100644
--- a/pkg/native_stack_traces/analysis_options.yaml
+++ b/pkg/native_stack_traces/analysis_options.yaml
@@ -1 +1,8 @@
-include: package:pedantic/analysis_options.1.9.0.yaml
+include: package:lints/recommended.yaml
+
+linter:
+  rules:
+    - avoid_dynamic_calls
+    - directives_ordering
+    - prefer_expression_function_bodies
+    - sort_pub_dependencies
diff --git a/pkg/native_stack_traces/bin/decode.dart b/pkg/native_stack_traces/bin/decode.dart
index 4ced1ca..a7ca956 100644
--- a/pkg/native_stack_traces/bin/decode.dart
+++ b/pkg/native_stack_traces/bin/decode.dart
@@ -2,13 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import "dart:async";
-import "dart:convert";
-import "dart:io" as io;
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io;
 
 import 'package:args/args.dart' show ArgParser, ArgResults;
-import 'package:path/path.dart' as path;
 import 'package:native_stack_traces/native_stack_traces.dart';
+import 'package:path/path.dart' as path;
 
 ArgParser _createBaseDebugParser(ArgParser parser) => parser
   ..addOption('debug',
@@ -134,7 +134,7 @@
 const int _badUsageExitCode = 1;
 
 void errorWithUsage(String message, {String? command}) {
-  print("Error: $message.\n");
+  print('Error: $message.\n');
   print(_usages[command]);
   io.exitCode = _badUsageExitCode;
 }
@@ -179,11 +179,11 @@
 
   void usageError(String message) => errorWithUsage(message, command: 'find');
   int? tryParseIntAddress(String s) {
-    if (!forceHexadecimal && !s.startsWith("0x")) {
+    if (!forceHexadecimal && !s.startsWith('0x')) {
       final decimal = int.tryParse(s);
       if (decimal != null) return decimal;
     }
-    return int.tryParse(s.startsWith("0x") ? s.substring(2) : s, radix: 16);
+    return int.tryParse(s.startsWith('0x') ? s.substring(2) : s, radix: 16);
   }
 
   PCOffset? convertAddress(StackTraceHeader header, String s) {
@@ -204,10 +204,10 @@
   }
 
   if ((options['vm_start'] == null) != (options['isolate_start'] == null)) {
-    return usageError("need both VM start and isolate start");
+    return usageError('need both VM start and isolate start');
   }
 
-  int vmStart = dwarf.vmStartAddress;
+  var vmStart = dwarf.vmStartAddress;
   if (options['vm_start'] != null) {
     final address = tryParseIntAddress(options['vm_start']);
     if (address == null) {
@@ -217,7 +217,7 @@
     vmStart = address;
   }
 
-  int isolateStart = dwarf.isolateStartAddress;
+  var isolateStart = dwarf.isolateStartAddress;
   if (options['isolate_start'] != null) {
     final address = tryParseIntAddress(options['isolate_start']);
     if (address == null) {
@@ -230,9 +230,12 @@
   final header = StackTraceHeader(isolateStart, vmStart);
 
   final locations = <PCOffset>[];
-  for (final String s in options['location'] + options.rest) {
+  for (final String s in [
+    ...(options['location'] as List<String>),
+    ...options.rest,
+  ]) {
     final location = convertAddress(header, s);
-    if (location == null) return usageError('could not parse PC address ${s}');
+    if (location == null) return usageError('could not parse PC address $s');
     locations.add(location);
   }
   if (locations.isEmpty) return usageError('no PC addresses to find');
@@ -241,14 +244,14 @@
     final addr = dwarf.virtualAddressOf(offset);
     final frames = dwarf
         .callInfoFor(addr, includeInternalFrames: verbose)
-        ?.map((CallInfo c) => "  " + c.toString());
+        ?.map((CallInfo c) => '  ' + c.toString());
     final addrString =
-        addr > 0 ? "0x" + addr.toRadixString(16) : addr.toString();
-    print("For virtual address ${addrString}:");
+        addr > 0 ? '0x' + addr.toRadixString(16) : addr.toString();
+    print('For virtual address $addrString:');
     if (frames == null) {
-      print("  Invalid virtual address.");
+      print('  Invalid virtual address.');
     } else if (frames.isEmpty) {
-      print("  Not a call from user or library code.");
+      print('  Not a call from user or library code.');
     } else {
       frames.forEach(print);
     }
@@ -280,7 +283,7 @@
       .transform(utf8.decoder)
       .transform(const LineSplitter())
       .transform(DwarfStackTraceDecoder(dwarf, includeInternalFrames: verbose))
-      .map((s) => s + "\n")
+      .map((s) => s + '\n')
       .transform(utf8.encoder);
 
   await output.addStream(convertedStream);
diff --git a/pkg/native_stack_traces/lib/elf.dart b/pkg/native_stack_traces/lib/elf.dart
index 3316655..31272b1 100644
--- a/pkg/native_stack_traces/lib/elf.dart
+++ b/pkg/native_stack_traces/lib/elf.dart
@@ -2,10 +2,10 @@
 // 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 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
 export 'src/constants.dart'
     show
         isolateDataSymbolName,
         isolateSymbolName,
         vmDataSymbolName,
         vmSymbolName;
+export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
diff --git a/pkg/native_stack_traces/lib/src/constants.dart b/pkg/native_stack_traces/lib/src/constants.dart
index ca987f5..4edfb41 100644
--- a/pkg/native_stack_traces/lib/src/constants.dart
+++ b/pkg/native_stack_traces/lib/src/constants.dart
@@ -3,20 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 
 // The section name in which the build ID is stored as a note.
-const String buildIdSectionName = ".note.gnu.build-id";
+const String buildIdSectionName = '.note.gnu.build-id';
 // The type of a build ID note.
 const int buildIdNoteType = 3;
 // The name of a build ID note.
-const String buildIdNoteName = "GNU";
+const String buildIdNoteName = 'GNU';
 
 // The dynamic symbol name for the VM instructions section.
-const String vmSymbolName = "_kDartVmSnapshotInstructions";
+const String vmSymbolName = '_kDartVmSnapshotInstructions';
 
 // The dynamic symbol name for the VM data section.
-const String vmDataSymbolName = "_kDartVmSnapshotData";
+const String vmDataSymbolName = '_kDartVmSnapshotData';
 
 // The dynamic symbol name for the isolate instructions section.
-const String isolateSymbolName = "_kDartIsolateSnapshotInstructions";
+const String isolateSymbolName = '_kDartIsolateSnapshotInstructions';
 
 // The dynamic symbol name for the isolate data section.
-const String isolateDataSymbolName = "_kDartIsolateSnapshotData";
+const String isolateDataSymbolName = '_kDartIsolateSnapshotData';
diff --git a/pkg/native_stack_traces/lib/src/convert.dart b/pkg/native_stack_traces/lib/src/convert.dart
index b6d96b4..d0d43a1 100644
--- a/pkg/native_stack_traces/lib/src/convert.dart
+++ b/pkg/native_stack_traces/lib/src/convert.dart
@@ -2,14 +2,14 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import "dart:async";
-import "dart:math";
+import 'dart:async';
+import 'dart:math';
 
 import 'constants.dart' as constants;
-import "dwarf.dart";
+import 'dwarf.dart';
 
 String _stackTracePiece(CallInfo call, int depth) =>
-    "#${depth.toString().padRight(6)} ${call}";
+    '#${depth.toString().padRight(6)} $call';
 
 // A pattern matching the last line of the non-symbolic stack trace header.
 //
@@ -45,7 +45,7 @@
   /// The [PCOffset] for the given absolute program counter address.
   PCOffset offsetOf(int address) {
     final isolateOffset = address - _isolateStart;
-    int vmOffset = address - _vmStart;
+    var vmOffset = address - _vmStart;
     if (vmOffset > 0 && vmOffset == min(vmOffset, isolateOffset)) {
       return PCOffset(vmOffset, InstructionsSection.vm);
     } else {
@@ -87,11 +87,11 @@
   final symbolString = match.namedGroup('symbol')!;
   final offsetString = match.namedGroup('offset')!;
   int? offset;
-  if (!forceHexadecimal && !offsetString.startsWith("0x")) {
+  if (!forceHexadecimal && !offsetString.startsWith('0x')) {
     offset = int.tryParse(offsetString);
   }
   if (offset == null) {
-    final digits = offsetString.startsWith("0x")
+    final digits = offsetString.startsWith('0x')
         ? offsetString.substring(2)
         : offsetString;
     offset = int.tryParse(digits, radix: 16);
@@ -182,8 +182,9 @@
   DwarfStackTraceDecoder(this._dwarf, {bool includeInternalFrames = false})
       : _includeInternalFrames = includeInternalFrames;
 
+  @override
   Stream<String> bind(Stream<String> stream) async* {
-    int depth = 0;
+    var depth = 0;
     StackTraceHeader? header;
     await for (final line in stream) {
       final parsedHeader = _parseInstructionsLine(line);
diff --git a/pkg/native_stack_traces/lib/src/dwarf.dart b/pkg/native_stack_traces/lib/src/dwarf.dart
index 71605a0..06ace82 100644
--- a/pkg/native_stack_traces/lib/src/dwarf.dart
+++ b/pkg/native_stack_traces/lib/src/dwarf.dart
@@ -13,9 +13,9 @@
 int _initialLengthValue(Reader reader) {
   final length = reader.readBytes(4);
   if (length == 0xffffffff) {
-    throw FormatException("64-bit DWARF format detected");
+    throw FormatException('64-bit DWARF format detected');
   } else if (length > 0xfffffff0) {
-    throw FormatException("Unrecognized reserved initial length value");
+    throw FormatException('Unrecognized reserved initial length value');
   }
   return length;
 }
@@ -33,9 +33,9 @@
 };
 
 const _tagStrings = <_Tag, String>{
-  _Tag.compileUnit: "DW_TAG_compile_unit",
-  _Tag.inlinedSubroutine: "DW_TAG_inlined_subroutine",
-  _Tag.subprogram: "DW_TAG_subroutine",
+  _Tag.compileUnit: 'DW_TAG_compile_unit',
+  _Tag.inlinedSubroutine: 'DW_TAG_inlined_subroutine',
+  _Tag.subprogram: 'DW_TAG_subroutine',
 };
 
 enum _AttributeName {
@@ -77,22 +77,22 @@
 };
 
 const _attributeNameStrings = <_AttributeName, String>{
-  _AttributeName.sibling: "DW_AT_sibling",
-  _AttributeName.name: "DW_AT_name",
-  _AttributeName.statementList: "DW_AT_stmt_list",
-  _AttributeName.lowProgramCounter: "DW_AT_low_pc",
-  _AttributeName.highProgramCounter: "DW_AT_high_pc",
-  _AttributeName.compilationDirectory: "DW_AT_comp_dir",
-  _AttributeName.inline: "DW_AT_inline",
-  _AttributeName.producer: "DW_AT_producer",
-  _AttributeName.abstractOrigin: "DW_AT_abstract_origin",
-  _AttributeName.artificial: "DW_AT_artificial",
-  _AttributeName.declarationColumn: "DW_AT_decl_column",
-  _AttributeName.declarationFile: "DW_AT_decl_file",
-  _AttributeName.declarationLine: "DW_AT_decl_line",
-  _AttributeName.callColumn: "DW_AT_call_column",
-  _AttributeName.callFile: "DW_AT_call_file",
-  _AttributeName.callLine: "DW_AT_call_line",
+  _AttributeName.sibling: 'DW_AT_sibling',
+  _AttributeName.name: 'DW_AT_name',
+  _AttributeName.statementList: 'DW_AT_stmt_list',
+  _AttributeName.lowProgramCounter: 'DW_AT_low_pc',
+  _AttributeName.highProgramCounter: 'DW_AT_high_pc',
+  _AttributeName.compilationDirectory: 'DW_AT_comp_dir',
+  _AttributeName.inline: 'DW_AT_inline',
+  _AttributeName.producer: 'DW_AT_producer',
+  _AttributeName.abstractOrigin: 'DW_AT_abstract_origin',
+  _AttributeName.artificial: 'DW_AT_artificial',
+  _AttributeName.declarationColumn: 'DW_AT_decl_column',
+  _AttributeName.declarationFile: 'DW_AT_decl_file',
+  _AttributeName.declarationLine: 'DW_AT_decl_line',
+  _AttributeName.callColumn: 'DW_AT_call_column',
+  _AttributeName.callFile: 'DW_AT_call_file',
+  _AttributeName.callLine: 'DW_AT_call_line',
 };
 
 enum _AttributeForm {
@@ -114,12 +114,12 @@
 };
 
 const _attributeFormStrings = <_AttributeForm, String>{
-  _AttributeForm.address: "DW_FORM_addr",
-  _AttributeForm.string: "DW_FORM_string",
-  _AttributeForm.flag: "DW_FORM_flag",
-  _AttributeForm.constant: "DW_FORM_udata",
-  _AttributeForm.reference4: "DW_FORM_ref4",
-  _AttributeForm.sectionOffset: "DW_FORM_sec_offset",
+  _AttributeForm.address: 'DW_FORM_addr',
+  _AttributeForm.string: 'DW_FORM_string',
+  _AttributeForm.flag: 'DW_FORM_flag',
+  _AttributeForm.constant: 'DW_FORM_udata',
+  _AttributeForm.reference4: 'DW_FORM_ref4',
+  _AttributeForm.sectionOffset: 'DW_FORM_sec_offset',
 };
 
 class _Attribute {
@@ -133,10 +133,10 @@
     final formInt = reader.readLEB128EncodedInteger();
     if (nameInt == 0 && formInt == 0) return null;
     if (!_attributeNames.containsKey(nameInt)) {
-      throw FormatException("Unexpected DW_AT value 0x${paddedHex(nameInt)}");
+      throw FormatException('Unexpected DW_AT value 0x${paddedHex(nameInt)}');
     }
     if (!_attributeForms.containsKey(formInt)) {
-      throw FormatException("Unexpected DW_FORM value 0x${paddedHex(formInt)}");
+      throw FormatException('Unexpected DW_FORM value 0x${paddedHex(formInt)}');
     }
     return _Attribute._(_attributeNames[nameInt]!, _attributeForms[formInt]!);
   }
@@ -173,8 +173,8 @@
       case _AttributeForm.reference4:
         final intValue = value as int;
         final unresolvedValue = paddedHex(intValue, 4);
-        final name = unit?.nameOfOrigin(intValue) ?? "<unresolved>";
-        return '0x${unresolvedValue} (origin: ${name})';
+        final name = unit?.nameOfOrigin(intValue) ?? '<unresolved>';
+        return '0x$unresolvedValue (origin: $name)';
     }
   }
 }
@@ -188,23 +188,23 @@
   _Abbreviation._(this.code, this.tag, this.children, this.attributes);
 
   // Constants from the DWARF specification.
-  static const _DW_CHILDREN_no = 0x00;
-  static const _DW_CHILDREN_yes = 0x01;
+  static const _dwChildrenNo = 0x00;
+  static const _dwChildrenYes = 0x01;
 
   static _Abbreviation? fromReader(Reader reader) {
     final code = reader.readLEB128EncodedInteger();
     if (code == 0) return null;
     final tagInt = reader.readLEB128EncodedInteger();
     if (!_tags.containsKey(tagInt)) {
-      throw FormatException("Unexpected DW_TAG value 0x${paddedHex(tagInt)}");
+      throw FormatException('Unexpected DW_TAG value 0x${paddedHex(tagInt)}');
     }
     final tag = _tags[tagInt]!;
     final childrenByte = reader.readByte();
-    if (childrenByte != _DW_CHILDREN_no && childrenByte != _DW_CHILDREN_yes) {
-      throw FormatException("Expected DW_CHILDREN_no or DW_CHILDREN_yes: "
-          "${childrenByte}");
+    if (childrenByte != _dwChildrenNo && childrenByte != _dwChildrenYes) {
+      throw FormatException('Expected DW_CHILDREN_no or DW_CHILDREN_yes: '
+          '$childrenByte');
     }
-    final children = childrenByte == _DW_CHILDREN_yes;
+    final children = childrenByte == _dwChildrenYes;
     final attributes = reader.readRepeated(_Attribute.fromReader).toList();
     return _Abbreviation._(code, tag, children, attributes);
   }
@@ -249,14 +249,16 @@
   }
 
   void writeToStringBuffer(StringBuffer buffer) {
-    buffer..writeln('Abbreviations table:')..writeln();
+    buffer
+      ..writeln('Abbreviations table:')
+      ..writeln();
     _abbreviations.forEach((key, abbreviation) {
       buffer
         ..write('  ')
         ..write(key)
         ..writeln(':');
       abbreviation.writeToStringBuffer(buffer);
-      buffer..writeln();
+      buffer.writeln();
     });
   }
 
@@ -283,7 +285,7 @@
     // DIEs with an abbreviation table index of 0 are list end markers.
     if (code == 0) return null;
     if (!header.abbreviations.containsKey(code)) {
-      throw FormatException("Unknown abbreviation code 0x${paddedHex(code)}");
+      throw FormatException('Unknown abbreviation code 0x${paddedHex(code)}');
     }
     final abbreviation = header.abbreviations[code]!;
     final attributes = <_Attribute, Object>{};
@@ -396,7 +398,7 @@
         ..writeln('):');
       final sortedChildren = children.entries.toList()
         ..sort((kv1, kv2) => Comparable.compare(kv1.key, kv2.key));
-      for (int i = 0; i < sortedChildren.length; i++) {
+      for (var i = 0; i < sortedChildren.length; i++) {
         final offset = sortedChildren[i].key;
         final child = sortedChildren[i].value;
         buffer
@@ -411,6 +413,7 @@
     }
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -435,13 +438,13 @@
     if (size == 0) return null;
     final version = reader.readBytes(2);
     if (version != 2) {
-      throw FormatException("Expected DWARF version 2, got $version");
+      throw FormatException('Expected DWARF version 2, got $version');
     }
     final abbreviationsOffset = reader.readBytes(4);
     final abbreviationsTable = abbreviationsTables[abbreviationsOffset];
     if (abbreviationsTable == null) {
-      throw FormatException("No abbreviation table found for offset "
-          "0x${paddedHex(abbreviationsOffset, 4)}");
+      throw FormatException('No abbreviation table found for offset '
+          '0x${paddedHex(abbreviationsOffset, 4)}');
     }
     final addressSize = reader.readByte();
     return CompilationUnitHeader._(
@@ -518,7 +521,7 @@
     final origin = referenceTable[offset];
     if (origin == null) {
       throw ArgumentError(
-          "${paddedHex(offset)} is not the offset of an abbreviated unit");
+          '${paddedHex(offset)} is not the offset of an abbreviated unit');
     }
     return origin[_AttributeName.name] as String;
   }
@@ -573,6 +576,7 @@
     }
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -591,7 +595,7 @@
   static FileEntry? fromReader(Reader reader) {
     final name = reader.readNullTerminatedString();
     // An empty null-terminated string marks the table end.
-    if (name == "") return null;
+    if (name == '') return null;
     final directoryIndex = reader.readLEB128EncodedInteger();
     final lastModified = reader.readLEB128EncodedInteger();
     final size = reader.readLEB128EncodedInteger();
@@ -599,10 +603,10 @@
   }
 
   @override
-  String toString() => "File name: $name\n"
-      "  Directory index: $directoryIndex\n"
-      "  Last modified: $lastModified\n"
-      "  Size: $size\n";
+  String toString() => 'File name: $name\n'
+      '  Directory index: $directoryIndex\n'
+      '  Last modified: $lastModified\n'
+      '  Size: $size\n';
 }
 
 class FileInfo {
@@ -613,7 +617,7 @@
   static FileInfo fromReader(Reader reader) {
     final offsetFiles = reader.readRepeated(FileEntry.fromReader).toList();
     final files = <int, FileEntry>{};
-    for (int i = 0; i < offsetFiles.length; i++) {
+    for (var i = 0; i < offsetFiles.length; i++) {
       // File entries are one-based, not zero-based.
       files[i + 1] = offsetFiles[i];
     }
@@ -625,15 +629,15 @@
 
   void writeToStringBuffer(StringBuffer buffer) {
     if (_files.isEmpty) {
-      buffer.writeln("No file information.");
+      buffer.writeln('No file information.');
       return;
     }
 
-    final indexHeader = "Entry";
-    final dirIndexHeader = "Dir";
-    final modifiedHeader = "Time";
-    final sizeHeader = "Size";
-    final nameHeader = "Name";
+    final indexHeader = 'Entry';
+    final dirIndexHeader = 'Dir';
+    final modifiedHeader = 'Time';
+    final sizeHeader = 'Size';
+    final nameHeader = 'Name';
 
     final indexStrings = _files
         .map((int i, FileEntry f) => MapEntry<int, String>(i, i.toString()));
@@ -653,27 +657,39 @@
     final maxSizeLength = sizeStrings.values
         .fold(sizeHeader.length, (int acc, String s) => max(acc, s.length));
 
-    buffer.writeln("File information:");
+    buffer.writeln('File information:');
 
-    buffer..write(" ")..write(indexHeader.padRight(maxIndexLength));
-    buffer..write(" ")..write(dirIndexHeader.padRight(maxDirIndexLength));
-    buffer..write(" ")..write(modifiedHeader.padRight(maxModifiedLength));
-    buffer..write(" ")..write(sizeHeader.padRight(maxSizeLength));
     buffer
-      ..write(" ")
+      ..write(' ')
+      ..write(indexHeader.padRight(maxIndexLength));
+    buffer
+      ..write(' ')
+      ..write(dirIndexHeader.padRight(maxDirIndexLength));
+    buffer
+      ..write(' ')
+      ..write(modifiedHeader.padRight(maxModifiedLength));
+    buffer
+      ..write(' ')
+      ..write(sizeHeader.padRight(maxSizeLength));
+    buffer
+      ..write(' ')
       ..writeln(nameHeader);
 
     for (final index in _files.keys) {
-      buffer..write(" ")..write(indexStrings[index]!.padRight(maxIndexLength));
       buffer
-        ..write(" ")
+        ..write(' ')
+        ..write(indexStrings[index]!.padRight(maxIndexLength));
+      buffer
+        ..write(' ')
         ..write(dirIndexStrings[index]!.padRight(maxDirIndexLength));
       buffer
-        ..write(" ")
+        ..write(' ')
         ..write(modifiedStrings[index]!.padRight(maxModifiedLength));
-      buffer..write(" ")..write(sizeStrings[index]!.padRight(maxSizeLength));
       buffer
-        ..write(" ")
+        ..write(' ')
+        ..write(sizeStrings[index]!.padRight(maxSizeLength));
+      buffer
+        ..write(' ')
         ..writeln(_files[index]!.name);
     }
   }
@@ -687,7 +703,7 @@
 }
 
 class LineNumberState {
-  final defaultIsStatement;
+  final bool defaultIsStatement;
 
   late int address;
   late int fileIndex;
@@ -723,11 +739,12 @@
     return clone;
   }
 
-  String toString() => "Current line number state machine registers:\n"
-      "  Address: ${paddedHex(address)}\n"
-      "  File index: $fileIndex\n"
-      "  Line number: $line\n"
-      "  Column number: $column\n"
+  @override
+  String toString() => 'Current line number state machine registers:\n'
+      '  Address: ${paddedHex(address)}\n'
+      '  File index: $fileIndex\n'
+      '  Line number: $line\n'
+      '  Column number: $column\n'
       "  Is ${isStatement ? "" : "not "}a statement.\n"
       "  Is ${basicBlock ? "" : "not "}at the beginning of a basic block.\n"
       "  Is ${endSequence ? "" : "not "}just after the end of a sequence.";
@@ -772,7 +789,7 @@
     final isStmtByte = reader.readByte();
     if (isStmtByte < 0 || isStmtByte > 1) {
       throw FormatException(
-          "Unexpected value for default_is_stmt: ${isStmtByte}");
+          'Unexpected value for default_is_stmt: $isStmtByte');
     }
     final defaultIsStatement = isStmtByte == 1;
     final lineBase = reader.readByte(signed: true);
@@ -780,24 +797,24 @@
     final opcodeBase = reader.readByte();
     final standardOpcodeLengths = <int, int>{};
     // Standard opcode numbering starts at 1.
-    for (int i = 1; i < opcodeBase; i++) {
+    for (var i = 1; i < opcodeBase; i++) {
       standardOpcodeLengths[i] = reader.readLEB128EncodedInteger();
     }
     final includeDirectories = <String>[];
     while (!reader.done) {
       final directory = reader.readNullTerminatedString();
-      if (directory == "") break;
+      if (directory == '') break;
       includeDirectories.add(directory);
     }
     if (reader.done) {
-      throw FormatException("Unterminated directory entry");
+      throw FormatException('Unterminated directory entry');
     }
     final filesInfo = FileInfo.fromReader(reader);
 
     // Header length doesn't include the 2-byte version or 4-byte length fields.
     if (reader.offset != headerStart + headerLength) {
-      throw FormatException("At offset ${reader.offset} after header, "
-          "expected to be at offset ${headerStart + headerLength}");
+      throw FormatException('At offset ${reader.offset} after header, '
+          'expected to be at offset ${headerStart + headerLength}');
     }
 
     return LineNumberProgramHeader._(
@@ -833,7 +850,7 @@
       ..write('  Opcode base: ')
       ..writeln(opcodeBase)
       ..writeln('Standard opcode lengths:');
-    for (int i = 1; i < opcodeBase; i++) {
+    for (var i = 1; i < opcodeBase; i++) {
       buffer
         ..write('    Opcode ')
         ..write(i)
@@ -876,7 +893,7 @@
     if (header == null) return null;
     final calculatedMatrix = _readOpcodes(reader, header).toList();
     if (calculatedMatrix.isEmpty) {
-      throw FormatException("No line number information generated by program");
+      throw FormatException('No line number information generated by program');
     }
     return LineNumberProgram._(header, calculatedMatrix);
   }
@@ -903,7 +920,7 @@
           final subOpcode = reader.readByte();
           switch (subOpcode) {
             case 0:
-              throw FormatException("Attempted to execute extended opcode 0");
+              throw FormatException('Attempted to execute extended opcode 0');
             case 1: // DW_LNE_end_sequence
               state.endSequence = true;
               yield state.clone();
@@ -918,10 +935,10 @@
               break;
             case 3: // DW_LNE_define_file
               throw FormatException(
-                  "DW_LNE_define_file instruction not handled");
+                  'DW_LNE_define_file instruction not handled');
             default:
               throw FormatException(
-                  "Extended opcode ${subOpcode} not in DWARF 2");
+                  'Extended opcode $subOpcode not in DWARF 2');
           }
           break;
         case 1: // DW_LNS_copy
@@ -954,7 +971,7 @@
           state.address += reader.readBytes(2);
           break;
         default:
-          throw FormatException("Standard opcode ${opcode} not in DWARF 2");
+          throw FormatException('Standard opcode $opcode not in DWARF 2');
       }
     }
   }
@@ -1003,12 +1020,13 @@
   void writeToStringBuffer(StringBuffer buffer) {
     header.writeToStringBuffer(buffer);
 
-    buffer.writeln("Results of line number program:");
+    buffer.writeln('Results of line number program:');
     for (final state in calculatedMatrix) {
-      buffer..writeln(state);
+      buffer.writeln(state);
     }
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -1040,6 +1058,7 @@
     });
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -1047,20 +1066,6 @@
   }
 }
 
-// TODO(11617): Replace calls to these functions with a general hashing solution
-// once available.
-int _hashCombine(int hash, int value) {
-  hash = 0x1fffffff & (hash + value);
-  hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
-  return hash ^ (hash >> 6);
-}
-
-int _hashFinish(int hash) {
-  hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
-  hash = hash ^ (hash >> 11);
-  return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
-}
-
 /// Represents the information for a call site.
 abstract class CallInfo {
   /// Whether this call site is considered internal (i.e. not located in either
@@ -1089,36 +1094,38 @@
   bool get isInternal => internal;
 
   @override
-  int get hashCode {
-    int hash = 0;
-    hash = _hashCombine(hash, inlined.hashCode);
-    hash = _hashCombine(hash, internal.hashCode);
-    hash = _hashCombine(hash, function.hashCode);
-    hash = _hashCombine(hash, filename.hashCode);
-    hash = _hashCombine(hash, line.hashCode);
-    hash = _hashCombine(hash, column.hashCode);
-    return _hashFinish(hash);
-  }
+  int get hashCode => Object.hash(
+        inlined,
+        internal,
+        function,
+        filename,
+        line,
+        column,
+      );
 
   @override
-  bool operator ==(Object other) {
-    if (other is DartCallInfo) {
-      return inlined == other.inlined &&
-          internal == other.internal &&
-          function == other.function &&
-          filename == other.filename &&
-          line == other.line &&
-          column == other.column;
-    }
-    return false;
-  }
+  bool operator ==(Object other) =>
+      other is DartCallInfo &&
+      inlined == other.inlined &&
+      internal == other.internal &&
+      function == other.function &&
+      filename == other.filename &&
+      line == other.line &&
+      column == other.column;
 
   void writeToStringBuffer(StringBuffer buffer) {
-    buffer..write(function)..write(' (')..write(filename);
+    buffer
+      ..write(function)
+      ..write(' (')
+      ..write(filename);
     if (line > 0) {
-      buffer..write(':')..write(line);
+      buffer
+        ..write(':')
+        ..write(line);
       if (column > 0) {
-        buffer..write(':')..write(column);
+        buffer
+          ..write(':')
+          ..write(column);
       }
     }
     buffer.write(')');
@@ -1140,19 +1147,14 @@
   StubCallInfo({required this.name, required this.offset});
 
   @override
-  int get hashCode => _hashFinish(
-      _hashCombine(_hashCombine(0, name.hashCode), offset.hashCode));
+  int get hashCode => Object.hash(name, offset);
 
   @override
-  bool operator ==(Object other) {
-    if (other is StubCallInfo) {
-      return name == other.name && offset == other.offset;
-    }
-    return false;
-  }
+  bool operator ==(Object other) =>
+      other is StubCallInfo && name == other.name && offset == other.offset;
 
   @override
-  String toString() => "${name}+0x${offset.toRadixString(16)}";
+  String toString() => '$name+0x${offset.toRadixString(16)}';
 }
 
 /// The instructions section in which a program counter address is located.
@@ -1181,14 +1183,11 @@
           includeInternalFrames: includeInternalFrames);
 
   @override
-  int get hashCode => _hashFinish(_hashCombine(offset.hashCode, section.index));
+  int get hashCode => Object.hash(offset, section);
 
   @override
-  bool operator ==(Object other) {
-    return other is PCOffset &&
-        offset == other.offset &&
-        section == other.section;
-  }
+  bool operator ==(Object other) =>
+      other is PCOffset && offset == other.offset && section == other.section;
 
   @override
   String toString() => 'PCOffset($section, $offset)';
@@ -1235,23 +1234,23 @@
       Dwarf.fromReader(Reader.fromFile(path));
 
   static Dwarf _loadSectionsFromElf(Reader reader, Elf elf) {
-    final abbrevSection = elf.namedSections(".debug_abbrev").single;
+    final abbrevSection = elf.namedSections('.debug_abbrev').single;
     final abbrevReader = abbrevSection.refocusedCopy(reader);
     final abbreviationsTables = Map.fromEntries(
         abbrevReader.readRepeatedWithOffsets(_AbbreviationsTable.fromReader));
 
-    final lineNumberSection = elf.namedSections(".debug_line").single;
+    final lineNumberSection = elf.namedSections('.debug_line').single;
     final lineNumberInfo =
         LineNumberInfo.fromReader(lineNumberSection.refocusedCopy(reader));
 
-    final infoSection = elf.namedSections(".debug_info").single;
+    final infoSection = elf.namedSections('.debug_info').single;
     final debugInfo = DebugInfo.fromReader(
         infoSection.refocusedCopy(reader), abbreviationsTables);
 
     final vmStartSymbol = elf.dynamicSymbolFor(constants.vmSymbolName);
     if (vmStartSymbol == null) {
       throw FormatException(
-          "Expected a dynamic symbol with name ${constants.vmSymbolName}");
+          'Expected a dynamic symbol with name ${constants.vmSymbolName}');
     }
     final vmStartAddress = vmStartSymbol.value;
 
@@ -1259,7 +1258,7 @@
         elf.dynamicSymbolFor(constants.isolateSymbolName);
     if (isolateStartSymbol == null) {
       throw FormatException(
-          "Expected a dynamic symbol with name ${constants.isolateSymbolName}");
+          'Expected a dynamic symbol with name ${constants.isolateSymbolName}');
     }
     final isolateStartAddress = isolateStartSymbol.value;
 
@@ -1316,7 +1315,7 @@
       case InstructionsSection.isolate:
         return pcOffset.offset + isolateStartAddress;
       default:
-        throw "Unexpected value for instructions section";
+        throw 'Unexpected value for instructions section';
     }
   }
 
@@ -1327,7 +1326,10 @@
       ..writeln('----------------------------------------')
       ..writeln();
     _abbreviationsTables.forEach((offset, table) {
-      buffer..write('(Offset ')..write(paddedHex(offset, 4))..write(') ');
+      buffer
+        ..write('(Offset ')
+        ..write(paddedHex(offset, 4))
+        ..write(') ');
       table.writeToStringBuffer(buffer);
     });
     buffer
diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart
index 268d5d80..ac7cb50 100644
--- a/pkg/native_stack_traces/lib/src/elf.dart
+++ b/pkg/native_stack_traces/lib/src/elf.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// ignore_for_file: constant_identifier_names
+
 import 'dart:typed_data';
 
 import 'reader.dart';
@@ -16,41 +18,33 @@
 }
 
 // Reads an Elf{32,64}_Addr.
-int _readElfAddress(Reader reader) {
-  return _readElfBytes(reader, reader.wordSize, reader.wordSize);
-}
+int _readElfAddress(Reader reader) =>
+    _readElfBytes(reader, reader.wordSize, reader.wordSize);
 
 // Reads an Elf{32,64}_Off.
-int _readElfOffset(Reader reader) {
-  return _readElfBytes(reader, reader.wordSize, reader.wordSize);
-}
+int _readElfOffset(Reader reader) =>
+    _readElfBytes(reader, reader.wordSize, reader.wordSize);
 
 // Reads an Elf{32,64}_Half.
-int _readElfHalf(Reader reader) {
-  return _readElfBytes(reader, 2, 2);
-}
+int _readElfHalf(Reader reader) => _readElfBytes(reader, 2, 2);
 
 // Reads an Elf{32,64}_Word.
-int _readElfWord(Reader reader) {
-  return _readElfBytes(reader, 4, 4);
-}
+int _readElfWord(Reader reader) => _readElfBytes(reader, 4, 4);
 
 // Reads an Elf64_Xword.
 int _readElfXword(Reader reader) {
   switch (reader.wordSize) {
     case 4:
-      throw "Internal reader error: reading Elf64_Xword in 32-bit ELF file";
+      throw 'Internal reader error: reading Elf64_Xword in 32-bit ELF file';
     case 8:
       return _readElfBytes(reader, 8, 8);
     default:
-      throw "Unsupported word size ${reader.wordSize}";
+      throw 'Unsupported word size ${reader.wordSize}';
   }
 }
 
 // Reads an Elf{32,64}_Section.
-int _readElfSection(Reader reader) {
-  return _readElfBytes(reader, 2, 2);
-}
+int _readElfSection(Reader reader) => _readElfBytes(reader, 2, 2);
 
 // Used in cases where the value read for a given field is Elf32_Word on 32-bit
 // and Elf64_Xword on 64-bit.
@@ -61,7 +55,7 @@
     case 8:
       return _readElfXword(reader);
     default:
-      throw "Unsupported word size ${reader.wordSize}";
+      throw 'Unsupported word size ${reader.wordSize}';
   }
 }
 
@@ -113,14 +107,14 @@
         wordSize = 8;
         break;
       default:
-        throw FormatException("Unexpected e_ident[EI_CLASS] value");
+        throw FormatException('Unexpected e_ident[EI_CLASS] value');
     }
     final calculatedHeaderSize = 0x18 + 3 * wordSize + 0x10;
 
     if (fileSize < calculatedHeaderSize) {
-      throw FormatException("ELF file too small for header: "
-          "file size ${fileSize} < "
-          "calculated header size $calculatedHeaderSize");
+      throw FormatException('ELF file too small for header: '
+          'file size $fileSize < '
+          'calculated header size $calculatedHeaderSize');
     }
 
     Endian endian;
@@ -132,11 +126,11 @@
         endian = Endian.big;
         break;
       default:
-        throw FormatException("Unexpected e_indent[EI_DATA] value");
+        throw FormatException('Unexpected e_indent[EI_DATA] value');
     }
 
     if (reader.readByte() != 0x01) {
-      throw FormatException("Unexpected e_ident[EI_VERSION] value");
+      throw FormatException('Unexpected e_ident[EI_VERSION] value');
     }
 
     // After this point, we need the reader to be correctly set up re: word
@@ -147,7 +141,7 @@
     // Skip rest of e_ident/e_type/e_machine, i.e. move to e_version.
     reader.seek(0x14, absolute: true);
     if (_readElfWord(reader) != 0x01) {
-      throw FormatException("Unexpected e_version value");
+      throw FormatException('Unexpected e_version value');
     }
 
     final entry = _readElfAddress(reader);
@@ -167,25 +161,25 @@
     final sectionHeaderStringsIndex = _readElfHalf(reader);
 
     if (reader.offset != headerSize) {
-      throw FormatException("Only read ${reader.offset} bytes, not the "
-          "full header size ${headerSize}");
+      throw FormatException('Only read ${reader.offset} bytes, not the '
+          'full header size $headerSize');
     }
 
     if (headerSize != calculatedHeaderSize) {
-      throw FormatException("Stored ELF header size ${headerSize} != "
-          "calculated ELF header size $calculatedHeaderSize");
+      throw FormatException('Stored ELF header size $headerSize != '
+          'calculated ELF header size $calculatedHeaderSize');
     }
     if (fileSize < programHeaderOffset) {
-      throw FormatException("File is truncated before program header");
+      throw FormatException('File is truncated before program header');
     }
     if (fileSize < programHeaderOffset + programHeaderSize) {
-      throw FormatException("File is truncated within the program header");
+      throw FormatException('File is truncated within the program header');
     }
     if (fileSize < sectionHeaderOffset) {
-      throw FormatException("File is truncated before section header");
+      throw FormatException('File is truncated before section header');
     }
     if (fileSize < sectionHeaderOffset + sectionHeaderSize) {
-      throw FormatException("File is truncated within the section header");
+      throw FormatException('File is truncated within the section header');
     }
 
     return ElfHeader._(
@@ -207,7 +201,7 @@
   int get sectionHeaderSize => sectionHeaderCount * sectionHeaderEntrySize;
 
   // Constants used within the ELF specification.
-  static const _ELFMAG = "\x7fELF";
+  static const _ELFMAG = '\x7fELF';
   static const _ELFCLASS32 = 0x01;
   static const _ELFCLASS64 = 0x02;
   static const _ELFDATA2LSB = 0x01;
@@ -220,10 +214,10 @@
       ..write(' bits');
     switch (endian) {
       case Endian.little:
-        buffer..writeln(' and little-endian');
+        buffer.writeln(' and little-endian');
         break;
       case Endian.big:
-        buffer..writeln(' and big-endian');
+        buffer.writeln(' and big-endian');
         break;
     }
     buffer
@@ -278,7 +272,7 @@
       this.paddr, this.filesz, this.memsz, this.align, this.wordSize);
 
   static ProgramHeaderEntry fromReader(Reader reader) {
-    int wordSize = reader.wordSize;
+    var wordSize = reader.wordSize;
     assert(wordSize == 4 || wordSize == 8);
     final type = _readElfWord(reader);
     late int flags;
@@ -299,14 +293,14 @@
   }
 
   static const _typeStrings = <int, String>{
-    _PT_NULL: "PT_NULL",
-    _PT_LOAD: "PT_LOAD",
-    _PT_DYNAMIC: "PT_DYNAMIC",
-    _PT_PHDR: "PT_PHDR",
+    _PT_NULL: 'PT_NULL',
+    _PT_LOAD: 'PT_LOAD',
+    _PT_DYNAMIC: 'PT_DYNAMIC',
+    _PT_PHDR: 'PT_PHDR',
   };
 
   static String _typeToString(int type) =>
-      _typeStrings[type] ?? "unknown (${paddedHex(type, 4)})";
+      _typeStrings[type] ?? 'unknown (${paddedHex(type, 4)})';
 
   void writeToStringBuffer(StringBuffer buffer) {
     buffer
@@ -328,6 +322,7 @@
       ..write(paddedHex(align, wordSize));
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -364,10 +359,11 @@
 
   void writeToStringBuffer(StringBuffer buffer) {
     for (var i = 0; i < length; i++) {
-      if (i != 0)
+      if (i != 0) {
         buffer
           ..writeln()
           ..writeln();
+      }
       buffer
         ..write('Entry ')
         ..write(i)
@@ -376,6 +372,7 @@
     }
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -453,19 +450,19 @@
   }
 
   static const _typeStrings = <int, String>{
-    _SHT_NULL: "SHT_NULL",
-    _SHT_PROGBITS: "SHT_PROGBITS",
-    _SHT_SYMTAB: "SHT_SYMTAB",
-    _SHT_STRTAB: "SHT_STRTAB",
-    _SHT_HASH: "SHT_HASH",
-    _SHT_DYNAMIC: "SHT_DYNAMIC",
-    _SHT_NOTE: "SHT_NOTE",
-    _SHT_NOBITS: "SHT_NOBITS",
-    _SHT_DYNSYM: "SHT_DYNSYM",
+    _SHT_NULL: 'SHT_NULL',
+    _SHT_PROGBITS: 'SHT_PROGBITS',
+    _SHT_SYMTAB: 'SHT_SYMTAB',
+    _SHT_STRTAB: 'SHT_STRTAB',
+    _SHT_HASH: 'SHT_HASH',
+    _SHT_DYNAMIC: 'SHT_DYNAMIC',
+    _SHT_NOTE: 'SHT_NOTE',
+    _SHT_NOBITS: 'SHT_NOBITS',
+    _SHT_DYNSYM: 'SHT_DYNSYM',
   };
 
   static String _typeToString(int type) =>
-      _typeStrings[type] ?? "unknown (${paddedHex(type, 4)})";
+      _typeStrings[type] ?? 'unknown (${paddedHex(type, 4)})';
 
   void writeToStringBuffer(StringBuffer buffer) {
     buffer.write('Name: ');
@@ -496,6 +493,7 @@
       ..write(entrySize);
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     writeToStringBuffer(buffer);
@@ -521,10 +519,11 @@
 
   void writeToStringBuffer(StringBuffer buffer) {
     for (var i = 0; i < entries.length; i++) {
-      if (i != 0)
+      if (i != 0) {
         buffer
           ..writeln()
           ..writeln();
+      }
       buffer
         ..write('Entry ')
         ..write(i)
@@ -620,6 +619,7 @@
     return Note._(entry, type, name, description);
   }
 
+  @override
   void writeToStringBuffer(StringBuffer buffer) {
     buffer
       ..write('Section "')
@@ -669,9 +669,9 @@
       ..writeln('" is a string table:');
     for (var key in _entries.keys) {
       buffer
-        ..write("  ")
+        ..write('  ')
         ..write(key)
-        ..write(" => ")
+        ..write(' => ')
         ..writeln(_entries[key]);
     }
   }
@@ -734,7 +734,7 @@
   void _cacheNameFromStringTable(StringTable table) {
     final nameFromTable = table[nameIndex];
     if (nameFromTable == null) {
-      throw FormatException("Index $nameIndex not found in string table");
+      throw FormatException('Index $nameIndex not found in string table');
     }
     name = nameFromTable;
   }
@@ -750,31 +750,31 @@
       ..write('" =>');
     switch (bind) {
       case SymbolBinding.STB_GLOBAL:
-        buffer..write(' a global');
+        buffer.write(' a global');
         break;
       case SymbolBinding.STB_LOCAL:
-        buffer..write(' a local');
+        buffer.write(' a local');
         break;
     }
     switch (visibility) {
       case SymbolVisibility.STV_DEFAULT:
         break;
       case SymbolVisibility.STV_HIDDEN:
-        buffer..write(' hidden');
+        buffer.write(' hidden');
         break;
       case SymbolVisibility.STV_INTERNAL:
-        buffer..write(' internal');
+        buffer.write(' internal');
         break;
       case SymbolVisibility.STV_PROTECTED:
-        buffer..write(' protected');
+        buffer.write(' protected');
         break;
     }
     buffer
-      ..write(" symbol that points to ")
+      ..write(' symbol that points to ')
       ..write(size)
-      ..write(" bytes at location 0x")
+      ..write(' bytes at location 0x')
       ..write(paddedHex(value, _wordSize))
-      ..write(" in section ")
+      ..write(' in section ')
       ..write(sectionIndex);
   }
 
@@ -821,7 +821,7 @@
       ..write(headerEntry.name)
       ..writeln('" is a symbol table:');
     for (var symbol in _entries) {
-      buffer.write(" ");
+      buffer.write(' ');
       symbol.writeToStringBuffer(buffer);
       buffer.writeln();
     }
@@ -851,7 +851,7 @@
   // We don't use DynamicTableTag for the key so that we can handle ELF files
   // that may use unknown (to us) tags.
   final Map<int, int> _entries;
-  final _wordSize;
+  final int _wordSize;
 
   DynamicTable._(SectionHeaderEntry entry, this._entries, this._wordSize)
       : super._(entry);
@@ -922,7 +922,7 @@
         }
       } else {
         buffer
-          ..write("Unknown tag ")
+          ..write('Unknown tag ')
           ..write(kv.key)
           ..write(' => ')
           ..writeln(kv.value);
@@ -975,7 +975,7 @@
   ///
   /// Returns -1 if there is no dynamic symbol that matches [name].
   Symbol? dynamicSymbolFor(String name) {
-    for (final section in namedSections(".dynsym")) {
+    for (final section in namedSections('.dynsym')) {
       final dynsym = section as SymbolTable;
       if (dynsym.containsKey(name)) return dynsym[name];
     }
@@ -1026,7 +1026,7 @@
     // header entries.
     if (header.sectionHeaderStringsIndex < 0 ||
         header.sectionHeaderStringsIndex >= sectionHeader.entries.length) {
-      throw FormatException("Section header string table index invalid");
+      throw FormatException('Section header string table index invalid');
     }
     final sectionHeaderStringTableEntry =
         sectionHeader.entries[header.sectionHeaderStringsIndex];
@@ -1034,13 +1034,13 @@
         sections[sectionHeaderStringTableEntry] as StringTable?;
     if (sectionHeaderStringTable == null) {
       throw FormatException(
-          "No section for entry ${sectionHeaderStringTableEntry}");
+          'No section for entry $sectionHeaderStringTableEntry');
     }
     final sectionsByName = <String, Set<Section>>{};
     for (final entry in sectionHeader.entries) {
       final section = sections[entry];
       if (section == null) {
-        throw FormatException("No section found for entry ${entry}");
+        throw FormatException('No section found for entry $entry');
       }
       entry.setName(sectionHeaderStringTable);
       sectionsByName.putIfAbsent(entry.name, () => {}).add(section);
@@ -1062,7 +1062,7 @@
         final stringTable = stringTableMap[entry];
         if (stringTable == null) {
           throw FormatException(
-              "String table not found at section header entry ${link}");
+              'String table not found at section header entry $link');
         }
         symbolTable._cacheNames(stringTable);
       }
@@ -1115,7 +1115,7 @@
 
   @override
   String toString() {
-    StringBuffer buffer = StringBuffer();
+    var buffer = StringBuffer();
     writeToStringBuffer(buffer);
     return buffer.toString();
   }
diff --git a/pkg/native_stack_traces/lib/src/reader.dart b/pkg/native_stack_traces/lib/src/reader.dart
index 9eb5be6..8158878 100644
--- a/pkg/native_stack_traces/lib/src/reader.dart
+++ b/pkg/native_stack_traces/lib/src/reader.dart
@@ -3,12 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:io';
-import 'dart:typed_data';
 import 'dart:math';
+import 'dart:typed_data';
 
-String paddedHex(int value, [int bytes = 0]) {
-  return value.toRadixString(16).padLeft(2 * bytes, '0');
-}
+String paddedHex(int value, [int bytes = 0]) =>
+    value.toRadixString(16).padLeft(2 * bytes, '0');
 
 class Reader {
   final ByteData bdata;
@@ -20,9 +19,9 @@
   int _offset = 0;
 
   Endian get endian => _endian as Endian;
-  void set endian(Endian value) => _endian = value;
+  set endian(Endian value) => _endian = value;
   int get wordSize => _wordSize as int;
-  void set wordSize(int value) => _wordSize = value;
+  set wordSize(int value) => _wordSize = value;
 
   /// Unless provided, [wordSize] and [endian] are initialized to values that
   /// ensure no reads are made that depend on their value (e.g., readBytes).
@@ -58,8 +57,8 @@
 
   int readBytes(int size, {bool signed = false}) {
     if (_offset + size > length) {
-      throw ArgumentError("attempt to read ${size} bytes with only "
-          "${length - _offset} bytes remaining in the reader");
+      throw ArgumentError('attempt to read $size bytes with only '
+          '${length - _offset} bytes remaining in the reader');
     }
     final start = _offset;
     _offset += size;
@@ -80,7 +79,7 @@
             : bdata.getUint64(start, endian);
       default:
         _offset -= size;
-        throw ArgumentError("invalid request to read $size bytes");
+        throw ArgumentError('invalid request to read $size bytes');
     }
   }
 
@@ -88,7 +87,7 @@
   int readWord() => readBytes(wordSize);
   String readNullTerminatedString() {
     final start = bdata.offsetInBytes + _offset;
-    for (int i = 0; _offset + i < bdata.lengthInBytes; i++) {
+    for (var i = 0; _offset + i < bdata.lengthInBytes; i++) {
       if (bdata.getUint8(_offset + i) == 0) {
         _offset += i + 1;
         return String.fromCharCodes(bdata.buffer.asUint8List(start, i));
@@ -152,47 +151,51 @@
       startOffset = max(startOffset, lowerWindow);
       endOffset = min(endOffset, upperWindow);
     }
-    for (int i = startOffset; i < endOffset; i += bytesPerLine) {
-      buffer..write("0x")..write(paddedHex(i, 8))..write(" ");
-      for (int j = 0; j < bytesPerLine && i + j < endOffset; j++) {
+    for (var i = startOffset; i < endOffset; i += bytesPerLine) {
+      buffer
+        ..write('0x')
+        ..write(paddedHex(i, 8))
+        ..write(' ');
+      for (var j = 0; j < bytesPerLine && i + j < endOffset; j++) {
         var byte = baseData.getUint8(i + j);
         buffer
-          ..write(i + j == currentOffset ? "|" : " ")
+          ..write(i + j == currentOffset ? '|' : ' ')
           ..write(paddedHex(byte, 1));
       }
       buffer.writeln();
     }
   }
 
+  @override
   String toString() {
     final buffer = StringBuffer();
     buffer
-      ..write("Word size: ")
+      ..write('Word size: ')
       ..write(_wordSize)
       ..writeln();
     buffer
-      ..write("Endianness: ")
+      ..write('Endianness: ')
       ..write(_endian)
       ..writeln();
     buffer
-      ..write("Start:  0x")
+      ..write('Start:  0x')
       ..write(paddedHex(start, _wordSize ?? 0))
-      ..write(" (")
+      ..write(' (')
       ..write(start)
-      ..writeln(")");
+      ..writeln(')');
     buffer
-      ..write("Offset: 0x")
+      ..write('Offset: 0x')
       ..write(paddedHex(offset, _wordSize ?? 0))
-      ..write(" (")
+      ..write(' (')
       ..write(offset)
-      ..writeln(")");
+      ..writeln(')');
     buffer
-      ..write("Length: 0x")
+      ..write('Length: 0x')
       ..write(paddedHex(length, _wordSize ?? 0))
-      ..write(" (")
+      ..write(' (')
       ..write(length)
-      ..writeln(")");
-    buffer..writeln("Bytes around current position:");
+      ..writeln(')');
+    buffer.writeln('Bytes around current position:');
     writeCurrentReaderPosition(buffer, maxSize: 256);
     return buffer.toString();
   }
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml
index 7dbc3b2..1110d5c 100644
--- a/pkg/native_stack_traces/pubspec.yaml
+++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,11 +1,11 @@
 name: native_stack_traces
 description: Utilities for working with non-symbolic stack traces.
-version: 0.4.4
+version: 0.4.5-dev
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
 
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.14.0 <3.0.0'
 
 executables:
   decode:
@@ -15,4 +15,4 @@
   path: ^1.8.0
 
 dev_dependencies:
-  pedantic: ^1.10.0
+  lints: ^1.0.0
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 7c9e1b7..94cd7c1 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -23,8 +23,6 @@
 Language/Libraries_and_Scripts/Scripts/top_level_main_t06: SkipByDesign # Uses dart:io.
 Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # Non-JS-interop external members are not supported
 Language/Libraries_and_Scripts/top_level_syntax_t01: SkipByDesign # Non-JS-interop external members are not supported
-Language/Metadata/before*: SkipByDesign # dart:mirrors not supported https://github.com/dart-lang/co19/issues/523.
-Language/Metadata/syntax_t10: SkipByDesign # dart:mirrors is not supported
 Language/Reference/Operator_Precedence/precedence_15_unary_prefix_t08: SkipByDesign # binary '~' produces different results in JavaScript and Dart
 LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t01: SkipByDesign # Non-JS-interop external members are not supported
 LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t02: SkipByDesign # Non-JS-interop external members are not supported
diff --git a/tests/co19/co19-dartdevc.status b/tests/co19/co19-dartdevc.status
index 683bc51..7e761f2 100644
--- a/tests/co19/co19-dartdevc.status
+++ b/tests/co19/co19-dartdevc.status
@@ -21,8 +21,6 @@
 Language/Libraries_and_Scripts/Scripts/top_level_main_t06: SkipByDesign # Uses dart:io.
 Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # External variables are not supported
 Language/Libraries_and_Scripts/top_level_syntax_t01: SkipByDesign # External variables are not supported
-Language/Metadata/before*: SkipByDesign # dart:mirrors not supported https://github.com/dart-lang/co19/issues/523
-Language/Metadata/syntax_t10: SkipByDesign # dart:mirrors is not supported
 Language/Reference/Operator_Precedence/precedence_15_unary_prefix_t08: SkipByDesign # binary '~' produces different results in JavaScript and Dart
 LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t01: SkipByDesign # External variables are not supported
 LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t02: SkipByDesign # External variables are not supported
diff --git a/tests/co19/co19-kernel.status b/tests/co19/co19-kernel.status
index 92bf7b5..09b27c9 100644
--- a/tests/co19/co19-kernel.status
+++ b/tests/co19/co19-kernel.status
@@ -3,7 +3,6 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $runtime == dart_precompiled ]
-Language/Metadata/syntax_t10: SkipByDesign # dart:mirrors is not supported
 LibTest/io/RawDatagramSocket/join_A01_t01: Skip # https://github.com/dart-lang/co19/issues/195
 LibTest/io/RawDatagramSocket/join_A01_t02: Skip # https://github.com/dart-lang/co19/issues/195
 LibTest/io/RawDatagramSocket/join_A02_t01: Skip # https://github.com/dart-lang/co19/issues/195
diff --git a/tools/VERSION b/tools/VERSION
index e2d1273..a3831ae 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 218
+PRERELEASE 219
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index af05909..8c911b1 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -3329,6 +3329,7 @@
           "script": "out/ReleaseX64/dart-sdk/bin/dart",
           "arguments": [
             "analyze",
+            "--fatal-infos",
             "pkg/native_stack_traces"
           ]
         },