Version 2.17.0-223.0.dev

Merge commit '42dedb589fd8fccb9e4a8773de2a3fc455c47dfe' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 2839d36..e86d9eb 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -421,7 +421,7 @@
       "name": "lints",
       "rootUri": "../third_party/pkg/lints",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.17"
     },
     {
       "name": "logging",
diff --git a/DEPS b/DEPS
index d942158..b9a31c1 100644
--- a/DEPS
+++ b/DEPS
@@ -125,7 +125,7 @@
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "7e00f893440a72de0637970325e4ea44bd1e8c8e",
   "linter_tag": "ed26087ae1b54fb8c532fcda618e6bf08827e315", # 1.20.0
-  "lints_tag": "f9670df2a66e0ec12eb51554e70c1cbf56c8f5d0",
+  "lints_tag": "8294e5648ab49474541527e2911e72e4c5aefe55", #2.0.0
   "logging_rev": "dfbe88b890c3b4f7bc06da5a7b3b43e9e263b688",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "7479783f0493f6717e1d7ae31cb37d39a91026b2",
diff --git a/pkg/analysis_server/lib/src/cider/document_symbols.dart b/pkg/analysis_server/lib/src/cider/document_symbols.dart
new file mode 100644
index 0000000..a1782db
--- /dev/null
+++ b/pkg/analysis_server/lib/src/cider/document_symbols.dart
@@ -0,0 +1,60 @@
+// 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.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/src/computer/computer_outline.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/micro/resolve_file.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+class CiderDocumentSymbolsComputer {
+  final FileResolver _fileResolver;
+
+  CiderDocumentSymbolsComputer(this._fileResolver);
+
+  List<DocumentSymbol> compute(String filePath) {
+    var result = <DocumentSymbol>[];
+    var resolvedUnit = _fileResolver.resolve(path: filePath);
+
+    final computer = DartUnitOutlineComputer(resolvedUnit);
+    final outline = computer.compute();
+
+    final children = outline.children;
+    if (children == null) {
+      return result;
+    }
+
+    result.addAll(children.map((child) => _asDocumentSymbol(
+        LspClientCapabilities.defaultSupportedSymbolKinds,
+        resolvedUnit.lineInfo,
+        child)));
+
+    return result;
+  }
+
+  DocumentSymbol _asDocumentSymbol(
+    Set<SymbolKind> supportedKinds,
+    LineInfo lineInfo,
+    Outline outline,
+  ) {
+    final codeRange = toRange(lineInfo, outline.codeOffset, outline.codeLength);
+    final nameLocation = outline.element.location;
+    final nameRange = nameLocation != null
+        ? toRange(lineInfo, nameLocation.offset, nameLocation.length)
+        : null;
+    return DocumentSymbol(
+      name: toElementName(outline.element),
+      detail: outline.element.parameters,
+      kind: elementKindToSymbolKind(supportedKinds, outline.element.kind),
+      deprecated: outline.element.isDeprecated,
+      range: codeRange,
+      selectionRange: nameRange ?? codeRange,
+      children: outline.children
+          ?.map((child) => _asDocumentSymbol(supportedKinds, lineInfo, child))
+          .toList(),
+    );
+  }
+}
diff --git a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
index 3cd73f3..c4664f7 100644
--- a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
@@ -457,6 +457,102 @@
       ]),
     );
   }
+
+  Future<void> test_snippets_createMethod_functionTypeNestedParameters() async {
+    const content = '''
+class A {
+  void a() => c^((cell) => cell.south);
+  void b() => c((cell) => cell.west);
+}
+''';
+
+    const expectedContent = r'''
+class A {
+  void a() => c((cell) => cell.south);
+  void b() => c((cell) => cell.west);
+
+  ${1:c}(${2:Function(dynamic cell)} ${3:param0}) {}
+}
+''';
+
+    newFile2(mainFilePath, withoutMarkers(content));
+    await initialize(
+      textDocumentCapabilities: withCodeActionKinds(
+          emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
+      workspaceCapabilities:
+          withDocumentChangesSupport(emptyWorkspaceClientCapabilities),
+      experimentalCapabilities: {
+        'snippetTextEdit': true,
+      },
+    );
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        position: positionFromMarker(content));
+    final fixAction = findEditAction(codeActions,
+        CodeActionKind('quickfix.create.method'), "Create method 'c'")!;
+
+    // Ensure the edit came back, and using documentChanges.
+    final edit = fixAction.edit!;
+    expect(edit.documentChanges, isNotNull);
+    expect(edit.changes, isNull);
+
+    // Ensure applying the changes will give us the expected content.
+    final contents = {
+      mainFilePath: withoutMarkers(content),
+    };
+    applyDocumentChanges(contents, edit.documentChanges!);
+    expect(contents[mainFilePath], equals(expectedContent));
+  }
+
+  Future<void>
+      test_snippets_extractVariable_functionTypeNestedParameters() async {
+    const content = '''
+main() {
+  useFunction(te^st);
+}
+
+useFunction(int g(a, b)) {}
+''';
+
+    const expectedContent = r'''
+main() {
+  ${1:int Function(dynamic a, dynamic b)} ${2:test};
+  useFunction(test);
+}
+
+useFunction(int g(a, b)) {}
+''';
+
+    newFile2(mainFilePath, withoutMarkers(content));
+    await initialize(
+      textDocumentCapabilities: withCodeActionKinds(
+          emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
+      workspaceCapabilities:
+          withDocumentChangesSupport(emptyWorkspaceClientCapabilities),
+      experimentalCapabilities: {
+        'snippetTextEdit': true,
+      },
+    );
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        position: positionFromMarker(content));
+    final fixAction = findEditAction(
+        codeActions,
+        CodeActionKind('quickfix.create.localVariable'),
+        "Create local variable 'test'")!;
+
+    // Ensure the edit came back, and using documentChanges.
+    final edit = fixAction.edit!;
+    expect(edit.documentChanges, isNotNull);
+    expect(edit.changes, isNull);
+
+    // Ensure applying the changes will give us the expected content.
+    final contents = {
+      mainFilePath: withoutMarkers(content),
+    };
+    applyDocumentChanges(contents, edit.documentChanges!);
+    expect(contents[mainFilePath], equals(expectedContent));
+  }
 }
 
 @reflectiveTest
diff --git a/pkg/analysis_server/test/src/cider/document_symbols_test.dart b/pkg/analysis_server/test/src/cider/document_symbols_test.dart
new file mode 100644
index 0000000..07a7536
--- /dev/null
+++ b/pkg/analysis_server/test/src/cider/document_symbols_test.dart
@@ -0,0 +1,299 @@
+// 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.
+
+import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/src/cider/document_symbols.dart';
+import 'package:test/expect.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../utilities/mock_packages.dart';
+import 'cider_service.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(CiderDocumentSymbolsComputerTest);
+  });
+}
+
+@reflectiveTest
+class CiderDocumentSymbolsComputerTest extends CiderServiceTest {
+  void test_class() {
+    var unitOutline = _compute('''
+abstract class A<K, V> {
+  int fa, fb;
+  String fc;
+  A(int i, String s);
+  A.name(num p);
+  A._privateName(num p);
+  static String ma(int pa) => null;
+  _mb(int pb);
+  R mc<R, P>(P p) {}
+  String get propA => null;
+  set propB(int v) {}
+}
+class B {
+  B(int p);
+}
+String fa(int pa) => null;
+R fb<R, P>(P p) {}
+''');
+
+    expect(unitOutline, hasLength(4));
+    // A
+    {
+      var outline_A = unitOutline[0];
+      _expect(outline_A,
+          kind: SymbolKind.Class,
+          name: 'A',
+          start: Position(line: 0, character: 0),
+          end: Position(line: 11, character: 1));
+      // A children
+      var outlines_A = outline_A.children!;
+      expect(outlines_A, hasLength(11));
+
+      _expect(outlines_A[0], kind: SymbolKind.Field, name: 'fa');
+      _expect(outlines_A[1], kind: SymbolKind.Field, name: 'fb');
+      _expect(outlines_A[2], kind: SymbolKind.Field, name: 'fc');
+
+      _expect(outlines_A[3],
+          kind: SymbolKind.Constructor,
+          name: 'A',
+          detail: '(int i, String s)',
+          start: Position(line: 3, character: 2),
+          end: Position(line: 3, character: 21));
+
+      _expect(outlines_A[4],
+          kind: SymbolKind.Constructor,
+          name: 'A.name',
+          start: Position(line: 4, character: 2),
+          end: Position(line: 4, character: 16),
+          detail: '(num p)');
+
+      _expect(outlines_A[5],
+          kind: SymbolKind.Constructor,
+          name: 'A._privateName',
+          start: Position(line: 5, character: 2),
+          end: Position(line: 5, character: 24),
+          detail: '(num p)');
+
+      _expect(outlines_A[6],
+          kind: SymbolKind.Method,
+          name: 'ma',
+          start: Position(line: 6, character: 2),
+          end: Position(line: 6, character: 35),
+          detail: '(int pa)');
+
+      _expect(outlines_A[7],
+          kind: SymbolKind.Method,
+          name: '_mb',
+          start: Position(line: 7, character: 2),
+          end: Position(line: 7, character: 14),
+          detail: '(int pb)');
+
+      _expect(outlines_A[8],
+          kind: SymbolKind.Method,
+          name: 'mc',
+          start: Position(line: 8, character: 2),
+          end: Position(line: 8, character: 20),
+          detail: '(P p)');
+
+      _expect(outlines_A[9],
+          kind: SymbolKind.Property,
+          name: 'propA',
+          start: Position(line: 9, character: 2),
+          end: Position(line: 9, character: 27));
+
+      _expect(outlines_A[10],
+          kind: SymbolKind.Property,
+          name: 'propB',
+          start: Position(line: 10, character: 2),
+          end: Position(line: 10, character: 21),
+          detail: '(int v)');
+      // // B
+      var outline_B = unitOutline[1];
+      _expect(outline_B,
+          kind: SymbolKind.Class,
+          name: 'B',
+          start: Position(line: 12, character: 0),
+          end: Position(line: 14, character: 1));
+
+      // B children
+      var outlines_B = outline_B.children!;
+      expect(outlines_B, hasLength(1));
+
+      _expect(outlines_B[0],
+          kind: SymbolKind.Constructor,
+          name: 'B',
+          start: Position(line: 13, character: 2),
+          end: Position(line: 13, character: 11),
+          detail: '(int p)');
+
+      _expect(unitOutline[2],
+          kind: SymbolKind.Function,
+          name: 'fa',
+          start: Position(line: 15, character: 0),
+          end: Position(line: 15, character: 26),
+          detail: '(int pa)');
+
+      _expect(unitOutline[3],
+          kind: SymbolKind.Function,
+          name: 'fb',
+          start: Position(line: 16, character: 0),
+          end: Position(line: 16, character: 18),
+          detail: '(P p)');
+    }
+  }
+
+  void test_isTest_isTestGroup() {
+    BazelMockPackages.instance.addMeta(resourceProvider);
+
+    var outline = _compute('''
+import 'package:meta/meta.dart';
+
+@isTestGroup
+void myGroup(name, body()) {}
+
+@isTest
+void myTest(name) {}
+
+void f() {
+  myGroup('group1', () {
+    myGroup('group1_1', () {
+      myTest('test1_1_1');
+      myTest('test1_1_2');
+    });
+    myGroup('group1_2', () {
+      myTest('test1_2_1');
+    });
+  });
+  myGroup('group2', () {
+    myTest('test2_1');
+    myTest('test2_2');
+  });
+}
+''');
+    // outline
+    expect(outline, hasLength(3));
+    // f
+    var f_outline = outline[2];
+    _expect(
+      f_outline,
+      kind: SymbolKind.Function,
+      name: 'f',
+      start: Position(line: 8, character: 0),
+      end: Position(line: 22, character: 1),
+      detail: '()',
+    );
+    var f_children = f_outline.children!;
+    expect(f_children, hasLength(2));
+    // group1
+    var group1_outline = f_children[0];
+    _expect(
+      group1_outline,
+      kind: SymbolKind.Method,
+      name: 'myGroup("group1")',
+      start: Position(line: 9, character: 2),
+      end: Position(line: 17, character: 4),
+    );
+    var group1_children = group1_outline.children!;
+    expect(group1_children, hasLength(2));
+    // group1_1
+    var group1_1_outline = group1_children[0];
+    _expect(group1_1_outline,
+        kind: SymbolKind.Method,
+        name: 'myGroup("group1_1")',
+        start: Position(line: 10, character: 4),
+        end: Position(line: 13, character: 6));
+    var group1_1_children = group1_1_outline.children!;
+    expect(group1_1_children, hasLength(2));
+    // test1_1_1
+    var test1_1_1_outline = group1_1_children[0];
+    _expect(test1_1_1_outline,
+        kind: SymbolKind.Method,
+        name: 'myTest("test1_1_1")',
+        start: Position(line: 11, character: 6),
+        end: Position(line: 11, character: 25));
+    // test1_1_1
+    var test1_1_2_outline = group1_1_children[1];
+    _expect(test1_1_2_outline,
+        kind: SymbolKind.Method,
+        name: 'myTest("test1_1_2")',
+        start: Position(line: 12, character: 6),
+        end: Position(line: 12, character: 25));
+    // group1_2
+    var group1_2_outline = group1_children[1];
+    _expect(
+      group1_2_outline,
+      kind: SymbolKind.Method,
+      name: 'myGroup("group1_2")',
+      start: Position(line: 14, character: 4),
+      end: Position(line: 16, character: 6),
+    );
+    var group1_2_children = group1_2_outline.children!;
+    expect(group1_2_children, hasLength(1));
+    // test2_1
+    var test1_2_1_outline = group1_2_children[0];
+    _expect(test1_2_1_outline,
+        kind: SymbolKind.Method,
+        name: 'myTest("test1_2_1")',
+        start: Position(line: 15, character: 6),
+        end: Position(line: 15, character: 25));
+    // group2
+    var group2_outline = f_children[1];
+    _expect(
+      group2_outline,
+      kind: SymbolKind.Method,
+      name: 'myGroup("group2")',
+      start: Position(line: 18, character: 2),
+      end: Position(line: 21, character: 4),
+    );
+    var group2_children = group2_outline.children!;
+    expect(group2_children, hasLength(2));
+    // test2_1
+    var test2_1_outline = group2_children[0];
+    _expect(test2_1_outline,
+        kind: SymbolKind.Method,
+        name: 'myTest("test2_1")',
+        start: Position(line: 19, character: 4),
+        end: Position(line: 19, character: 21));
+    // test2_2
+    var test2_2_outline = group2_children[1];
+    _expect(
+      test2_2_outline,
+      kind: SymbolKind.Method,
+      name: 'myTest("test2_2")',
+      start: Position(line: 20, character: 4),
+      end: Position(line: 20, character: 21),
+    );
+  }
+
+  List<DocumentSymbol> _compute(String content) {
+    newFile2(testPath, content);
+    return CiderDocumentSymbolsComputer(
+      fileResolver,
+    ).compute(convertPath(testPath));
+  }
+
+  void _expect(DocumentSymbol outline,
+      {SymbolKind? kind,
+      String? name,
+      Position? start,
+      Position? end,
+      String? detail}) {
+    if (kind != null) {
+      expect(outline.kind, kind);
+    }
+    if (name != null) {
+      expect(outline.name, name);
+    }
+    if (start != null) {
+      var range = outline.range;
+      expect(range.start, start);
+      expect(range.end, end);
+    }
+    if (detail != null) {
+      expect(outline.detail, detail);
+    }
+  }
+}
diff --git a/pkg/analysis_server/test/src/cider/test_all.dart b/pkg/analysis_server/test/src/cider/test_all.dart
index 2220710..3f7985f 100644
--- a/pkg/analysis_server/test/src/cider/test_all.dart
+++ b/pkg/analysis_server/test/src/cider/test_all.dart
@@ -6,6 +6,7 @@
 
 import 'assists_test.dart' as assists;
 import 'completion_test.dart' as completion;
+import 'document_symbols_test.dart' as symbols;
 import 'fixes_test.dart' as fixes;
 import 'rename_test.dart' as rename;
 import 'signature_help_test.dart' as signature;
@@ -14,6 +15,7 @@
   defineReflectiveSuite(() {
     assists.main();
     completion.main();
+    symbols.main();
     fixes.main();
     rename.main();
     signature.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
index 5f54e50..2add22c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
@@ -515,6 +515,26 @@
 ''');
   }
 
+  Future<void> test_functionType_argument() async {
+    await resolveTestCode('''
+class A {
+  a() => b((c) => c.d);
+}
+''');
+    await assertHasFix('''
+class A {
+  a() => b((c) => c.d);
+
+  b(Function(dynamic c) param0) {}
+}
+''');
+    var groups = change.linkedEditGroups;
+    var index = 0;
+    assertLinkedGroup(groups[index++], ['b((c', 'b(Function']);
+    assertLinkedGroup(groups[index++], ['Function(dynamic c)']);
+    assertLinkedGroup(groups[index++], ['param0']);
+  }
+
   Future<void> test_functionType_method_enclosingClass_instance() async {
     await resolveTestCode('''
 class C {
diff --git a/pkg/analysis_server/test/src/utilities/mock_packages.dart b/pkg/analysis_server/test/src/utilities/mock_packages.dart
index 3df14ee..884b687 100644
--- a/pkg/analysis_server/test/src/utilities/mock_packages.dart
+++ b/pkg/analysis_server/test/src/utilities/mock_packages.dart
@@ -53,6 +53,10 @@
     _addFiles(provider, 'flutter');
   }
 
+  void addMeta(MemoryResourceProvider provider) {
+    _addFiles(provider, 'meta');
+  }
+
   /// Add files of the given [packageName] to the [provider].
   Folder _addFiles(MemoryResourceProvider provider, String packageName) {
     var packagesPath = provider.convertPath('/workspace/third_party/dart');
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index be0b7fc..dca43e9 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -349,6 +349,12 @@
   /// The buffer in which the content of the edit is being composed.
   final StringBuffer _buffer = StringBuffer();
 
+  /// Whether the builder is currently writing an edit group.
+  ///
+  /// This flag is set internally when writing an edit group to prevent
+  /// nested/overlapping edit groups from being produced.
+  bool _isWritingEditGroup = false;
+
   /// Initialize a newly created builder to build a source edit.
   EditBuilderImpl(this.fileEditBuilder, this.offset, this.length) {
     _eol = fileEditBuilder.changeBuilder.eol;
@@ -363,9 +369,16 @@
       void Function(LinkedEditBuilder builder) buildLinkedEdit) {
     var builder = createLinkedEditBuilder();
     var start = offset + _buffer.length;
+    // If we're already writing an edit group we must not produce others nested
+    // inside, so just call the callback without capturing the group.
+    if (_isWritingEditGroup) {
+      return buildLinkedEdit(builder);
+    }
     try {
+      _isWritingEditGroup = true;
       buildLinkedEdit(builder);
     } finally {
+      _isWritingEditGroup = false;
       var end = offset + _buffer.length;
       var length = end - start;
       if (length != 0) {
diff --git a/pkg/dart2wasm/lib/class_info.dart b/pkg/dart2wasm/lib/class_info.dart
index e420c22..c94c023 100644
--- a/pkg/dart2wasm/lib/class_info.dart
+++ b/pkg/dart2wasm/lib/class_info.dart
@@ -315,6 +315,10 @@
   void collect() {
     // Create class info and Wasm structs for all classes.
     initializeTop();
+    // Subclasses of the [_Function] class are generated on the fly as fields
+    // with function types are encountered. Therefore, this class must be early
+    // in the initialization order.
+    initialize(translator.functionClass);
     for (Library library in translator.component.libraries) {
       for (Class cls in library.classes) {
         initialize(cls);
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 0a47313..4abdfe1 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1816,7 +1816,7 @@
   __ Bind(&loop);
   __ movzxb(RBX, Address(RSI, RDX, TIMES_1, 0));
   __ movb(FieldAddress(RAX, RDX, TIMES_1, target::OneByteString::data_offset()),
-          RBX);
+          ByteRegisterOf(RBX));
   __ incq(RDX);
   __ Bind(&check);
   __ cmpq(RDX, RCX);
@@ -1833,7 +1833,7 @@
   __ SmiUntag(RBX);
   __ SmiUntag(RCX);
   __ movb(FieldAddress(RAX, RBX, TIMES_1, target::OneByteString::data_offset()),
-          RCX);
+          ByteRegisterOf(RCX));
   __ ret();
 }
 
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index 82a37b8..befd618 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -311,6 +311,13 @@
   EmitOperand(reg & 7, address);
 }
 
+void Assembler::EmitB(int reg, const Address& address, int opcode) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOperandREX(reg & ~0x10, address, reg >= 8 ? REX_PREFIX : REX_NONE);
+  EmitUint8(opcode);
+  EmitOperand(reg & 7, address);
+}
+
 void Assembler::movl(Register dst, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   Operand operand(dst);
@@ -1507,7 +1514,8 @@
     testq(value, Immediate(kSmiTagMask));
     j(ZERO, &done, kNearJump);
   }
-  movb(TMP, FieldAddress(object, target::Object::tags_offset()));
+  movb(ByteRegisterOf(TMP),
+       FieldAddress(object, target::Object::tags_offset()));
   shrl(TMP, Immediate(target::UntaggedObject::kBarrierOverlapShift));
   andl(TMP, Address(THR, target::Thread::write_barrier_mask_offset()));
   testb(FieldAddress(value, target::Object::tags_offset()), TMP);
@@ -1573,7 +1581,8 @@
     testq(value, Immediate(kSmiTagMask));
     j(ZERO, &done, kNearJump);
   }
-  movb(TMP, FieldAddress(object, target::Object::tags_offset()));
+  movb(ByteRegisterOf(TMP),
+       FieldAddress(object, target::Object::tags_offset()));
   shrl(TMP, Immediate(target::UntaggedObject::kBarrierOverlapShift));
   andl(TMP, Address(THR, target::Thread::write_barrier_mask_offset()));
   testb(FieldAddress(value, target::Object::tags_offset()), TMP);
@@ -1755,7 +1764,7 @@
   switch (sz) {
     case kByte:
     case kUnsignedByte:
-      return movb(address, reg);
+      return movb(address, ByteRegisterOf(reg));
     case kTwoBytes:
     case kUnsignedTwoBytes:
       return movw(address, reg);
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 52af843..753ef20b 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -334,10 +334,18 @@
   void name(Register dst, const Address& src) {                                \
     Emit##width(dst, src, __VA_ARGS__);                                        \
   }
+#define RAB(name, ...)                                                         \
+  void name(ByteRegister dst, const Address& src) {                            \
+    EmitB(dst, src, __VA_ARGS__);                                              \
+  }
 #define AR(width, name, ...)                                                   \
   void name(const Address& dst, Register src) {                                \
     Emit##width(src, dst, __VA_ARGS__);                                        \
   }
+#define ARB(name, ...)                                                         \
+  void name(const Address& dst, ByteRegister src) {                            \
+    EmitB(src, dst, __VA_ARGS__);                                              \
+  }
 #define REGULAR_INSTRUCTION(name, ...)                                         \
   RA(W, name##w, __VA_ARGS__)                                                  \
   RA(L, name##l, __VA_ARGS__)                                                  \
@@ -355,11 +363,11 @@
 #undef REGULAR_INSTRUCTION
   RA(Q, movsxd, 0x63)
   RR(Q, movsxd, 0x63)
-  AR(L, movb, 0x88)
+  ARB(movb, 0x88)
   AR(L, movl, 0x89)
   AR(Q, movq, 0x89)
   AR(W, movw, 0x89)
-  RA(L, movb, 0x8A)
+  RAB(movb, 0x8A)
   RA(L, movl, 0x8B)
   RA(Q, movq, 0x8B)
   RR(L, movl, 0x8B)
@@ -1315,6 +1323,7 @@
              int opcode,
              int prefix2 = -1,
              int prefix1 = -1);
+  void EmitB(int reg, const Address& address, int opcode);
   void CmpPS(XmmRegister dst, XmmRegister src, int condition);
 
   inline void EmitUint8(uint8_t value);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index 1daf4c0..5ff3e51 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -936,16 +936,16 @@
       } else {
         switch (src_type.AsPrimitive().representation()) {
           case compiler::ffi::kInt8:  // Sign extend operand.
-            __ movsxb(dst_reg, ByteRegisterOf(src_reg));
+            __ ExtendValue(dst_reg, src_reg, compiler::kByte);
             return;
           case compiler::ffi::kInt16:
-            __ movsxw(dst_reg, src_reg);
+            __ ExtendValue(dst_reg, src_reg, compiler::kTwoBytes);
             return;
           case compiler::ffi::kUint8:  // Zero extend operand.
-            __ movzxb(dst_reg, ByteRegisterOf(src_reg));
+            __ ExtendValue(dst_reg, src_reg, compiler::kUnsignedByte);
             return;
           case compiler::ffi::kUint16:
-            __ movzxw(dst_reg, src_reg);
+            __ ExtendValue(dst_reg, src_reg, compiler::kUnsignedTwoBytes);
             return;
           default:
             // 32 to 64 bit is covered in IL by Representation conversions.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index bed125b..43ce96a 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -944,7 +944,7 @@
           __ movw(dst_addr, src_reg);
           return;
         case 1:
-          __ movb(dst_addr, src_reg);
+          __ movb(dst_addr, ByteRegisterOf(src_reg));
           return;
         default:
           UNREACHABLE();
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index d5ca6ba..831b5e9 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -2010,8 +2010,7 @@
         __ movb(element_address,
                 compiler::Immediate(static_cast<int8_t>(constant.Value())));
       } else {
-        ASSERT(locs()->in(2).reg() == RAX);
-        __ movb(element_address, RAX);
+        __ movb(element_address, ByteRegisterOf(locs()->in(2).reg()));
       }
       break;
     case kTypedDataUint8ClampedArrayCid:
@@ -2029,18 +2028,18 @@
         __ movb(element_address,
                 compiler::Immediate(static_cast<int8_t>(value)));
       } else {
-        ASSERT(locs()->in(2).reg() == RAX);
+        const Register storedValueReg = locs()->in(2).reg();
         compiler::Label store_value, store_0xff;
-        __ CompareImmediate(RAX, compiler::Immediate(0xFF));
+        __ CompareImmediate(storedValueReg, compiler::Immediate(0xFF));
         __ j(BELOW_EQUAL, &store_value, compiler::Assembler::kNearJump);
         // Clamp to 0x0 or 0xFF respectively.
         __ j(GREATER, &store_0xff);
-        __ xorq(RAX, RAX);
+        __ xorq(storedValueReg, storedValueReg);
         __ jmp(&store_value, compiler::Assembler::kNearJump);
         __ Bind(&store_0xff);
-        __ LoadImmediate(RAX, compiler::Immediate(0xFF));
+        __ LoadImmediate(storedValueReg, compiler::Immediate(0xFF));
         __ Bind(&store_value);
-        __ movb(element_address, RAX);
+        __ movb(element_address, ByteRegisterOf(storedValueReg));
       }
       break;
     }
diff --git a/runtime/vm/compiler/ffi/native_location.cc b/runtime/vm/compiler/ffi/native_location.cc
index 7b362fd..ef92266 100644
--- a/runtime/vm/compiler/ffi/native_location.cc
+++ b/runtime/vm/compiler/ffi/native_location.cc
@@ -228,7 +228,13 @@
   if (!other.IsFpuRegisters()) {
     return false;
   }
-  return other.AsFpuRegisters().fpu_reg_ == fpu_reg_;
+  auto& other_fpu_reg = other.AsFpuRegisters();
+  if (other_fpu_reg.fpu_reg_kind() != fpu_reg_kind()) {
+    return false;
+  }
+  // We can only compare `fpu_reg_` if the kind is the same.
+  // Q5 is not the same register as (nor overlaps) D5.
+  return other_fpu_reg.fpu_reg_ == fpu_reg_;
 }
 
 bool NativeStackLocation::Equals(const NativeLocation& other) const {
diff --git a/runtime/vm/compiler/ffi/native_location_test.cc b/runtime/vm/compiler/ffi/native_location_test.cc
index 8ba10d9..a9d4e1c 100644
--- a/runtime/vm/compiler/ffi/native_location_test.cc
+++ b/runtime/vm/compiler/ffi/native_location_test.cc
@@ -35,6 +35,51 @@
   EXPECT_EQ(4, half_1.offset_in_bytes());
 }
 
+// Regression test for NativeFpuRegistersLocation::Equals not considering kind
+// when comparing.
+UNIT_TEST_CASE_WITH_ZONE(NativeStackLocation_Equals) {
+  const auto& native_type = *new (Z) NativePrimitiveType(kInt8);
+
+  // Two FPU registers of the same kind and number are equal.
+  {
+    const auto& native_location1 = *new (Z) NativeFpuRegistersLocation(
+        native_type, native_type, kQuadFpuReg,
+        /*fpu_register=*/0);
+
+    const auto& native_location2 = *new (Z) NativeFpuRegistersLocation(
+        native_type, native_type, kQuadFpuReg,
+        /*fpu_register=*/0);
+
+    EXPECT(native_location1.Equals(native_location2));
+  }
+
+  // Two FPU registers with different numbers are NOT equal.
+  {
+    const auto& native_location1 = *new (Z) NativeFpuRegistersLocation(
+        native_type, native_type, kQuadFpuReg,
+        /*fpu_register=*/2);
+
+    const auto& native_location2 = *new (Z) NativeFpuRegistersLocation(
+        native_type, native_type, kQuadFpuReg,
+        /*fpu_register=*/4);
+
+    EXPECT(!native_location1.Equals(native_location2));
+  }
+
+  // Two FPU registers with different kinds are NOT equal.
+  {
+    const auto& native_location1 = *new (Z) NativeFpuRegistersLocation(
+        native_type, native_type, kQuadFpuReg,
+        /*fpu_register=*/3);
+
+    const auto& native_location2 = *new (Z) NativeFpuRegistersLocation(
+        native_type, native_type, kDoubleFpuReg,
+        /*fpu_register=*/3);
+
+    EXPECT(!native_location1.Equals(native_location2));
+  }
+}
+
 }  // namespace ffi
 }  // namespace compiler
 }  // namespace dart
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 0ef9ca6..fe33347 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6908,6 +6908,8 @@
   if (result.IsNull()) {
     // Canonicalize each type argument.
     AbstractType& type_arg = AbstractType::Handle(zone);
+    GrowableHandlePtrArray<const AbstractType> canonicalized_types(zone,
+                                                                   num_types);
     for (intptr_t i = 0; i < num_types; i++) {
       type_arg = TypeAt(i);
       type_arg = type_arg.Canonicalize(thread, trail);
@@ -6916,7 +6918,7 @@
         ASSERT(IsRecursive());
         return this->ptr();
       }
-      SetTypeAt(i, type_arg);
+      canonicalized_types.Add(type_arg);
     }
     // Canonicalization of a type argument of a recursive type argument vector
     // may change the hash of the vector, so invalidate.
@@ -6931,6 +6933,9 @@
     // canonical entry.
     result ^= table.GetOrNull(CanonicalTypeArgumentsKey(*this));
     if (result.IsNull()) {
+      for (intptr_t i = 0; i < num_types; i++) {
+        SetTypeAt(i, canonicalized_types.At(i));
+      }
       // Make sure we have an old space object and add it to the table.
       if (this->IsNew()) {
         result ^= Object::Clone(*this, Heap::kOld);
diff --git a/tests/language/field/function_field_test.dart b/tests/language/field/function_field_test.dart
new file mode 100644
index 0000000..1b40df7
--- /dev/null
+++ b/tests/language/field/function_field_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+class A {
+  int Function(int) f;
+
+  A(this.f);
+}
+
+main() {
+  A a = A((x) => x + x);
+  Expect.equals(a.f(2), 4);
+}
diff --git a/tests/language_2/field/function_field_test.dart b/tests/language_2/field/function_field_test.dart
new file mode 100644
index 0000000..1b40df7
--- /dev/null
+++ b/tests/language_2/field/function_field_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+class A {
+  int Function(int) f;
+
+  A(this.f);
+}
+
+main() {
+  A a = A((x) => x + x);
+  Expect.equals(a.f(2), 4);
+}
diff --git a/tools/VERSION b/tools/VERSION
index e732f20..e12d293 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 222
+PRERELEASE 223
 PRERELEASE_PATCH 0
\ No newline at end of file