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