Version 2.16.0-18.0.dev
Merge commit 'b74e8e5cec7cbff32945aa93a511475c4ddd9e11' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index 2a5834e..49b1b09 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -180,7 +180,10 @@
// To handle this, if both unformatted/formatted contain at least one
// newline, split this change into two around the last newline so that the
// final part (likely leading whitespace) can be included without
- // including the whole change.
+ // including the whole change. This cannot be done if the newline is at
+ // the end of the source whitespace though, as this would create a split
+ // where the first part is the same and the second part is empty,
+ // resulting in an infinite loop/stack overflow.
//
// Without this, functionality like VS Code's "format modified lines"
// (which uses Git status to know which lines are edited) may appear to
@@ -188,7 +191,8 @@
if (unformattedStart < rangeStart.result &&
unformattedEnd > rangeStart.result &&
unformattedWhitespace.contains('\n') &&
- formattedWhitespace.contains('\n')) {
+ formattedWhitespace.contains('\n') &&
+ !unformattedWhitespace.endsWith('\n')) {
// Find the offsets of the character after the last newlines.
final unformattedOffset = unformattedWhitespace.lastIndexOf('\n') + 1;
final formattedOffset = formattedWhitespace.lastIndexOf('\n') + 1;
diff --git a/pkg/analysis_server/test/lsp/format_test.dart b/pkg/analysis_server/test/lsp/format_test.dart
index 61bac2e..7ffff8a 100644
--- a/pkg/analysis_server/test/lsp/format_test.dart
+++ b/pkg/analysis_server/test/lsp/format_test.dart
@@ -295,6 +295,26 @@
await expectRangeFormattedContents(mainFileUri, contents, expected);
}
+ Future<void> test_formatRange_trailingNewline_47702() async {
+ // Check we complete when a formatted block ends with a newline.
+ // https://github.com/dart-lang/sdk/issues/47702
+ const contents = '''
+int a;
+[[
+ int b;
+]]
+''';
+ final expected = '''
+int a;
+
+int b;
+
+''';
+ await initialize();
+ await openFile(mainFileUri, withoutMarkers(contents));
+ await expectRangeFormattedContents(mainFileUri, contents, expected);
+ }
+
Future<void> test_invalidSyntax() async {
const contents = '''void f(((( {
print('test');
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index ba209b7..9983408 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -2200,6 +2200,7 @@
/// [BlockFunctionBody]
/// | [EmptyFunctionBody]
/// | [ExpressionFunctionBody]
+/// | [NativeFunctionBody]
///
/// Clients may not extend, implement or mix-in this class.
abstract class FunctionBody implements AstNode {
diff --git a/pkg/analyzer/lib/src/lint/pub.dart b/pkg/analyzer/lib/src/lint/pub.dart
index be237ff..8c11a57 100644
--- a/pkg/analyzer/lib/src/lint/pub.dart
+++ b/pkg/analyzer/lib/src/lint/pub.dart
@@ -207,7 +207,9 @@
PSDependencyList? get devDependencies;
PSEntry? get documentation;
PSEntry? get homepage;
+ PSEntry? get issueTracker;
PSEntry? get name;
+ PSEntry? get repository;
PSEntry? get version;
void accept(PubspecVisitor visitor);
}
@@ -224,7 +226,9 @@
T? visitPackageDevDependency(PSDependency dependency) => null;
T? visitPackageDocumentation(PSEntry documentation) => null;
T? visitPackageHomepage(PSEntry homepage) => null;
+ T? visitPackageIssueTracker(PSEntry issueTracker) => null;
T? visitPackageName(PSEntry name) => null;
+ T? visitPackageRepository(PSEntry repostory) => null;
T? visitPackageVersion(PSEntry version) => null;
}
@@ -410,8 +414,12 @@
@override
PSEntry? homepage;
@override
+ PSEntry? issueTracker;
+ @override
PSEntry? name;
@override
+ PSEntry? repository;
+ @override
PSEntry? version;
@override
PSDependencyList? dependencies;
@@ -445,6 +453,12 @@
if (homepage != null) {
visitor.visitPackageHomepage(homepage!);
}
+ if (issueTracker != null) {
+ visitor.visitPackageIssueTracker(issueTracker!);
+ }
+ if (repository != null) {
+ visitor.visitPackageRepository(repository!);
+ }
if (name != null) {
visitor.visitPackageName(name!);
}
@@ -474,6 +488,8 @@
sb.writelin(authors);
sb.writelin(description);
sb.writelin(homepage);
+ sb.writelin(repository);
+ sb.writelin(issueTracker);
sb.writelin(dependencies);
sb.writelin(devDependencies);
sb.writelin(dependencyOverrides);
@@ -502,6 +518,12 @@
case 'homepage':
homepage = _processScalar(key, v, resourceProvider);
break;
+ case 'repository':
+ repository = _processScalar(key, v, resourceProvider);
+ break;
+ case 'issue_tracker':
+ issueTracker = _processScalar(key, v, resourceProvider);
+ break;
case 'name':
name = _processScalar(key, v, resourceProvider);
break;
diff --git a/pkg/analyzer/test/src/lint/pub_test.dart b/pkg/analyzer/test/src/lint/pub_test.dart
index 6634c41..db3a2d0 100644
--- a/pkg/analyzer/test/src/lint/pub_test.dart
+++ b/pkg/analyzer/test/src/lint/pub_test.dart
@@ -50,6 +50,8 @@
unittest: '>=0.11.0 <0.12.0'
dependency_overrides:
foo: 1.2.0
+repository: https://github.com/dart-lang/linter
+issue_tracker: https://github.com/dart-lang/linter/issues
""";
Pubspec ps = Pubspec.parse(src);
@@ -72,6 +74,10 @@
});
testValue('homepage', ps.homepage,
equals('https://github.com/dart-lang/linter'));
+ testValue('repository', ps.repository,
+ equals('https://github.com/dart-lang/linter'));
+ testValue('issue_tracker', ps.issueTracker,
+ equals('https://github.com/dart-lang/linter/issues'));
testValue(
'description', ps.description, equals('Style linter for Dart.'));
testValue('version', ps.version, equals('0.0.1'));
diff --git a/pkg/front_end/test/analyser_ignored/load_dill_twice_test.dart b/pkg/front_end/test/analyser_ignored/load_dill_twice_test.dart
index 803f98f..2b03e4d 100644
--- a/pkg/front_end/test/analyser_ignored/load_dill_twice_test.dart
+++ b/pkg/front_end/test/analyser_ignored/load_dill_twice_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'dart:io' show Platform, exit;
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
diff --git a/pkg/front_end/test/covariance_check/covariance_check_test.dart b/pkg/front_end/test/covariance_check/covariance_check_test.dart
index 4f59ddc..c91c012 100644
--- a/pkg/front_end/test/covariance_check/covariance_check_test.dart
+++ b/pkg/front_end/test/covariance_check/covariance_check_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'dart:io' show Directory, Platform;
import 'package:_fe_analyzer_shared/src/testing/id.dart';
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
@@ -33,7 +31,7 @@
InternalCompilerResult compilerResult,
Library library,
Map<Id, ActualData<String>> actualMap,
- {bool verbose}) {
+ {bool? verbose}) {
new CovarianceCheckDataExtractor(compilerResult, actualMap)
.computeForLibrary(library);
}
@@ -44,7 +42,7 @@
InternalCompilerResult compilerResult,
Member member,
Map<Id, ActualData<String>> actualMap,
- {bool verbose}) {
+ {bool? verbose}) {
member.accept(new CovarianceCheckDataExtractor(compilerResult, actualMap));
}
@@ -58,7 +56,7 @@
: super(compilerResult, actualMap);
@override
- String computeNodeValue(Id id, TreeNode node) {
+ String? computeNodeValue(Id id, TreeNode node) {
if (node is AsExpression && node.isCovarianceCheck) {
return typeToText(node.type);
}
diff --git a/pkg/front_end/test/enable_non_nullable/enable_non_nullable_test.dart b/pkg/front_end/test/enable_non_nullable/enable_non_nullable_test.dart
index f5fb221..a5f766e 100644
--- a/pkg/front_end/test/enable_non_nullable/enable_non_nullable_test.dart
+++ b/pkg/front_end/test/enable_non_nullable/enable_non_nullable_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
import 'dart:io';
import 'package:expect/expect.dart';
@@ -59,10 +57,10 @@
}
Future<void> test(
- {bool enableNonNullableByDefault,
- bool enableNonNullableExplicitly,
- Version versionImpliesOptIn,
- Version versionOptsInAllowed}) async {
+ {required bool enableNonNullableByDefault,
+ required bool enableNonNullableExplicitly,
+ required Version versionImpliesOptIn,
+ required Version versionOptsInAllowed}) async {
CompilerOptions options = new CompilerOptions();
if (enableNonNullableByDefault) {
// Pretend non-nullable is on by default.
@@ -112,12 +110,12 @@
Directory directory = new Directory.fromUri(
Uri.base.resolve('pkg/front_end/test/enable_non_nullable/data/'));
- CompilerResult result = await kernelForProgramInternal(
+ CompilerResult result = (await kernelForProgramInternal(
directory.uri.resolve('main.dart'), options,
- retainDataForTesting: true);
+ retainDataForTesting: true))!;
Expect.isFalse(
hadDiagnostic, "Compilation had diagnostics (errors, warnings)!");
- for (Library library in result.component.libraries) {
+ for (Library library in result.component!.libraries) {
if (library.importUri.scheme != 'dart') {
bool usesLegacy =
await uriUsesLegacyLanguageVersion(library.fileUri, options);
diff --git a/pkg/front_end/test/extensions/extensions_test.dart b/pkg/front_end/test/extensions/extensions_test.dart
index 5765b21..2bb86f3 100644
--- a/pkg/front_end/test/extensions/extensions_test.dart
+++ b/pkg/front_end/test/extensions/extensions_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'dart:io' show Directory, Platform;
import 'package:_fe_analyzer_shared/src/testing/id.dart';
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
@@ -45,7 +43,7 @@
InternalCompilerResult compilerResult,
Member member,
Map<Id, ActualData<Features>> actualMap,
- {bool verbose}) {
+ {bool? verbose}) {
member.accept(new ExtensionsDataExtractor(compilerResult, actualMap));
}
@@ -55,7 +53,7 @@
InternalCompilerResult compilerResult,
Class cls,
Map<Id, ActualData<Features>> actualMap,
- {bool verbose}) {
+ {bool? verbose}) {
new ExtensionsDataExtractor(compilerResult, actualMap).computeForClass(cls);
}
@@ -65,7 +63,7 @@
InternalCompilerResult compilerResult,
Library library,
Map<Id, ActualData<Features>> actualMap,
- {bool verbose}) {
+ {bool? verbose}) {
new ExtensionsDataExtractor(compilerResult, actualMap)
.computeForLibrary(library);
}
@@ -76,7 +74,7 @@
InternalCompilerResult compilerResult,
Extension extension,
Map<Id, ActualData<Features>> actualMap,
- {bool verbose}) {
+ {bool? verbose}) {
new ExtensionsDataExtractor(compilerResult, actualMap)
.computeForExtension(extension);
}
@@ -146,9 +144,9 @@
Features computeLibraryValue(Id id, Library library) {
Features features = new Features();
SourceLibraryBuilder libraryBuilder =
- lookupLibraryBuilder(compilerResult, library);
+ lookupLibraryBuilder(compilerResult, library) as SourceLibraryBuilder;
libraryBuilder.forEachExtensionInScope((ExtensionBuilder extension) {
- LibraryBuilder library = extension.parent;
+ LibraryBuilder library = extension.parent as LibraryBuilder;
String libraryPrefix = '';
if (library != libraryBuilder) {
libraryPrefix = '${library.fileUri.pathSegments.last}.';
@@ -159,28 +157,32 @@
}
@override
- Features computeClassValue(Id id, Class cls) {
- ClassBuilder clsBuilder = lookupClassBuilder(compilerResult, cls);
+ Features? computeClassValue(Id id, Class cls) {
+ ClassBuilder clsBuilder =
+ lookupClassBuilder(compilerResult, cls) as ClassBuilder;
if (!clsBuilder.isExtension) {
return null;
}
Features features = new Features();
features[Tags.builderName] = clsBuilder.name;
if (clsBuilder.typeVariables != null) {
- for (TypeVariableBuilder typeVariable in clsBuilder.typeVariables) {
+ for (TypeVariableBuilder typeVariable in clsBuilder.typeVariables!) {
features.addElement(Tags.builderTypeParameters,
typeVariableBuilderToText(typeVariable));
}
}
- features[Tags.builderSupertype] = clsBuilder.supertypeBuilder?.name;
+ if (clsBuilder.supertypeBuilder != null) {
+ features[Tags.builderSupertype] =
+ clsBuilder.supertypeBuilder!.name as String;
+ }
if (clsBuilder.interfaceBuilders != null) {
- for (TypeBuilder superinterface in clsBuilder.interfaceBuilders) {
+ for (TypeBuilder superinterface in clsBuilder.interfaceBuilders!) {
features.addElement(Tags.builderInterfaces, superinterface.name);
}
}
if (clsBuilder.onTypes != null) {
- for (TypeBuilder onType in clsBuilder.onTypes) {
+ for (TypeBuilder onType in clsBuilder.onTypes!) {
features.addElement(Tags.builderOnTypes, typeBuilderToText(onType));
}
}
@@ -190,7 +192,9 @@
features.addElement(
Tags.clsTypeParameters, typeParameterToText(typeParameter));
}
- features[Tags.clsSupertype] = cls.supertype?.classNode?.name;
+ if (cls.supertype != null) {
+ features[Tags.clsSupertype] = cls.supertype!.classNode.name;
+ }
for (Supertype superinterface in cls.implementedTypes) {
features.addElement(Tags.clsInterfaces, superinterface.classNode.name);
}
@@ -198,9 +202,9 @@
}
@override
- Features computeExtensionValue(Id id, Extension extension) {
+ Features? computeExtensionValue(Id id, Extension extension) {
ExtensionBuilder extensionBuilder =
- lookupExtensionBuilder(compilerResult, extension);
+ lookupExtensionBuilder(compilerResult, extension)!;
if (!extensionBuilder.isExtension) {
return null;
}
@@ -208,19 +212,14 @@
features[Tags.builderName] = extensionBuilder.name;
if (extensionBuilder.typeParameters != null) {
for (TypeVariableBuilder typeVariable
- in extensionBuilder.typeParameters) {
+ in extensionBuilder.typeParameters!) {
features.addElement(Tags.builderTypeParameters,
typeVariableBuilderToText(typeVariable));
}
}
- if (extensionBuilder.onType != null) {
- features[Tags.builderOnType] = typeBuilderToText(extensionBuilder.onType);
- }
-
+ features[Tags.builderOnType] = typeBuilderToText(extensionBuilder.onType);
features[Tags.extensionName] = extension.name;
- if (extension.onType != null) {
- features[Tags.extensionOnType] = typeToText(extension.onType);
- }
+ features[Tags.extensionOnType] = typeToText(extension.onType);
for (TypeParameter typeParameter in extension.typeParameters) {
features.addElement(
Tags.extensionTypeParameters, typeParameterToText(typeParameter));
@@ -233,17 +232,17 @@
}
@override
- Features computeMemberValue(Id id, Member member) {
+ Features? computeMemberValue(Id id, Member member) {
if (!member.isExtensionMember) {
return null;
}
- MemberBuilder memberBuilder = lookupMemberBuilder(compilerResult, member);
+ MemberBuilder memberBuilder = lookupMemberBuilder(compilerResult, member)!;
Features features = new Features();
features[Tags.builderName] = memberBuilder.name;
if (memberBuilder is FunctionBuilder) {
if (memberBuilder.formals != null) {
- for (FormalParameterBuilder parameter in memberBuilder.formals) {
+ for (FormalParameterBuilder parameter in memberBuilder.formals!) {
if (parameter.isRequired) {
features.addElement(Tags.builderRequiredParameters, parameter.name);
} else if (parameter.isPositional) {
@@ -259,7 +258,7 @@
features.markAsUnsorted(Tags.builderNamedParameters);
}
if (memberBuilder.typeVariables != null) {
- for (TypeVariableBuilder typeVariable in memberBuilder.typeVariables) {
+ for (TypeVariableBuilder typeVariable in memberBuilder.typeVariables!) {
features.addElement(Tags.builderTypeParameters,
typeVariableBuilderToText(typeVariable));
}
@@ -269,23 +268,23 @@
features[Tags.memberName] = getMemberName(member);
if (member.function != null) {
for (int index = 0;
- index < member.function.positionalParameters.length;
+ index < member.function!.positionalParameters.length;
index++) {
VariableDeclaration parameter =
- member.function.positionalParameters[index];
- if (index < member.function.requiredParameterCount) {
+ member.function!.positionalParameters[index];
+ if (index < member.function!.requiredParameterCount) {
features.addElement(Tags.memberRequiredParameters, parameter.name);
} else {
features.addElement(Tags.memberPositionalParameters, parameter.name);
}
}
- for (VariableDeclaration parameter in member.function.namedParameters) {
+ for (VariableDeclaration parameter in member.function!.namedParameters) {
features.addElement(Tags.memberNamedParameters, parameter.name);
}
features.markAsUnsorted(Tags.memberRequiredParameters);
features.markAsUnsorted(Tags.memberPositionalParameters);
features.markAsUnsorted(Tags.memberNamedParameters);
- for (TypeParameter typeParameter in member.function.typeParameters) {
+ for (TypeParameter typeParameter in member.function!.typeParameters) {
features.addElement(
Tags.memberTypeParameters, typeParameterToText(typeParameter));
}
@@ -295,7 +294,7 @@
}
@override
- Features computeNodeValue(Id id, TreeNode node) {
+ Features? computeNodeValue(Id id, TreeNode node) {
if (node is ThisExpression) {
Features features = new Features();
features.add(Tags.hasThis);
diff --git a/pkg/front_end/test/fasta/ambiguous_export_test.dart b/pkg/front_end/test/fasta/ambiguous_export_test.dart
index fa0ef8f..2b9dc15 100644
--- a/pkg/front_end/test/fasta/ambiguous_export_test.dart
+++ b/pkg/front_end/test/fasta/ambiguous_export_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
import 'package:async_helper/async_helper.dart' show asyncTest;
import 'package:expect/expect.dart' show Expect;
@@ -34,7 +32,7 @@
await c.options.getUriTranslator(), c.options.target);
target.loader.appendLibraries(component);
DillLibraryBuilder builder = target.loader.read(library.importUri, -1);
- await target.loader.buildOutline(builder);
+ target.loader.buildOutline(builder);
builder.markAsReadyToFinalizeExports();
var mainExport =
builder.exportScope.lookupLocalMember("main", setter: false);
diff --git a/pkg/front_end/test/fasta/analyze_git_test.dart b/pkg/front_end/test/fasta/analyze_git_test.dart
index c649729..30d0ba3 100644
--- a/pkg/front_end/test/fasta/analyze_git_test.dart
+++ b/pkg/front_end/test/fasta/analyze_git_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'dart:io' show exitCode;
import "package:testing/src/run_tests.dart" as testing show main;
diff --git a/pkg/front_end/test/fasta/analyze_src_with_lints_git_test.dart b/pkg/front_end/test/fasta/analyze_src_with_lints_git_test.dart
index b4b0e5f..664e888 100644
--- a/pkg/front_end/test/fasta/analyze_src_with_lints_git_test.dart
+++ b/pkg/front_end/test/fasta/analyze_src_with_lints_git_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'dart:io' show exitCode;
import "package:testing/src/run_tests.dart" as testing show main;
diff --git a/pkg/front_end/test/fasta/assert_locations_test.dart b/pkg/front_end/test/fasta/assert_locations_test.dart
index 029196a..4f4787e 100644
--- a/pkg/front_end/test/fasta/assert_locations_test.dart
+++ b/pkg/front_end/test/fasta/assert_locations_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.assert_locations_test;
import 'package:async_helper/async_helper.dart' show asyncTest;
@@ -47,7 +45,7 @@
// parenthesis of the assert statement to the first character of the
// condition.
void makeAssertWithMessage(String condition,
- {String message, bool trailingComma: false, int additionalOffset: 0}) {
+ {String? message, bool trailingComma: false, int additionalOffset: 0}) {
final name = 'testCase${spans.length}';
sb.writeln('void $name(x) {');
sb.write('assert(');
@@ -110,7 +108,7 @@
/// When [AssertStatement] is reached it is checked against this
/// span.
- ConditionSpan expectedSpan;
+ ConditionSpan? expectedSpan;
VerifyingVisitor(this.test);
@@ -126,8 +124,8 @@
@override
void visitAssertStatement(AssertStatement node) {
- Expect.equals(expectedSpan.startOffset, node.conditionStartOffset);
- Expect.equals(expectedSpan.endOffset, node.conditionEndOffset);
+ Expect.equals(expectedSpan!.startOffset, node.conditionStartOffset);
+ Expect.equals(expectedSpan!.endOffset, node.conditionEndOffset);
}
}
@@ -139,12 +137,12 @@
Expect.fail(
"Unexpected message: ${message.plainTextFormatted.join('\n')}");
};
- Component p = (await compileScript(test.source,
+ Component? p = (await compileScript(test.source,
options: options, fileName: 'synthetic-test.dart'))
?.component;
Expect.isNotNull(p);
VerifyingVisitor visitor = new VerifyingVisitor(test);
- p.mainMethod.enclosingLibrary.accept(visitor);
+ p!.mainMethod!.enclosingLibrary.accept(visitor);
Expect.setEquals(test.spans.keys, visitor.verified);
});
}
diff --git a/pkg/front_end/test/fasta/bootstrap_test.dart b/pkg/front_end/test/fasta/bootstrap_test.dart
index fd49f77..750c685 100644
--- a/pkg/front_end/test/fasta/bootstrap_test.dart
+++ b/pkg/front_end/test/fasta/bootstrap_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'dart:io' show Directory, File, Platform;
import 'package:async_helper/async_helper.dart' show asyncEnd, asyncStart;
diff --git a/pkg/front_end/test/fasta/expression_suite.dart b/pkg/front_end/test/fasta/expression_suite.dart
index d6159f9..bc3fb8e 100644
--- a/pkg/front_end/test/fasta/expression_suite.dart
+++ b/pkg/front_end/test/fasta/expression_suite.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.expression_test;
import "dart:convert" show JsonEncoder;
@@ -73,7 +71,7 @@
ProcessedOptions get options => compilerContext.options;
- MemoryFileSystem get fileSystem => options.fileSystem;
+ MemoryFileSystem get fileSystem => options.fileSystem as MemoryFileSystem;
Future<T> runInContext<T>(Future<T> action(CompilerContext c)) {
return compilerContext.runInContext<T>(action);
@@ -91,7 +89,7 @@
}
class CompilationResult {
- Procedure compiledProcedure;
+ Procedure? compiledProcedure;
List<DiagnosticMessage> errors;
CompilationResult(this.compiledProcedure, this.errors);
@@ -115,7 +113,7 @@
buffer.write("<no procedure>");
} else {
Printer printer = new Printer(buffer);
- printer.visitProcedure(compiledProcedure);
+ printer.visitProcedure(compiledProcedure!);
printer.writeConstantTable(new Component());
}
Uri base = entryPoint.resolve(".");
@@ -126,9 +124,9 @@
class TestCase {
final TestDescription description;
- final Uri entryPoint;
+ final Uri? entryPoint;
- final Uri import;
+ final Uri? import;
final List<String> definitions;
@@ -136,13 +134,13 @@
final bool isStaticMethod;
- final Uri library;
+ final Uri? library;
- final String className;
+ final String? className;
- final String methodName;
+ final String? methodName;
- String expression;
+ String? expression;
List<CompilationResult> results = [];
@@ -170,12 +168,12 @@
"static = $isStaticMethod)";
}
- String validate() {
+ String? validate() {
print(this);
if (entryPoint == null) {
return "No entryPoint.";
}
- if (!(new File.fromUri(entryPoint)).existsSync()) {
+ if (!(new File.fromUri(entryPoint!)).existsSync()) {
return "Entry point $entryPoint doesn't exist.";
}
if (library == null) {
@@ -202,10 +200,10 @@
Future<Result<Null>> run(List<TestCase> tests, Context context) async {
String actual = "";
for (var test in tests) {
- var primary = test.results.first.printResult(test.entryPoint, context);
+ var primary = test.results.first.printResult(test.entryPoint!, context);
actual += primary;
for (int i = 1; i < test.results.length; ++i) {
- var secondary = test.results[i].printResult(test.entryPoint, context);
+ var secondary = test.results[i].printResult(test.entryPoint!, context);
if (primary != secondary) {
return fail(
null,
@@ -256,15 +254,15 @@
Uri uri = description.uri;
String contents = await new File.fromUri(uri).readAsString();
- Uri entryPoint;
- Uri import;
+ Uri? entryPoint;
+ Uri? import;
List<String> definitions = <String>[];
List<String> typeDefinitions = <String>[];
bool isStaticMethod = false;
- Uri library;
- String className;
- String methodName;
- String expression;
+ Uri? library;
+ String? className;
+ String? methodName;
+ String? expression;
dynamic maps = loadYamlNode(contents, sourceUrl: uri);
if (maps is YamlMap) maps = [maps];
@@ -282,7 +280,7 @@
} else if (key == "position") {
Uri uri = description.uri.resolveUri(Uri.parse(value as String));
library = uri.removeFragment();
- if (uri.fragment != null && uri.fragment != '') {
+ if (uri.fragment != '') {
className = uri.fragment;
}
} else if (key == "method") {
@@ -339,12 +337,12 @@
.add(new TypeParameter(name, new DynamicType(), new DynamicType()));
}
- Procedure compiledProcedure = await compiler.compileExpression(
- test.expression,
+ Procedure? compiledProcedure = await compiler.compileExpression(
+ test.expression!,
definitions,
typeParams,
"debugExpr",
- test.library,
+ test.library!,
className: test.className,
methodName: test.methodName,
isStatic: test.isStaticMethod,
@@ -363,23 +361,24 @@
Future<Result<List<TestCase>>> run(
List<TestCase> tests, Context context) async {
for (var test in tests) {
- context.fileSystem.entityForUri(test.entryPoint).writeAsBytesSync(
- await new File.fromUri(test.entryPoint).readAsBytes());
+ context.fileSystem.entityForUri(test.entryPoint!).writeAsBytesSync(
+ await new File.fromUri(test.entryPoint!).readAsBytes());
if (test.import != null) {
- context.fileSystem.entityForUri(test.import).writeAsBytesSync(
- await new File.fromUri(test.import).readAsBytes());
+ context.fileSystem.entityForUri(test.import!).writeAsBytesSync(
+ await new File.fromUri(test.import!).readAsBytes());
}
var sourceCompiler = new IncrementalCompiler(context.compilerContext);
Component component =
- await sourceCompiler.computeDelta(entryPoints: [test.entryPoint]);
+ await sourceCompiler.computeDelta(entryPoints: [test.entryPoint!]);
var errors = context.takeErrors();
if (!errors.isEmpty) {
return fail(tests, "Couldn't compile entry-point: $errors");
}
Uri dillFileUri = new Uri(
- scheme: test.entryPoint.scheme, path: test.entryPoint.path + ".dill");
+ scheme: test.entryPoint!.scheme,
+ path: test.entryPoint!.path + ".dill");
File dillFile = new File.fromUri(dillFileUri);
if (!await dillFile.exists()) {
await writeComponentToFile(component, dillFileUri);
@@ -391,7 +390,7 @@
var dillCompiler =
new IncrementalCompiler(context.compilerContext, dillFileUri);
component =
- await dillCompiler.computeDelta(entryPoints: [test.entryPoint]);
+ await dillCompiler.computeDelta(entryPoints: [test.entryPoint!]);
component.computeCanonicalNames();
await dillFile.delete();
diff --git a/pkg/front_end/test/fasta/generator_to_string_test.dart b/pkg/front_end/test/fasta/generator_to_string_test.dart
index b2c4f52..5fdfef0 100644
--- a/pkg/front_end/test/fasta/generator_to_string_test.dart
+++ b/pkg/front_end/test/fasta/generator_to_string_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
/// Test of toString on generators.
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'
@@ -32,7 +30,8 @@
VariableDeclaration,
VariableGet,
VoidType,
- defaultLanguageVersion;
+ defaultLanguageVersion,
+ dummyLibraryDependency;
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
@@ -61,6 +60,8 @@
import 'package:front_end/src/fasta/source/source_library_builder.dart'
show ImplicitLanguageVersion, SourceLibraryBuilder;
+import '../mock_file_system.dart';
+
void check(String expected, Object generator) {
Expect.stringEquals(expected, "$generator");
}
@@ -88,7 +89,7 @@
/*packageUri*/ null,
new ImplicitLanguageVersion(defaultLanguageVersion),
new KernelTarget(
- null,
+ const MockFileSystem(),
false,
new DillTarget(c.options.ticker, uriTranslator,
new NoneTarget(new TargetFlags())),
@@ -97,7 +98,7 @@
null);
libraryBuilder.markLanguageVersionFinal();
LoadLibraryBuilder loadLibraryBuilder =
- new LoadLibraryBuilder(libraryBuilder, null, -1);
+ new LoadLibraryBuilder(libraryBuilder, dummyLibraryDependency, -1);
Procedure getter = new Procedure(
new Name("myGetter"), ProcedureKind.Getter, new FunctionNode(null),
fileUri: uri);
diff --git a/pkg/front_end/test/fasta/incremental_dartino_suite.dart b/pkg/front_end/test/fasta/incremental_dartino_suite.dart
index f3b354e..2f7c9f6 100644
--- a/pkg/front_end/test/fasta/incremental_dartino_suite.dart
+++ b/pkg/front_end/test/fasta/incremental_dartino_suite.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.incremental_test;
import "dart:convert" show JsonEncoder;
@@ -73,7 +71,7 @@
ProcessedOptions get options => compilerContext.options;
- MemoryFileSystem get fileSystem => options.fileSystem;
+ MemoryFileSystem get fileSystem => options.fileSystem as MemoryFileSystem;
Future<T> runInContext<T>(Future<T> action(CompilerContext c)) {
return compilerContext.runInContext<T>(action);
@@ -103,9 +101,9 @@
Uri uri = description.uri;
String contents = await new File.fromUri(uri).readAsString();
Map<String, List<String>> sources = <String, List<String>>{};
- List<IncrementalExpectation> expectations;
+ List<IncrementalExpectation>? expectations;
bool firstPatch = true;
- YamlMap map = loadYamlNode(contents, sourceUrl: uri);
+ YamlMap map = loadYamlNode(contents, sourceUrl: uri) as YamlMap;
map.forEach((_fileName, _contents) {
String fileName = _fileName; // Strong mode hurray!
String contents = _contents; // Strong mode hurray!
@@ -135,7 +133,7 @@
Future<Result<TestCase>> run(TestCase test, Context context) async {
for (int edits = 0;; edits++) {
bool foundSources = false;
- test.sources.forEach((String name, List<String> sources) {
+ test.sources!.forEach((String name, List<String> sources) {
if (edits < sources.length) {
String source = sources[edits];
Uri uri = base.resolve(name);
@@ -157,7 +155,7 @@
Component component =
await compiler.computeDelta(entryPoints: [entryPoint]);
List<DiagnosticMessage> errors = context.takeErrors();
- if (test.expectations[edits].hasCompileTimeError) {
+ if (test.expectations![edits].hasCompileTimeError) {
if (errors.isEmpty) {
return fail(test, "Compile-time error expected, but none reported");
}
@@ -176,9 +174,9 @@
class TestCase {
final TestDescription description;
- final Map<String, List<String>> sources;
+ final Map<String, List<String>>? sources;
- final List<IncrementalExpectation> expectations;
+ final List<IncrementalExpectation>? expectations;
TestCase(this.description, this.sources, this.expectations);
@@ -192,16 +190,16 @@
if (sources == null) {
return step.fail(this, "No sources.");
}
- if (expectations == null || expectations.isEmpty) {
+ if (expectations == null || expectations!.isEmpty) {
return step.fail(this, "No expectations.");
}
- for (String name in sources.keys) {
- List<String> versions = sources[name];
- if (versions.length != 1 && versions.length != expectations.length) {
+ for (String name in sources!.keys) {
+ List<String> versions = sources![name]!;
+ if (versions.length != 1 && versions.length != expectations!.length) {
return step.fail(
this,
"Found ${versions.length} versions of $name,"
- " but expected 1 or ${expectations.length}.");
+ " but expected 1 or ${expectations!.length}.");
}
}
return step.pass(this);
diff --git a/pkg/front_end/test/fasta/incremental_expectations.dart b/pkg/front_end/test/fasta/incremental_expectations.dart
index 7e9a45c..1bf173d 100644
--- a/pkg/front_end/test/fasta/incremental_expectations.dart
+++ b/pkg/front_end/test/fasta/incremental_expectations.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
library fasta.test.incremental_expectations;
import "dart:convert" show JsonDecoder, JsonEncoder;
diff --git a/pkg/front_end/test/fasta/incremental_hello_test.dart b/pkg/front_end/test/fasta/incremental_hello_test.dart
index 33d7d3a..ea8e04c 100644
--- a/pkg/front_end/test/fasta/incremental_hello_test.dart
+++ b/pkg/front_end/test/fasta/incremental_hello_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.incremental_dynamic_test;
import 'package:async_helper/async_helper.dart' show asyncTest;
@@ -34,7 +32,7 @@
throw "Unexpected message: ${message.plainTextFormatted.join('\n')}";
}
-Future<void> test({bool sdkFromSource}) async {
+Future<void> test({required bool sdkFromSource}) async {
final CompilerOptions optionBuilder = new CompilerOptions()
..packagesFileUri = Uri.base.resolve(".packages")
..target = new VmTarget(new TargetFlags())
diff --git a/pkg/front_end/test/fasta/incremental_source_files.dart b/pkg/front_end/test/fasta/incremental_source_files.dart
index 4108b62..274e65b 100644
--- a/pkg/front_end/test/fasta/incremental_source_files.dart
+++ b/pkg/front_end/test/fasta/incremental_source_files.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.incremental_source_files;
/// Expand a file with diffs in common merge conflict format into a [List] that
@@ -57,10 +55,9 @@
/// ["head v1 tail", "head v2 tail"]
List<String> expandUpdates(List updates) {
int outputCount = updates.firstWhere((e) => e is Iterable).length;
- List<StringBuffer> result = new List<StringBuffer>.filled(outputCount, null);
- for (int i = 0; i < outputCount; i++) {
- result[i] = new StringBuffer();
- }
+ List<StringBuffer> result = new List<StringBuffer>.generate(
+ outputCount, (_) => new StringBuffer(),
+ growable: false);
for (var chunk in updates) {
if (chunk is Iterable) {
int segmentCount = 0;
diff --git a/pkg/front_end/test/fasta/link_test.dart b/pkg/front_end/test/fasta/link_test.dart
index d1029eb..7918446 100644
--- a/pkg/front_end/test/fasta/link_test.dart
+++ b/pkg/front_end/test/fasta/link_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// @dart = 2.9
-
import 'package:_fe_analyzer_shared/src/util/link.dart' show Link, LinkBuilder;
import 'package:expect/expect.dart' show Expect;
@@ -45,6 +43,6 @@
Expect.stringEquals("[ B, A ]", "${strings.reverse(const Link<String>())}");
Link<int> ints =
- const Link<int>().prepend(1).reverse(const Link<int>()).tail.prepend(1);
+ const Link<int>().prepend(1).reverse(const Link<int>()).tail!.prepend(1);
Expect.stringEquals("[ 1 ]", "${ints}");
}
diff --git a/pkg/front_end/test/mock_file_system.dart b/pkg/front_end/test/mock_file_system.dart
new file mode 100644
index 0000000..60e45be
--- /dev/null
+++ b/pkg/front_end/test/mock_file_system.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2021, 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:front_end/src/api_prototype/file_system.dart';
+
+class MockFileSystem implements FileSystem {
+ final String? scheme;
+
+ const MockFileSystem({this.scheme});
+
+ @override
+ FileSystemEntity entityForUri(Uri uri) {
+ if (scheme != null && uri.scheme != scheme) throw "unsupported";
+ return new MockFileSystemEntity(uri, this);
+ }
+}
+
+class MockFileSystemEntity implements FileSystemEntity {
+ @override
+ final Uri uri;
+ final FileSystem fileSystem;
+ MockFileSystemEntity(this.uri, this.fileSystem);
+
+ @override
+ dynamic noSuchMethod(m) => super.noSuchMethod(m);
+}
diff --git a/pkg/front_end/test/scheme_based_file_system_test.dart b/pkg/front_end/test/scheme_based_file_system_test.dart
index 3be6daf..4332fca 100644
--- a/pkg/front_end/test/scheme_based_file_system_test.dart
+++ b/pkg/front_end/test/scheme_based_file_system_test.dart
@@ -7,10 +7,12 @@
import 'package:test/test.dart';
+import 'mock_file_system.dart';
+
void main() {
test('lookup of registered schemes is handled', () {
- var fs1 = new MockFileSystem('scheme1');
- var fs2 = new MockFileSystem('scheme2');
+ var fs1 = new MockFileSystem(scheme: 'scheme1');
+ var fs2 = new MockFileSystem(scheme: 'scheme2');
var fileSystem =
new SchemeBasedFileSystem({'scheme1': fs1, 'scheme2': fs2});
@@ -23,30 +25,9 @@
});
test('lookup of an unregistered scheme will throw', () {
- var fileSystem =
- new SchemeBasedFileSystem({'scheme1': new MockFileSystem('scheme1')});
+ var fileSystem = new SchemeBasedFileSystem(
+ {'scheme1': new MockFileSystem(scheme: 'scheme1')});
expect(() => fileSystem.entityForUri(Uri.parse('scheme2:a.dart')),
throwsA((e) => e is FileSystemException));
});
}
-
-class MockFileSystem implements FileSystem {
- String scheme;
- MockFileSystem(this.scheme);
-
- @override
- FileSystemEntity entityForUri(Uri uri) {
- if (uri.scheme != scheme) throw "unsupported";
- return new MockFileSystemEntity(uri, this);
- }
-}
-
-class MockFileSystemEntity implements FileSystemEntity {
- @override
- final Uri uri;
- final FileSystem fileSystem;
- MockFileSystemEntity(this.uri, this.fileSystem);
-
- @override
- dynamic noSuchMethod(m) => super.noSuchMethod(m);
-}
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 4e7efb6..3c8bb1f 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -237,6 +237,10 @@
return cid_start == kIllegalCid && cid_end == kIllegalCid;
}
+ bool Equals(const CidRangeValue& other) const {
+ return cid_start == other.cid_start && cid_end == other.cid_end;
+ }
+
intptr_t cid_start;
intptr_t cid_end;
};
@@ -959,6 +963,11 @@
return static_cast<T*>(this);
}
+ template <typename T>
+ const T* Cast() const {
+ return static_cast<const T*>(this);
+ }
+
// Returns structure describing location constraints required
// to emit native code for this instruction.
LocationSummary* locs() {
@@ -8893,7 +8902,9 @@
virtual bool AllowsCSE() const { return true; }
virtual bool HasUnknownSideEffects() const { return false; }
- virtual bool AttributesEqual(const Instruction& other) const { return true; }
+ virtual bool AttributesEqual(const Instruction& other) const {
+ return other.Cast<CheckClassIdInstr>()->cids().Equals(cids_);
+ }
PRINT_OPERANDS_TO_SUPPORT
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 67f8112..e356875 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -29,10 +29,15 @@
// Quick access to the current zone.
#define Z (zone())
-class CSEInstructionMap : public ValueObject {
+// A set of Instructions used by CSE pass.
+//
+// Instructions are compared as if all redefinitions were removed from the
+// graph, with the exception of LoadField instruction which gets special
+// treatment.
+class CSEInstructionSet : public ValueObject {
public:
- CSEInstructionMap() : map_() {}
- explicit CSEInstructionMap(const CSEInstructionMap& other)
+ CSEInstructionSet() : map_() {}
+ explicit CSEInstructionSet(const CSEInstructionSet& other)
: ValueObject(), map_(other.map_) {}
Instruction* Lookup(Instruction* other) const {
@@ -46,7 +51,59 @@
}
private:
- PointerSet<Instruction> map_;
+ static Definition* OriginalDefinition(Value* value) {
+ return value->definition()->OriginalDefinition();
+ }
+
+ static bool EqualsIgnoringRedefinitions(const Instruction& a,
+ const Instruction& b) {
+ const auto tag = a.tag();
+ if (tag != b.tag()) return false;
+ const auto input_count = a.InputCount();
+ if (input_count != b.InputCount()) return false;
+
+ // We would like to avoid replacing a load from a redefinition with a
+ // load from an original definition because that breaks the dependency
+ // on the redefinition and enables potentially incorrect code motion.
+ if (tag != Instruction::kLoadField) {
+ for (intptr_t i = 0; i < input_count; ++i) {
+ if (OriginalDefinition(a.InputAt(i)) !=
+ OriginalDefinition(b.InputAt(i))) {
+ return false;
+ }
+ }
+ } else {
+ for (intptr_t i = 0; i < input_count; ++i) {
+ if (!a.InputAt(i)->Equals(*b.InputAt(i))) return false;
+ }
+ }
+ return a.AttributesEqual(b);
+ }
+
+ class Trait {
+ public:
+ typedef Instruction* Value;
+ typedef Instruction* Key;
+ typedef Instruction* Pair;
+
+ static Key KeyOf(Pair kv) { return kv; }
+ static Value ValueOf(Pair kv) { return kv; }
+
+ static inline uword Hash(Key key) {
+ uword result = key->tag();
+ for (intptr_t i = 0; i < key->InputCount(); ++i) {
+ result = CombineHashes(
+ result, OriginalDefinition(key->InputAt(i))->ssa_temp_index());
+ }
+ return FinalizeHash(result, kBitsPerInt32 - 1);
+ }
+
+ static inline bool IsKeyEqual(Pair kv, Key key) {
+ return EqualsIgnoringRedefinitions(*kv, *key);
+ }
+ };
+
+ DirectChainedHashMap<Trait> map_;
};
// Place describes an abstract location (e.g. field) that IR can load
@@ -2870,13 +2927,14 @@
DISALLOW_COPY_AND_ASSIGN(LoadOptimizer);
};
-bool DominatorBasedCSE::Optimize(FlowGraph* graph) {
+bool DominatorBasedCSE::Optimize(FlowGraph* graph,
+ bool run_load_optimization /* = true */) {
bool changed = false;
- if (FLAG_load_cse) {
+ if (FLAG_load_cse && run_load_optimization) {
changed = LoadOptimizer::OptimizeGraph(graph) || changed;
}
- CSEInstructionMap map;
+ CSEInstructionSet map;
changed = OptimizeRecursive(graph, graph->graph_entry(), &map) || changed;
return changed;
@@ -2884,7 +2942,7 @@
bool DominatorBasedCSE::OptimizeRecursive(FlowGraph* graph,
BlockEntryInstr* block,
- CSEInstructionMap* map) {
+ CSEInstructionSet* map) {
bool changed = false;
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
@@ -2912,7 +2970,7 @@
BlockEntryInstr* child = block->dominated_blocks()[i];
if (i < num_children - 1) {
// Copy map.
- CSEInstructionMap child_map(*map);
+ CSEInstructionSet child_map(*map);
changed = OptimizeRecursive(graph, child, &child_map) || changed;
} else {
// Reuse map for the last child.
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.h b/runtime/vm/compiler/backend/redundancy_elimination.h
index 877223b..6042147 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.h
+++ b/runtime/vm/compiler/backend/redundancy_elimination.h
@@ -14,7 +14,7 @@
namespace dart {
-class CSEInstructionMap;
+class CSEInstructionSet;
class AllocationSinking : public ZoneAllocated {
public:
@@ -88,12 +88,12 @@
public:
// Return true, if the optimization changed the flow graph.
// False, if nothing changed.
- static bool Optimize(FlowGraph* graph);
+ static bool Optimize(FlowGraph* graph, bool run_load_optimization = true);
private:
static bool OptimizeRecursive(FlowGraph* graph,
BlockEntryInstr* entry,
- CSEInstructionMap* map);
+ CSEInstructionSet* map);
};
class DeadStoreElimination : public AllStatic {
diff --git a/runtime/vm/compiler/backend/redundancy_elimination_test.cc b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
index 353fdd7..1dd76e6 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination_test.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
@@ -1352,6 +1352,116 @@
}
}
+// This test checks that CSE unwraps redefinitions when comparing all
+// instructions except loads, which are handled specially.
+ISOLATE_UNIT_TEST_CASE(CSE_Redefinitions) {
+ const char* script_chars = R"(
+ @pragma("vm:external-name", "BlackholeNative")
+ external dynamic blackhole([a, b, c, d, e, f]);
+ class K<T> {
+ final T field;
+ K(this.field);
+ }
+ )";
+ const Library& lib =
+ Library::Handle(LoadTestScript(script_chars, NoopNativeLookup));
+
+ const Class& cls = Class::ZoneHandle(
+ lib.LookupLocalClass(String::Handle(Symbols::New(thread, "K"))));
+ const Error& err = Error::Handle(cls.EnsureIsFinalized(thread));
+ EXPECT(err.IsNull());
+
+ const Field& original_field = Field::Handle(
+ cls.LookupField(String::Handle(Symbols::New(thread, "field"))));
+ EXPECT(!original_field.IsNull());
+ const Field& field = Field::Handle(original_field.CloneFromOriginal());
+
+ const Function& blackhole =
+ Function::ZoneHandle(GetFunction(lib, "blackhole"));
+
+ using compiler::BlockBuilder;
+ CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
+ FlowGraphBuilderHelper H;
+
+ auto b1 = H.flow_graph()->graph_entry()->normal_entry();
+
+ BoxInstr* box0;
+ BoxInstr* box1;
+ LoadFieldInstr* load0;
+ LoadFieldInstr* load1;
+ LoadFieldInstr* load2;
+ StaticCallInstr* call;
+ ReturnInstr* ret;
+
+ {
+ BlockBuilder builder(H.flow_graph(), b1);
+ auto& slot = Slot::Get(field, &H.flow_graph()->parsed_function());
+ auto param0 =
+ builder.AddParameter(0, 0, /*with_frame=*/true, kUnboxedDouble);
+ auto param1 = builder.AddParameter(1, 2, /*with_frame=*/true, kTagged);
+ auto redef0 =
+ builder.AddDefinition(new RedefinitionInstr(new Value(param0)));
+ auto redef1 =
+ builder.AddDefinition(new RedefinitionInstr(new Value(param0)));
+ box0 = builder.AddDefinition(
+ BoxInstr::Create(kUnboxedDouble, new Value(redef0)));
+ box1 = builder.AddDefinition(
+ BoxInstr::Create(kUnboxedDouble, new Value(redef1)));
+
+ auto redef2 =
+ builder.AddDefinition(new RedefinitionInstr(new Value(param1)));
+ auto redef3 =
+ builder.AddDefinition(new RedefinitionInstr(new Value(param1)));
+ load0 = builder.AddDefinition(
+ new LoadFieldInstr(new Value(redef2), slot, InstructionSource()));
+ load1 = builder.AddDefinition(
+ new LoadFieldInstr(new Value(redef3), slot, InstructionSource()));
+ load2 = builder.AddDefinition(
+ new LoadFieldInstr(new Value(redef3), slot, InstructionSource()));
+
+ auto args = new InputsArray(3);
+ args->Add(new Value(load0));
+ args->Add(new Value(load1));
+ args->Add(new Value(load2));
+ call = builder.AddInstruction(new StaticCallInstr(
+ InstructionSource(), blackhole, 0, Array::empty_array(), args,
+ S.GetNextDeoptId(), 0, ICData::RebindRule::kStatic));
+
+ ret = builder.AddReturn(new Value(box1));
+ }
+ H.FinishGraph();
+
+ // Running CSE without load optimization should eliminate redundant boxing
+ // but keep loads intact if they don't have exactly matching inputs.
+ DominatorBasedCSE::Optimize(H.flow_graph(), /*run_load_optimization=*/false);
+
+ EXPECT_PROPERTY(box1, it.WasEliminated());
+ EXPECT_PROPERTY(ret, it.value()->definition() == box0);
+
+ EXPECT_PROPERTY(load0, !it.WasEliminated());
+ EXPECT_PROPERTY(load1, !it.WasEliminated());
+ EXPECT_PROPERTY(load2, it.WasEliminated());
+
+ EXPECT_PROPERTY(call, it.ArgumentAt(0) == load0);
+ EXPECT_PROPERTY(call, it.ArgumentAt(1) == load1);
+ EXPECT_PROPERTY(call, it.ArgumentAt(2) == load1);
+
+ // Running load optimization pass should remove the second load but
+ // insert a redefinition to prevent code motion because the field
+ // has a generic type.
+ DominatorBasedCSE::Optimize(H.flow_graph(), /*run_load_optimization=*/true);
+
+ EXPECT_PROPERTY(load0, !it.WasEliminated());
+ EXPECT_PROPERTY(load1, it.WasEliminated());
+ EXPECT_PROPERTY(load2, it.WasEliminated());
+
+ EXPECT_PROPERTY(call, it.ArgumentAt(0) == load0);
+ EXPECT_PROPERTY(call, it.ArgumentAt(1)->IsRedefinition() &&
+ it.ArgumentAt(1)->OriginalDefinition() == load0);
+ EXPECT_PROPERTY(call, it.ArgumentAt(2)->IsRedefinition() &&
+ it.ArgumentAt(2)->OriginalDefinition() == load0);
+}
+
#endif // !defined(TARGET_ARCH_IA32)
} // namespace dart
diff --git a/runtime/vm/hash_map.h b/runtime/vm/hash_map.h
index 2c816a6..aacbf87 100644
--- a/runtime/vm/hash_map.h
+++ b/runtime/vm/hash_map.h
@@ -310,7 +310,7 @@
ASSERT_NOTNULL(zone),
initial_size) {}
- // There is a current use of the copy constructor in CSEInstructionMap
+ // There is a current use of the copy constructor in CSEInstructionSet
// (compiler/backend/redundancy_elimination.cc), so work is needed if we
// want to disallow it.
DirectChainedHashMap(const DirectChainedHashMap& other)
diff --git a/tools/VERSION b/tools/VERSION
index c188fa2..7a5b83a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 17
+PRERELEASE 18
PRERELEASE_PATCH 0
\ No newline at end of file