Version 2.16.0-17.0.dev
Merge commit 'c6fe9e21f005537ecfff947a0e5dbab129a17c26' into 'dev'
diff --git a/pkg/front_end/test/comments_on_certain_arguments_tool.dart b/pkg/front_end/test/comments_on_certain_arguments_tool.dart
index ce02610..49d1ecbe 100644
--- a/pkg/front_end/test/comments_on_certain_arguments_tool.dart
+++ b/pkg/front_end/test/comments_on_certain_arguments_tool.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:convert' show utf8;
import 'dart:io'
@@ -56,7 +54,7 @@
Set<Uri> libUris = {};
-Component component;
+late Component component;
Future<void> main(List<String> args) async {
api.CompilerOptions compilerOptions = getOptions();
@@ -100,8 +98,9 @@
List<Uri> editsPerformed = [];
for (Uri uri in edits.keys) {
print("\n\n\n");
- if (edits[uri] != null && edits[uri].isNotEmpty) {
- String update;
+ List<Edit>? theseEdits = edits[uri];
+ if (theseEdits != null && theseEdits.isNotEmpty) {
+ String? update;
while (update != "y" &&
update != "yes" &&
update != "n" &&
@@ -111,9 +110,8 @@
}
if (update != "y" && update != "yes") continue;
- List<Edit> theseEdits = edits[uri];
theseEdits.sort();
- String content = utf8.decode(component.uriToSource[uri].source,
+ String content = utf8.decode(component.uriToSource[uri]!.source,
allowMalformed: true);
StringBuffer sb = new StringBuffer();
int latest = 0;
@@ -128,7 +126,7 @@
case EditType.Delete:
print(edit);
// We "delete" by skipping...
- latest = edit.offset + edit.length;
+ latest = edit.offset + edit.length!;
break;
}
}
@@ -195,19 +193,19 @@
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
super.visitSuperMethodInvocation(node);
- note(node.interfaceTargetReference.node, node.arguments, node);
+ note(node.interfaceTargetReference!.node!, node.arguments, node);
}
@override
void visitStaticInvocation(StaticInvocation node) {
super.visitStaticInvocation(node);
- note(node.targetReference.node, node.arguments, node);
+ note(node.targetReference.node!, node.arguments, node);
}
@override
void visitConstructorInvocation(ConstructorInvocation node) {
super.visitConstructorInvocation(node);
- note(node.targetReference.node, node.arguments, node);
+ note(node.targetReference.node!, node.arguments, node);
}
void note(
@@ -223,28 +221,25 @@
for (int i = 0; i < arguments.positional.length; i++) {
bool wantComment = false;
- if (arguments.positional[i] is NullLiteral ||
- arguments.positional[i] is BoolLiteral ||
- arguments.positional[i] is IntLiteral) {
+ Expression argument = arguments.positional[i];
+ if (argument is NullLiteral ||
+ argument is BoolLiteral ||
+ argument is IntLiteral) {
wantComment = true;
- } else if (arguments.positional[i] is MapLiteral) {
- MapLiteral literal = arguments.positional[i];
- if (literal.entries.isEmpty) wantComment = true;
- } else if (arguments.positional[i] is ListLiteral) {
- ListLiteral literal = arguments.positional[i];
- if (literal.expressions.isEmpty) wantComment = true;
- } else if (arguments.positional[i] is InstanceInvocation) {
- InstanceInvocation methodInvocation = arguments.positional[i];
- if (methodInvocation.receiver is NullLiteral ||
- methodInvocation.receiver is IntLiteral ||
- methodInvocation.receiver is BoolLiteral) {
+ } else if (argument is MapLiteral) {
+ if (argument.entries.isEmpty) wantComment = true;
+ } else if (argument is ListLiteral) {
+ if (argument.expressions.isEmpty) wantComment = true;
+ } else if (argument is InstanceInvocation) {
+ if (argument.receiver is NullLiteral ||
+ argument.receiver is IntLiteral ||
+ argument.receiver is BoolLiteral) {
wantComment = true;
}
- } else if (arguments.positional[i] is DynamicInvocation) {
- DynamicInvocation methodInvocation = arguments.positional[i];
- if (methodInvocation.receiver is NullLiteral ||
- methodInvocation.receiver is IntLiteral ||
- methodInvocation.receiver is BoolLiteral) {
+ } else if (argument is DynamicInvocation) {
+ if (argument.receiver is NullLiteral ||
+ argument.receiver is IntLiteral ||
+ argument.receiver is BoolLiteral) {
wantComment = true;
}
}
@@ -269,10 +264,10 @@
return;
}
if (argumentExpression.fileOffset == -1) return;
- Location location = argumentExpression.location;
- Token token = cache[location.file];
+ Location location = argumentExpression.location!;
+ Token token = cache[location.file]!;
while (token.offset != argumentExpression.fileOffset) {
- token = token.next;
+ token = token.next!;
if (token.isEof) {
throw "Couldn't find token for $argumentExpression "
"(${argumentExpression.fileOffset}).";
@@ -280,7 +275,7 @@
}
bool foundComment = false;
List<CommentToken> badComments = [];
- CommentToken commentToken = token.precedingComments;
+ CommentToken? commentToken = token.precedingComments;
while (commentToken != null) {
if (commentToken.lexeme == expectedComment) {
// Exact match.
@@ -291,17 +286,16 @@
commentToken.lexeme.endsWith("= */")) {
badComments.add(commentToken);
}
- commentToken = commentToken.next;
+ commentToken = commentToken.next as CommentToken?;
}
if (badComments.isNotEmpty) {
for (CommentToken comment in badComments) {
Location calculatedLocation =
- component.getLocation(location.file, comment.offset);
+ component.getLocation(location.file, comment.offset)!;
print("Please remove comment of length ${comment.lexeme.length} at "
"${comment.offset} => "
"${calculatedLocation}");
- edits[location.file] ??= [];
- edits[location.file]
+ (edits[location.file] ??= [])
.add(new Edit.delete(comment.offset, comment.lexeme.length));
}
}
@@ -309,12 +303,12 @@
return;
}
Location calculatedLocation =
- component.getLocation(location.file, token.offset);
+ component.getLocation(location.file, token.offset)!;
print("Please add comment $expectedComment at "
"${token.offset} => "
"${calculatedLocation}");
- edits[location.file] ??= [];
- edits[location.file].add(new Edit.insert(token.offset, expectedComment));
+ (edits[location.file] ??= [])
+ .add(new Edit.insert(token.offset, expectedComment));
}
Map<Uri, List<Edit>> edits = {};
@@ -323,8 +317,8 @@
class Edit implements Comparable<Edit> {
final int offset;
- final int length;
- final String insertData;
+ final int? length;
+ final String? insertData;
final EditType editType;
Edit.insert(this.offset, this.insertData)
: editType = EditType.Insert,
diff --git a/pkg/front_end/test/compile_benchmark.dart b/pkg/front_end/test/compile_benchmark.dart
index 2ad309a..c244da3 100644
--- a/pkg/front_end/test/compile_benchmark.dart
+++ b/pkg/front_end/test/compile_benchmark.dart
@@ -1,5 +1,3 @@
-// @dart = 2.9
-
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
@@ -23,7 +21,7 @@
Platform.script.resolve("compile_benchmark_helper.dart");
void main(List<String> args) {
- List<String> arguments;
+ List<String>? arguments;
bool tryToAnnotate = false;
bool tryToSlowDown = false;
bool timeInsteadOfCount = false;
@@ -135,7 +133,7 @@
if (tryToSlowDown) {
didSomething = true;
for (Procedure p in sortedProcedures) {
- Uri busyWaiting = busyWaitProcedure(
+ Uri? busyWaiting = busyWaitProcedure(
dillData,
tmp.uri,
(lib) => lib.importUri == p.enclosingLibrary.importUri,
@@ -245,13 +243,14 @@
///
/// The annotation is copied from the [preferInlineMe] method in the helper.
Uri preferInlineProcedure(List<int> dillData, Uri tmp,
- bool libraryMatcher(Library lib), String className, String procedureName) {
+ bool libraryMatcher(Library lib), String? className, String procedureName) {
Component component = new Component();
new BinaryBuilder(dillData, disableLazyReading: true)
.readComponent(component);
Procedure preferInlineMeProcedure = getProcedure(component,
(lib) => lib.fileUri == benchmarkHelper, null, "preferInlineMe");
- ConstantExpression annotation = preferInlineMeProcedure.annotations.single;
+ ConstantExpression annotation =
+ preferInlineMeProcedure.annotations.single as ConstantExpression;
Procedure markProcedure =
getProcedure(component, libraryMatcher, className, procedureName);
markProcedure.addAnnotation(
@@ -268,8 +267,8 @@
///
/// This will make the procedure busy-wait approximately 0.002 ms for each
/// invocation (+ whatever overhead and imprecision).
-Uri busyWaitProcedure(List<int> dillData, Uri tmp,
- bool libraryMatcher(Library lib), String className, String procedureName) {
+Uri? busyWaitProcedure(List<int> dillData, Uri tmp,
+ bool libraryMatcher(Library lib), String? className, String procedureName) {
Component component = new Component();
new BinaryBuilder(dillData, disableLazyReading: true)
.readComponent(component);
@@ -280,7 +279,7 @@
getProcedure(component, libraryMatcher, className, procedureName);
if (markProcedure.function.body == null) return null;
- Statement orgBody = markProcedure.function.body;
+ Statement orgBody = markProcedure.function.body as Statement;
markProcedure.function.body = new Block([
new ExpressionStatement(new StaticInvocation(
busyWaitProcedure, new Arguments([new IntLiteral(2 /* 0.002 ms */)]))),
@@ -375,13 +374,13 @@
if (node.function.body == null) return;
int procedureNum = procedures.length;
procedures.add(node);
- Statement orgBody = node.function.body;
+ Statement orgBody = node.function.body as Statement;
node.function.body = new Block([
new ExpressionStatement(new StaticInvocation(registerCallProcedure,
new Arguments([new IntLiteral(procedureNum)]))),
orgBody
]);
- node.function.body.parent = node.function;
+ node.function.body!.parent = node.function;
}
}
@@ -408,7 +407,7 @@
if (node.function.dartAsyncMarker != AsyncMarker.Sync) return;
int procedureNum = procedures.length;
procedures.add(node);
- Statement orgBody = node.function.body;
+ Statement orgBody = node.function.body as Statement;
// Rewrite as
// {
// registerCallStartProcedure(x);
@@ -428,12 +427,12 @@
)
]);
node.function.body = block;
- node.function.body.parent = node.function;
+ node.function.body!.parent = node.function;
}
}
Procedure getProcedure(Component component, bool libraryMatcher(Library lib),
- String className, String procedureName) {
+ String? className, String procedureName) {
Library lib = component.libraries.where(libraryMatcher).single;
List<Procedure> procedures = lib.procedures;
if (className != null) {
@@ -444,7 +443,7 @@
return procedures.where((p) => p.name.text == procedureName).single;
}
-List<int> runXTimes(int x, List<String> arguments, [List<dynamic> stdout]) {
+List<int> runXTimes(int x, List<String> arguments, [List<dynamic>? stdout]) {
List<int> result = [];
Stopwatch stopwatch = new Stopwatch()..start();
for (int i = 0; i < x; i++) {
diff --git a/pkg/front_end/test/crashing_test_case_minimizer.dart b/pkg/front_end/test/crashing_test_case_minimizer.dart
index c5e60d5..55c2b69 100644
--- a/pkg/front_end/test/crashing_test_case_minimizer.dart
+++ b/pkg/front_end/test/crashing_test_case_minimizer.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:convert' show jsonDecode;
import 'dart:io' show File;
@@ -20,8 +18,8 @@
// parser on it and verifies that no syntax errors have been introduced.
Future<void> main(List<String> arguments) async {
- String filename;
- Uri loadJson;
+ String? filename;
+ Uri? loadJson;
for (String arg in arguments) {
if (arg.startsWith("--json=")) {
String json = arg.substring("--json=".length);
@@ -92,7 +90,7 @@
if (settings.noPlatform) {
int i = 0;
while (settings.platformUri == null ||
- new File.fromUri(settings.platformUri).existsSync()) {
+ new File.fromUri(settings.platformUri!).existsSync()) {
settings.platformUri = Uri.base.resolve("nonexisting_$i");
i++;
}
@@ -100,7 +98,7 @@
if (settings.platformUri == null) {
throw "No platform given. Use --platform=/path/to/platform.dill";
}
- if (!new File.fromUri(settings.platformUri).existsSync()) {
+ if (!new File.fromUri(settings.platformUri!).existsSync()) {
throw "The platform file '${settings.platformUri}' doesn't exist";
}
}
diff --git a/pkg/front_end/test/dartdoc_test_test.dart b/pkg/front_end/test/dartdoc_test_test.dart
index 455a8cf..35de337 100644
--- a/pkg/front_end/test/dartdoc_test_test.dart
+++ b/pkg/front_end/test/dartdoc_test_test.dart
@@ -1,5 +1,3 @@
-// @dart = 2.9
-
import 'dart:convert';
import 'dart:typed_data';
@@ -393,7 +391,7 @@
}
int expectCalls = 0;
-String expectCategory;
+String? expectCategory;
void expect(dynamic actual, dynamic expected) {
expectCalls++;
@@ -461,23 +459,23 @@
return false;
}
-impl.CommentString extractFirstComment(String test) {
+impl.CommentString? extractFirstComment(String test) {
Token firstToken = impl.scanRawBytes(utf8.encode(test) as Uint8List);
Token token = firstToken;
while (true) {
- CommentToken comment = token.precedingComments;
+ CommentToken? comment = token.precedingComments;
if (comment != null) {
return impl.extractComments(comment, test);
}
if (token.isEof) break;
- Token next = token.next;
+ Token? next = token.next;
if (next == null) break;
token = next;
}
return null;
}
-List<impl.Test> extractTests(String test, [Uri uri]) {
+List<impl.Test> extractTests(String test, [Uri? uri]) {
return impl.extractTests(utf8.encode(test) as Uint8List,
uri ?? new Uri(scheme: "darttest", path: "/foo.dart"));
}
diff --git a/pkg/front_end/test/dartdoctest_suite.dart b/pkg/front_end/test/dartdoctest_suite.dart
index 447ab20..d71cd57 100644
--- a/pkg/front_end/test/dartdoctest_suite.dart
+++ b/pkg/front_end/test/dartdoctest_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 file.
-// @dart = 2.9
-
import 'package:testing/testing.dart'
show Chain, ChainContext, Result, Step, TestDescription, runMe;
diff --git a/pkg/front_end/test/deps_git_test.dart b/pkg/front_end/test/deps_git_test.dart
index c542bc5..2017352 100644
--- a/pkg/front_end/test/deps_git_test.dart
+++ b/pkg/front_end/test/deps_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.md file.
-// @dart = 2.9
-
import 'dart:io';
import 'package:_fe_analyzer_shared/src/messages/severity.dart';
@@ -75,7 +73,7 @@
new DillTarget(ticker, uriTranslator, c.options.target);
KernelTarget kernelTarget =
new KernelTarget(c.fileSystem, false, dillTarget, uriTranslator);
- Uri platform = c.options.sdkSummary;
+ Uri? platform = c.options.sdkSummary;
if (platform != null) {
var bytes = new File.fromUri(platform).readAsBytesSync();
var platformComponent = loadComponentFromBytes(bytes);
@@ -84,7 +82,7 @@
}
kernelTarget.setEntryPoints(c.options.inputs);
- await dillTarget.buildOutlines();
+ dillTarget.buildOutlines();
await kernelTarget.loader.buildOutlines();
return new List<Uri>.from(c.dependencies);
});
diff --git a/pkg/front_end/test/desugar_test.dart b/pkg/front_end/test/desugar_test.dart
index 82fdd4a..18342bb 100644
--- a/pkg/front_end/test/desugar_test.dart
+++ b/pkg/front_end/test/desugar_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 to ensure that desugaring APIs used by clients like dart2js are
/// always up to date.
///
@@ -33,13 +31,13 @@
Future<void> testRedirectingFactoryDirect() async {
var component = await compileUnit(['a.dart'], {'a.dart': aSource});
- checkIsRedirectingFactory(component, 'a.dart', 'A', 'foo');
+ checkIsRedirectingFactory(component!, 'a.dart', 'A', 'foo');
checkIsRedirectingFactory(component, 'core', 'Uri', 'file');
}
Future<void> testRedirectingFactorySerialized() async {
var component = await compileUnit(['a.dart'], {'a.dart': aSource});
- var bytes = serializeComponent(component);
+ var bytes = serializeComponent(component!);
component = new ir.Component();
new BinaryBuilder(bytes).readComponent(component);
checkIsRedirectingFactory(component, 'a.dart', 'A', 'foo');
@@ -62,8 +60,8 @@
var lib =
component.libraries.firstWhere((l) => l.importUri.path.endsWith(uriPath));
var cls = lib.classes.firstWhere((c) => c.name == className);
- ir.Procedure member =
- cls.members.firstWhere((m) => m.name.text == constructorName);
+ ir.Procedure member = cls.members
+ .firstWhere((m) => m.name.text == constructorName) as ir.Procedure;
Expect.isTrue(
member.kind == ir.ProcedureKind.Factory, "$member is not a factory");
Expect.isTrue(api.isRedirectingFactory(member));
diff --git a/pkg/front_end/test/dijkstras_sssp_algorithm.dart b/pkg/front_end/test/dijkstras_sssp_algorithm.dart
index 1c0a623..398ea2c 100644
--- a/pkg/front_end/test/dijkstras_sssp_algorithm.dart
+++ b/pkg/front_end/test/dijkstras_sssp_algorithm.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:collection';
/// Dijkstra's algorithm for single source shortest path.
@@ -21,8 +19,8 @@
DijkstrasAlgorithm(Iterable<GraphNode<E>> graphNodes, GraphNode<E> source,
int Function(E, E) comparator, int Function(E, E) distance) {
SplayTreeSet<GraphNode<E>> q = new SplayTreeSet<GraphNode<E>>((a, b) {
- int distA = dist[a];
- int distB = dist[b];
+ int? distA = dist[a];
+ int? distB = dist[b];
int when0() {
if (identical(a, b)) return 0;
@@ -39,7 +37,7 @@
if (distA == null && distB == null) {
return when0();
}
- if (distA < distB) return -1;
+ if (distA! < distB!) return -1;
if (distA > distB) return 1;
return when0();
});
@@ -56,7 +54,7 @@
while (q.isNotEmpty) {
GraphNode<E> u = q.first;
- int distToU = dist[u];
+ int? distToU = dist[u];
if (distToU == null) {
// No path to any of the remaining ${q.length} nodes.
break;
@@ -68,7 +66,7 @@
int distanceUToV = distance(u.node, v.node);
if (distanceUToV < 0) throw "Got negative distance. That's not allowed";
int alt = distToU + distanceUToV;
- int distToV = dist[v];
+ int? distToV = dist[v];
if (distToV == null || alt < distToV) {
// Decrease length (decrease priority in priority queue).
q.remove(v);
@@ -85,7 +83,7 @@
GraphNode<E> u = target;
while (u == source || prev[u] != null) {
path.add(u.node);
- u = prev[u];
+ u = prev[u]!;
}
return path.reversed.toList();
}
diff --git a/pkg/front_end/test/dill_round_trip_test.dart b/pkg/front_end/test/dill_round_trip_test.dart
index 32d5759..716b186 100644
--- a/pkg/front_end/test/dill_round_trip_test.dart
+++ b/pkg/front_end/test/dill_round_trip_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:front_end/src/fasta/kernel/utils.dart' show serializeComponent;
import 'package:kernel/ast.dart' show Component;
diff --git a/pkg/front_end/test/explicit_creation_git_test.dart b/pkg/front_end/test/explicit_creation_git_test.dart
index 5028925..dc8e3a1 100644
--- a/pkg/front_end/test/explicit_creation_git_test.dart
+++ b/pkg/front_end/test/explicit_creation_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';
import 'package:_fe_analyzer_shared/src/messages/severity.dart';
@@ -86,15 +84,14 @@
Stopwatch stopwatch = new Stopwatch()..start();
- await CompilerContext.runWithOptions<List<Uri>>(options,
- (CompilerContext c) async {
+ await CompilerContext.runWithOptions(options, (CompilerContext c) async {
UriTranslator uriTranslator = await c.options.getUriTranslator();
DillTarget dillTarget =
new DillTarget(ticker, uriTranslator, c.options.target);
KernelTarget kernelTarget =
new KernelTargetTest(c.fileSystem, false, dillTarget, uriTranslator);
- Uri platform = c.options.sdkSummary;
+ Uri? platform = c.options.sdkSummary;
if (platform != null) {
var bytes = new File.fromUri(platform).readAsBytesSync();
var platformComponent = loadComponentFromBytes(bytes);
@@ -103,10 +100,9 @@
}
kernelTarget.setEntryPoints(c.options.inputs);
- await dillTarget.buildOutlines();
+ dillTarget.buildOutlines();
await kernelTarget.buildOutlines();
await kernelTarget.buildComponent();
- return null;
});
print("Done in ${stopwatch.elapsedMilliseconds} ms. "
@@ -138,7 +134,7 @@
@override
BodyBuilder createBodyBuilderForOutlineExpression(
SourceLibraryBuilder library,
- DeclarationBuilder declarationBuilder,
+ DeclarationBuilder? declarationBuilder,
ModifierBuilder member,
Scope scope,
Uri fileUri) {
@@ -162,10 +158,10 @@
BodyBuilder createListenerInternal(
ModifierBuilder builder,
Scope memberScope,
- Scope formalParameterScope,
+ Scope? formalParameterScope,
bool isDeclarationInstanceMember,
- VariableDeclaration extensionThis,
- List<TypeParameter> extensionTypeParameters,
+ VariableDeclaration? extensionThis,
+ List<TypeParameter>? extensionTypeParameters,
TypeInferrer typeInferrer,
ConstantContext constantContext) {
return new BodyBuilderTest(
@@ -221,7 +217,7 @@
@override
BodyBuilderTest.forOutlineExpression(
SourceLibraryBuilder library,
- DeclarationBuilder declarationBuilder,
+ DeclarationBuilder? declarationBuilder,
ModifierBuilder member,
Scope scope,
Uri fileUri)
@@ -230,18 +226,18 @@
@override
Expression buildConstructorInvocation(
- TypeDeclarationBuilder type,
+ TypeDeclarationBuilder? type,
Token nameToken,
Token nameLastToken,
- Arguments arguments,
+ Arguments? arguments,
String name,
- List<TypeBuilder> typeArguments,
+ List<TypeBuilder>? typeArguments,
int charOffset,
Constness constness,
{bool isTypeArgumentsInForest = false,
- TypeDeclarationBuilder typeAliasBuilder,
- UnresolvedKind unresolvedKind}) {
- Token maybeNewOrConst = nameToken.previous;
+ TypeDeclarationBuilder? typeAliasBuilder,
+ required UnresolvedKind unresolvedKind}) {
+ Token maybeNewOrConst = nameToken.previous!;
bool doReport = true;
if (maybeNewOrConst is KeywordToken) {
if (maybeNewOrConst.lexeme == "new" ||
diff --git a/pkg/front_end/test/ffi_test.dart b/pkg/front_end/test/ffi_test.dart
index 02cc7fbf..79f7ba7 100644
--- a/pkg/front_end/test/ffi_test.dart
+++ b/pkg/front_end/test/ffi_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:expect/expect.dart';
void main() {
diff --git a/pkg/front_end/test/flutter_gallery_leak_tester.dart b/pkg/front_end/test/flutter_gallery_leak_tester.dart
index 973961d..84fe990 100644
--- a/pkg/front_end/test/flutter_gallery_leak_tester.dart
+++ b/pkg/front_end/test/flutter_gallery_leak_tester.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:async';
import 'dart:convert';
import 'dart:io';
@@ -11,7 +9,7 @@
import "vm_service_heap_helper.dart" as helper;
-Completer completer;
+late Completer completer;
Set<String> files = {};
@@ -36,7 +34,7 @@
bool quicker = false;
bool alternativeInvalidation = false;
- String rootPath;
+ String? rootPath;
for (String arg in args) {
if (arg == "--quicker") {
diff --git a/pkg/front_end/test/generated_files_up_to_date_git_test.dart b/pkg/front_end/test/generated_files_up_to_date_git_test.dart
index 5161eef..78cdefb 100644
--- a/pkg/front_end/test/generated_files_up_to_date_git_test.dart
+++ b/pkg/front_end/test/generated_files_up_to_date_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.md file.
-// @dart = 2.9
-
import "dart:io" show File, exitCode;
import "../tool/_fasta/generate_messages.dart" as generateMessages;
diff --git a/pkg/front_end/test/hot_reload_e2e_test.dart b/pkg/front_end/test/hot_reload_e2e_test.dart
index 9a2a0b9..105abbc 100644
--- a/pkg/front_end/test/hot_reload_e2e_test.dart
+++ b/pkg/front_end/test/hot_reload_e2e_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
-
/// Integration test that runs the incremental compiler, runs the compiled
/// program, incrementally rebuild portions of the app, and triggers a hot
/// reload on the running program.
@@ -45,12 +43,12 @@
import 'tool/reload.dart' show RemoteVm;
abstract class TestCase {
- IncrementalKernelGenerator compiler;
- MemoryFileSystem fs;
- Directory outDir;
- Uri outputUri;
- List<Future<String>> lines;
- Future programIsDone;
+ late IncrementalKernelGenerator compiler;
+ late MemoryFileSystem fs;
+ late Directory outDir;
+ late Uri outputUri;
+ List<Future<String>> lines = const [];
+ late Future programIsDone;
String get name;
@@ -83,14 +81,14 @@
Future<void> tearDown() async {
outDir.deleteSync(recursive: true);
- lines = null;
+ lines = const [];
}
Future<int> computeVmPort() async {
var portLine = await lines[0];
Expect.isTrue(observatoryPortRegExp.hasMatch(portLine));
var match = observatoryPortRegExp.firstMatch(portLine);
- return int.parse(match.group(1));
+ return int.parse(match!.group(1)!);
}
/// Request vm to resume execution
@@ -319,7 +317,7 @@
compiler.invalidate(Uri.parse("org-dartlang-test:///b.dart"));
compiler.invalidate(Uri.parse("org-dartlang-test:///c.dart"));
var component = await compiler.computeDelta();
- if (component != null && !component.libraries.isEmpty) {
+ if (!component.libraries.isEmpty) {
await writeProgram(component, outputUri);
return true;
}
diff --git a/pkg/front_end/test/incremental_bulk_compiler_full.dart b/pkg/front_end/test/incremental_bulk_compiler_full.dart
index 61b3899..ce9a726 100644
--- a/pkg/front_end/test/incremental_bulk_compiler_full.dart
+++ b/pkg/front_end/test/incremental_bulk_compiler_full.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:expect/expect.dart' show Expect;
import 'package:front_end/src/api_prototype/compiler_options.dart'
@@ -15,9 +13,6 @@
import 'package:front_end/src/compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
-import 'package:front_end/src/fasta/incremental_compiler.dart'
- show IncrementalCompiler;
-
import 'package:kernel/kernel.dart' show Component;
import 'package:kernel/text/ast_to_text.dart'
@@ -49,7 +44,7 @@
return result;
}
- IncrementalCompiler compiler;
+ IncrementalKernelGenerator? compiler;
}
CompilerOptions getOptions() {
@@ -78,9 +73,9 @@
// "One shot compile"
bool oneShotFailed = false;
- List<int> oneShotSerialized;
+ late List<int> oneShotSerialized;
try {
- IncrementalCompiler compiler =
+ IncrementalKernelGenerator compiler =
new IncrementalKernelGenerator(getOptions(), uri);
oneShotSerialized = util.postProcess(await compiler.computeDelta());
} catch (e) {
@@ -89,13 +84,13 @@
// Bulk
bool bulkFailed = false;
- List<int> bulkSerialized;
+ late List<int> bulkSerialized;
try {
globalDebuggingNames = new NameSystem();
if (context.compiler == null) {
context.compiler = new IncrementalKernelGenerator(getOptions(), uri);
}
- Component bulkCompiledComponent = await context.compiler
+ Component bulkCompiledComponent = await context.compiler!
.computeDelta(entryPoints: [uri], fullComponent: true);
bulkSerialized = util.postProcess(bulkCompiledComponent);
} catch (e) {
@@ -105,13 +100,13 @@
// Compile again - the serialized output should be the same.
bool bulk2Failed = false;
- List<int> bulkSerialized2;
+ late List<int> bulkSerialized2;
try {
globalDebuggingNames = new NameSystem();
if (context.compiler == null) {
context.compiler = new IncrementalKernelGenerator(getOptions(), uri);
}
- Component bulkCompiledComponent = await context.compiler
+ Component bulkCompiledComponent = await context.compiler!
.computeDelta(entryPoints: [uri], fullComponent: true);
bulkSerialized2 = util.postProcess(bulkCompiledComponent);
} catch (e) {
diff --git a/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart b/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart
index 03993a7..b60f41c 100644
--- a/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart
+++ b/pkg/front_end/test/incremental_bulk_compiler_smoke_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 file.
-// @dart = 2.9
-
import 'package:testing/testing.dart' show Chain, runMe;
import 'incremental_bulk_compiler_full.dart' show Context;
diff --git a/pkg/front_end/test/incremental_compiler_leak_tester.dart b/pkg/front_end/test/incremental_compiler_leak_tester.dart
index a9f3b81..d05460b 100644
--- a/pkg/front_end/test/incremental_compiler_leak_tester.dart
+++ b/pkg/front_end/test/incremental_compiler_leak_tester.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:async';
import 'dart:io';
@@ -29,12 +27,12 @@
@override
Future<void> run() async {
vmService.VM vm = await serviceClient.getVM();
- if (vm.isolates.length != 1) {
- throw "Expected 1 isolate, got ${vm.isolates.length}";
+ if (vm.isolates!.length != 1) {
+ throw "Expected 1 isolate, got ${vm.isolates!.length}";
}
- vmService.IsolateRef isolateRef = vm.isolates.single;
- await waitUntilIsolateIsRunnable(isolateRef.id);
- await serviceClient.resume(isolateRef.id);
+ vmService.IsolateRef isolateRef = vm.isolates!.single;
+ await waitUntilIsolateIsRunnable(isolateRef.id!);
+ await serviceClient.resume(isolateRef.id!);
Map<vmService.ClassRef, List<int>> instanceCounts =
new Map<vmService.ClassRef, List<int>>();
@@ -76,12 +74,12 @@
Map<vmService.ClassRef, vmService.Class> classInfo) {
bool foundLeak = false;
for (vmService.ClassRef c in instanceCounts.keys) {
- List<int> listOfInstanceCounts = instanceCounts[c];
+ List<int> listOfInstanceCounts = instanceCounts[c]!;
// Ignore VM internal stuff like "PatchClass", "PcDescriptors" etc.
// (they don't have a url).
- vmService.Class classDetails = classInfo[c];
- String uriString = classDetails.location?.script?.uri;
+ vmService.Class classDetails = classInfo[c]!;
+ String? uriString = classDetails.location?.script?.uri;
if (uriString == null) continue;
// For now ignore anything not in package:kernel or package:front_end.
@@ -136,30 +134,30 @@
try {
while (true) {
if (shouldBail(iterationNumber)) break;
- if (!await waitUntilPaused(isolateRef.id)) break;
+ if (!await waitUntilPaused(isolateRef.id!)) break;
print("\n\n====================\n\nIteration #$iterationNumber");
iterationNumber++;
vmService.AllocationProfile allocationProfile =
- await forceGC(isolateRef.id);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
+ await forceGC(isolateRef.id!);
+ for (vmService.ClassHeapStats member in allocationProfile.members!) {
if (!classInfo.containsKey(member.classRef)) {
- vmService.Class c = await serviceClient.getObject(
- isolateRef.id, member.classRef.id);
- classInfo[member.classRef] = c;
+ vmService.Class c = (await serviceClient.getObject(
+ isolateRef.id!, member.classRef!.id!)) as vmService.Class;
+ classInfo[member.classRef!] = c;
}
- List<int> listOfInstanceCounts = instanceCounts[member.classRef];
+ List<int>? listOfInstanceCounts = instanceCounts[member.classRef];
if (listOfInstanceCounts == null) {
- listOfInstanceCounts = instanceCounts[member.classRef] = <int>[];
+ listOfInstanceCounts = instanceCounts[member.classRef!] = <int>[];
}
while (listOfInstanceCounts.length < iterationNumber - 2) {
listOfInstanceCounts.add(0);
}
- listOfInstanceCounts.add(member.instancesCurrent);
+ listOfInstanceCounts.add(member.instancesCurrent!);
if (listOfInstanceCounts.length != iterationNumber - 1) {
throw "Unexpected length";
}
}
- await serviceClient.resume(isolateRef.id);
+ await serviceClient.resume(isolateRef.id!);
}
} catch (e) {
print("Got error: $e");
@@ -173,7 +171,7 @@
}
bool ignoredClass(vmService.Class classDetails) {
- String uriString = classDetails.location?.script?.uri;
+ String? uriString = classDetails.location?.script?.uri;
if (uriString == null) return true;
if (uriString.startsWith("package:front_end/")) {
// Classes used for lazy initialization will naturally fluctuate.
@@ -210,7 +208,7 @@
// naturally increase, e.g. we can get 2 more booleans every time (up to
// a maximum of 2 per library or however many would have been there if we
// didn't canonicalize at all).
- if (classDetails.name.endsWith("Constant")) return true;
+ if (classDetails.name!.endsWith("Constant")) return true;
// These classes have proved to fluctuate, although the reason is less
// clear.
diff --git a/pkg/front_end/test/incremental_dart2js_load_from_dill_test.dart b/pkg/front_end/test/incremental_dart2js_load_from_dill_test.dart
index f4b0342..329ca69 100644
--- a/pkg/front_end/test/incremental_dart2js_load_from_dill_test.dart
+++ b/pkg/front_end/test/incremental_dart2js_load_from_dill_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;
import 'package:expect/expect.dart' show Expect;
@@ -20,7 +18,7 @@
import 'incremental_suite.dart'
show checkIsEqual, getOptions, initializedCompile, normalCompile;
-Directory outDir;
+late Directory outDir;
Future<void> main() async {
outDir =
@@ -64,10 +62,10 @@
.readComponent(c);
for (Uri uri in c.uriToSource.keys) {
if (cSdk.uriToSource.containsKey(uri)) {
- if ((c.uriToSource[uri].source?.length ?? 0) != 0) {
+ if (c.uriToSource[uri]!.source.length != 0) {
throw "Compile contained sources for the sdk $uri";
}
- if ((c.uriToSource[uri].lineStarts?.length ?? 0) != 0) {
+ if ((c.uriToSource[uri]!.lineStarts?.length ?? 0) != 0) {
throw "Compile contained line starts for the sdk $uri";
}
}
@@ -80,8 +78,8 @@
[normalDill, true],
[nonexisting, false],
]) {
- Uri initializeWith = initializationData[0];
- bool initializeExpect = initializationData[1];
+ Uri initializeWith = initializationData[0] as Uri;
+ bool initializeExpect = initializationData[1] as bool;
stopwatch.reset();
bool initializeResult = await initializedCompile(
dart2jsUrl, fullDillFromInitialized, initializeWith, [invalidateUri],
@@ -135,16 +133,16 @@
visitor.matchNamedNodes, visitor.checkNodes, 'procedures');
}
- bool _isMixinOrCloneReference(EquivalenceVisitor visitor, Reference a,
- Reference b, String propertyName) {
+ bool _isMixinOrCloneReference(EquivalenceVisitor visitor, Reference? a,
+ Reference? b, String propertyName) {
if (a != null && b != null) {
- ReferenceName thisName = ReferenceName.fromReference(a);
- ReferenceName otherName = ReferenceName.fromReference(b);
+ ReferenceName thisName = ReferenceName.fromReference(a)!;
+ ReferenceName otherName = ReferenceName.fromReference(b)!;
if (thisName.kind == ReferenceNameKind.Member &&
otherName.kind == ReferenceNameKind.Member &&
thisName.memberName == otherName.memberName) {
- String thisClassName = thisName.declarationName;
- String otherClassName = otherName.declarationName;
+ String? thisClassName = thisName.declarationName;
+ String? otherClassName = otherName.declarationName;
if (thisClassName != null &&
otherClassName != null &&
thisClassName.contains('&${otherClassName}')) {
diff --git a/pkg/front_end/test/incremental_dart2js_test.dart b/pkg/front_end/test/incremental_dart2js_test.dart
index 3a36586..d133437 100644
--- a/pkg/front_end/test/incremental_dart2js_test.dart
+++ b/pkg/front_end/test/incremental_dart2js_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 "incremental_dart2js_tester.dart";
Future<void> main(List<String> args) async {
diff --git a/pkg/front_end/test/incremental_dart2js_tester.dart b/pkg/front_end/test/incremental_dart2js_tester.dart
index 3428af3..9ecdb51 100644
--- a/pkg/front_end/test/incremental_dart2js_tester.dart
+++ b/pkg/front_end/test/incremental_dart2js_tester.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:developer";
import 'dart:io' show Platform;
@@ -49,9 +47,9 @@
final int limit;
Stopwatch stopwatch = new Stopwatch();
- List<int> firstCompileData;
- Map<Uri, List<int>> libToData;
- List<Uri> uris;
+ late List<int> firstCompileData;
+ late Map<Uri, List<int>> libToData;
+ late List<Uri> uris;
List<Uri> diffs = <Uri>[];
Set<Uri> componentUris = new Set<Uri>();
@@ -136,7 +134,7 @@
List<int> libSerialized =
serializeComponent(c2, filter: (l) => l == library);
- if (!isEqual(libToData[library.importUri], libSerialized)) {
+ if (!isEqual(libToData[library.importUri]!, libSerialized)) {
print("=====");
print("=====");
print("=====");
@@ -182,7 +180,7 @@
.alternativeInvalidationStrategy] = useExperimentalInvalidation;
helper.TestIncrementalCompiler compiler =
new helper.TestIncrementalCompiler(options, input);
- Component c = await compiler.computeDelta();
+ Component? c = await compiler.computeDelta();
print("Compiled dart2js to Component with ${c.libraries.length} libraries "
"in ${stopwatch.elapsedMilliseconds} ms.");
stopwatch.reset();
@@ -213,8 +211,9 @@
stopwatch.reset();
uris = c.uriToSource.values
- .map((s) => s != null ? s.importUri : null)
- .where((u) => u != null && u.scheme != "dart")
+ .map((s) => s.importUri)
+ .whereType<Uri>()
+ .where((u) => u.scheme != "dart")
.toSet()
.toList();
diff --git a/pkg/front_end/test/incremental_flutter_tester.dart b/pkg/front_end/test/incremental_flutter_tester.dart
index 3663d30..fc01452 100644
--- a/pkg/front_end/test/incremental_flutter_tester.dart
+++ b/pkg/front_end/test/incremental_flutter_tester.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, exit;
import 'package:front_end/src/api_prototype/compiler_options.dart'
@@ -32,7 +30,7 @@
import "incremental_utils.dart" as util;
-void usage(String extraMessage) {
+Never usage(String extraMessage) {
print("""Usage as something like:
out/ReleaseX64/dart pkg/front_end/test/incremental_flutter_tester.dart \
--fast --experimental \
@@ -49,8 +47,8 @@
Future<void> main(List<String> args) async {
bool fast = false;
bool useExperimentalInvalidation = false;
- File inputFile;
- Directory flutterPatchedSdk;
+ File? inputFile;
+ Directory? flutterPatchedSdk;
for (String arg in args) {
if (arg == "--fast") {
fast = true;
@@ -84,12 +82,12 @@
.alternativeInvalidationStrategy] = useExperimentalInvalidation;
helper.TestIncrementalCompiler compiler =
new helper.TestIncrementalCompiler(options, inputFile.uri);
- Component c = await compiler.computeDelta();
+ Component? c = await compiler.computeDelta();
print("Compiled to Component with ${c.libraries.length} "
"libraries in ${stopwatch.elapsedMilliseconds} ms.");
stopwatch.reset();
- List<int> firstCompileData;
- Map<Uri, List<int>> libToData;
+ late List<int> firstCompileData;
+ late Map<Uri, List<int>> libToData;
if (fast) {
libToData = {};
c.libraries.sort((l1, l2) {
@@ -117,8 +115,9 @@
stopwatch.reset();
List<Uri> uris = c.uriToSource.values
- .map((s) => s != null ? s.importUri : null)
- .where((u) => u != null && u.scheme != "dart")
+ .map((s) => s.importUri)
+ .whereType<Uri>()
+ .where((u) => u.scheme != "dart")
.toSet()
.toList();
@@ -179,7 +178,7 @@
List<int> libSerialized =
serializeComponent(c2, filter: (l) => l == library);
- if (!isEqual(libToData[library.importUri], libSerialized)) {
+ if (!isEqual(libToData[library.importUri]!, libSerialized)) {
print("=====");
print("=====");
print("=====");
@@ -285,12 +284,12 @@
class PrinterPrime extends Printer {
PrinterPrime(StringSink sink,
- {NameSystem syntheticNames,
+ {NameSystem? syntheticNames,
bool showOffsets: false,
bool showMetadata: false,
- ImportTable importTable,
- Annotator annotator,
- Map<String, MetadataRepository<Object>> metadata})
+ ImportTable? importTable,
+ Annotator? annotator,
+ Map<String, MetadataRepository<dynamic>>? metadata})
: super(sink,
showOffsets: showOffsets,
showMetadata: showMetadata,
@@ -300,7 +299,7 @@
@override
PrinterPrime createInner(ImportTable importTable,
- Map<String, MetadataRepository<Object>> metadata) {
+ Map<String, MetadataRepository<dynamic>>? metadata) {
return new PrinterPrime(sink,
importTable: importTable,
metadata: metadata,
@@ -311,7 +310,7 @@
}
@override
- void writeInterfaceTarget(Name name, Reference target) {
+ void writeInterfaceTarget(Name name, Reference? target) {
// Skipped!
}
}
diff --git a/pkg/front_end/test/incremental_load_from_invalid_dill_test.dart b/pkg/front_end/test/incremental_load_from_invalid_dill_test.dart
index ac40f6e..3d96399 100644
--- a/pkg/front_end/test/incremental_load_from_invalid_dill_test.dart
+++ b/pkg/front_end/test/incremental_load_from_invalid_dill_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 File;
import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
@@ -57,21 +55,21 @@
}
class Tester {
- Uri sdkRoot;
- Uri base;
- Uri sdkSummary;
- Uri initializeFrom;
- Uri helperFile;
- Uri helper2File;
- Uri entryPoint;
- Uri entryPointImportDartFoo;
- Uri platformUri;
- List<int> sdkSummaryData;
- List<DiagnosticMessage> errorMessages;
- List<DiagnosticMessage> warningMessages;
- MemoryFileSystem fs;
- CompilerOptions options;
- IncrementalCompiler compiler;
+ late Uri sdkRoot;
+ late Uri base;
+ late Uri sdkSummary;
+ late Uri initializeFrom;
+ late Uri helperFile;
+ late Uri helper2File;
+ late Uri entryPoint;
+ late Uri entryPointImportDartFoo;
+ late Uri platformUri;
+ late List<int> sdkSummaryData;
+ late List<DiagnosticMessage> errorMessages;
+ late List<DiagnosticMessage> warningMessages;
+ late MemoryFileSystem fs;
+ late CompilerOptions options;
+ late IncrementalCompiler compiler;
Future<void> compileExpectInitializeFailAndSpecificWarning(
Code expectedWarningCode, bool writeFileOnCrashReport) async {
@@ -248,7 +246,7 @@
List<int> mixedPart1;
{
// Create a component that is compiled without NNBD.
- Map<ExperimentalFlag, bool> prevTesting =
+ Map<ExperimentalFlag, bool>? prevTesting =
options.defaultExperimentFlagsForTesting;
options.defaultExperimentFlagsForTesting = {
ExperimentalFlag.nonNullable: false
@@ -270,7 +268,7 @@
List<int> mixedPart2;
{
// Create a component that is compiled with strong NNBD.
- Map<ExperimentalFlag, bool> prevTesting =
+ Map<ExperimentalFlag, bool>? prevTesting =
options.defaultExperimentFlagsForTesting;
options.defaultExperimentFlagsForTesting = {
ExperimentalFlag.nonNullable: true
@@ -302,7 +300,7 @@
class DeleteTempFilesIncrementalCompiler extends IncrementalCompiler {
DeleteTempFilesIncrementalCompiler(CompilerContext context,
- [Uri initializeFromDillUri])
+ [Uri? initializeFromDillUri])
: super(context, initializeFromDillUri);
@override
diff --git a/pkg/front_end/test/issue_34856_test.dart b/pkg/front_end/test/issue_34856_test.dart
index a82d5c5..a0b5fc1 100644
--- a/pkg/front_end/test/issue_34856_test.dart
+++ b/pkg/front_end/test/issue_34856_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 File;
import 'package:async_helper/async_helper.dart' show asyncTest;
@@ -68,14 +66,14 @@
fs.entityForUri(platformDill).writeAsBytesSync(platformDillBytes);
fs
.entityForUri(base.resolve("lib.dart"))
- .writeAsStringSync(files["lib.dart"]);
+ .writeAsStringSync(files["lib.dart"]!);
CompilerOptions options = new CompilerOptions()
..fileSystem = fs
..sdkSummary = platformDill;
Component component =
(await kernelForModule(<Uri>[base.resolve("lib.dart")], options))
- .component;
+ .component!;
fs = new MemoryFileSystem(base);
fs.entityForUri(platformDill).writeAsBytesSync(platformDillBytes);
@@ -84,7 +82,7 @@
.writeAsBytesSync(serializeComponent(component));
fs
.entityForUri(base.resolve("repro.dart"))
- .writeAsStringSync(files["repro.dart"]);
+ .writeAsStringSync(files["repro.dart"]!);
options = new CompilerOptions()
..fileSystem = fs
@@ -94,12 +92,12 @@
List<Uri> inputs = <Uri>[base.resolve("repro.dart")];
- component = (await kernelForModule(inputs, options)).component;
+ component = (await kernelForModule(inputs, options)).component!;
List<Object> errors = await CompilerContext.runWithOptions(
new ProcessedOptions(options: options, inputs: inputs),
(_) => new Future<List<Object>>.value(
- verifyComponent(component, options.target, skipPlatform: true)));
+ verifyComponent(component, options.target!, skipPlatform: true)));
serializeComponent(component);
diff --git a/pkg/front_end/test/kernel_generator_test.dart b/pkg/front_end/test/kernel_generator_test.dart
index 275df00..4c0bb6d 100644
--- a/pkg/front_end/test/kernel_generator_test.dart
+++ b/pkg/front_end/test/kernel_generator_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:kernel/ast.dart'
show EmptyStatement, Component, ReturnStatement, StaticInvocation;
@@ -45,7 +43,7 @@
..compileSdk = true // To prevent FE from loading an sdk-summary.
..onDiagnostic = errors.add;
- Component component =
+ Component? component =
(await compileScript('main() => print("hi");', options: options))
?.component;
expect(component, isNotNull);
@@ -59,7 +57,7 @@
Uri.parse('org-dartlang-test:///not_existing_summary_file')
..onDiagnostic = errors.add;
- Component component =
+ Component? component =
(await compileScript('main() => print("hi");', options: options))
?.component;
expect(component, isNotNull);
@@ -74,14 +72,14 @@
// sources of the sdk directly.
..librariesSpecificationUri = invalidCoreLibsSpecUri;
Component component =
- (await compileScript('main() => print("hi");', options: options))
- ?.component;
+ (await compileScript('main() => print("hi");', options: options))!
+ .component!;
var core = component.libraries.firstWhere(isDartCoreLibrary);
var printMember = core.members.firstWhere((m) => m.name.text == 'print');
// Note: summaries created by the SDK today contain empty statements as
// method bodies.
- expect(printMember.function.body is! EmptyStatement, isTrue);
+ expect(printMember.function!.body is! EmptyStatement, isTrue);
});
test('compiler requires a main method', () async {
@@ -95,8 +93,8 @@
test('generated program contains source-info', () async {
Component component = (await compileScript(
'a() => print("hi"); main() {}',
- fileName: 'a.dart'))
- ?.component;
+ fileName: 'a.dart'))!
+ .component!;
// Kernel always store an empty '' key in the map, so there is always at
// least one. Having more means that source-info is added.
expect(component.uriToSource.keys.length, greaterThan(1));
@@ -139,13 +137,13 @@
var unitA = await compileUnit(['a.dart'], sources);
// Pretend that the compiled code is a summary
- sources['a.dill'] = serializeComponent(unitA);
+ sources['a.dill'] = serializeComponent(unitA!);
var unitBC = await compileUnit(['b.dart', 'c.dart'], sources,
additionalDills: ['a.dill']);
// Pretend that the compiled code is a summary
- sources['bc.dill'] = serializeComponent(unitBC);
+ sources['bc.dill'] = serializeComponent(unitBC!);
void checkDCallsC(Component component) {
var dLib = findLibrary(component, 'd.dart');
@@ -160,11 +158,11 @@
var unitD1 = await compileUnit(['d.dart'], sources,
additionalDills: ['a.dill', 'bc.dill']);
- checkDCallsC(unitD1);
+ checkDCallsC(unitD1!);
var unitD2 = await compileUnit(['d.dart'], sources,
additionalDills: ['bc.dill', 'a.dill']);
- checkDCallsC(unitD2);
+ checkDCallsC(unitD2!);
});
// TODO(sigmund): add tests with trimming dependencies
diff --git a/pkg/front_end/test/lint_suite.dart b/pkg/front_end/test/lint_suite.dart
index 130a452..18d7e28 100644
--- a/pkg/front_end/test/lint_suite.dart
+++ b/pkg/front_end/test/lint_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 file.
-// @dart = 2.9
-
import 'dart:io' show Directory, File, FileSystemEntity;
import 'dart:typed_data' show Uint8List;
@@ -58,10 +56,10 @@
}
String getErrorMessage(int offset, int squigglyLength, String message) {
- cache.source ??= new Source(cache.lineStarts, cache.rawBytes, uri, uri);
- Location location = cache.source.getLocation(uri, offset);
+ cache.source ??= new Source(cache.lineStarts, cache.rawBytes!, uri, uri);
+ Location location = cache.source!.getLocation(uri, offset);
return command_line_reporting.formatErrorMessage(
- cache.source.getTextLine(location.line),
+ cache.source!.getTextLine(location.line),
location,
squigglyLength,
uri.toString(),
@@ -70,16 +68,16 @@
}
class LintTestCache {
- List<int> rawBytes;
- List<int> lineStarts;
- Source source;
- Token firstToken;
- PackageConfig packages;
+ List<int>? rawBytes;
+ late List<int> lineStarts;
+ Source? source;
+ Token? firstToken;
+ PackageConfig? packages;
}
class Context extends ChainContext {
final bool onlyInGit;
- Context({this.onlyInGit});
+ Context({required this.onlyInGit});
@override
final List<Step> steps = const <Step>[
@@ -95,7 +93,7 @@
@override
Stream<LintTestDescription> list(Chain suite) async* {
- Set<Uri> gitFiles;
+ late Set<Uri> gitFiles;
if (onlyInGit) {
gitFiles = await getGitFiles(suite.uri);
}
@@ -160,9 +158,9 @@
File f = new File.fromUri(description.uri);
description.cache.rawBytes = f.readAsBytesSync();
- Uint8List bytes = new Uint8List(description.cache.rawBytes.length + 1);
+ Uint8List bytes = new Uint8List(description.cache.rawBytes!.length + 1);
bytes.setRange(
- 0, description.cache.rawBytes.length, description.cache.rawBytes);
+ 0, description.cache.rawBytes!.length, description.cache.rawBytes!);
Utf8BytesScanner scanner =
new Utf8BytesScanner(bytes, includeComments: true);
@@ -195,7 +193,7 @@
Parser parser = new Parser(description.listener,
useImplicitCreationExpression: useImplicitCreationExpressionInCfe);
- parser.parseUnit(description.cache.firstToken);
+ parser.parseUnit(description.cache.firstToken!);
if (description.listener.problems.isEmpty) {
return pass(description);
@@ -206,9 +204,9 @@
class LintListener extends Listener {
List<String> problems = <String>[];
- LintTestDescription description;
+ late final LintTestDescription description;
@override
- Uri uri;
+ late final Uri uri;
void onProblem(int offset, int squigglyLength, String message) {
problems.add(description.getErrorMessage(offset, squigglyLength, message));
@@ -220,15 +218,15 @@
@override
void beginVariablesDeclaration(
- Token token, Token lateToken, Token varFinalOrConst) {
+ Token token, Token? lateToken, Token? varFinalOrConst) {
if (!_latestTypes.last.type) {
onProblem(
- varFinalOrConst.offset, varFinalOrConst.length, "No explicit type.");
+ varFinalOrConst!.offset, varFinalOrConst.length, "No explicit type.");
}
}
@override
- void handleType(Token beginToken, Token questionMark) {
+ void handleType(Token beginToken, Token? questionMark) {
_latestTypes.add(new LatestType(beginToken, true));
}
@@ -238,17 +236,17 @@
}
@override
- void endFunctionType(Token functionToken, Token questionMark) {
+ void endFunctionType(Token functionToken, Token? questionMark) {
_latestTypes.add(new LatestType(functionToken, true));
}
@override
void endTopLevelFields(
- Token externalToken,
- Token staticToken,
- Token covariantToken,
- Token lateToken,
- Token varFinalOrConst,
+ Token? externalToken,
+ Token? staticToken,
+ Token? covariantToken,
+ Token? lateToken,
+ Token? varFinalOrConst,
int count,
Token beginToken,
Token endToken) {
@@ -260,29 +258,29 @@
@override
void endClassFields(
- Token abstractToken,
- Token externalToken,
- Token staticToken,
- Token covariantToken,
- Token lateToken,
- Token varFinalOrConst,
+ Token? abstractToken,
+ Token? externalToken,
+ Token? staticToken,
+ Token? covariantToken,
+ Token? lateToken,
+ Token? varFinalOrConst,
int count,
Token beginToken,
Token endToken) {
if (!_latestTypes.last.type) {
onProblem(
- varFinalOrConst.offset, varFinalOrConst.length, "No explicit type.");
+ varFinalOrConst!.offset, varFinalOrConst.length, "No explicit type.");
}
_latestTypes.removeLast();
}
@override
void endFormalParameter(
- Token thisKeyword,
- Token periodAfterThis,
+ Token? thisKeyword,
+ Token? periodAfterThis,
Token nameToken,
- Token initializerStart,
- Token initializerEnd,
+ Token? initializerStart,
+ Token? initializerEnd,
FormalParameterKind kind,
MemberKind memberKind) {
_latestTypes.removeLast();
@@ -300,8 +298,8 @@
Set<Uri> seenImports = new Set<Uri>();
@override
- void endImport(Token importKeyword, Token semicolon) {
- Token importUriToken = importKeyword.next;
+ void endImport(Token importKeyword, Token? semicolon) {
+ Token importUriToken = importKeyword.next!;
String importUri = importUriToken.lexeme;
if (importUri.startsWith("r")) {
importUri = importUri.substring(2, importUri.length - 1);
@@ -311,7 +309,7 @@
Uri resolved = uri.resolve(importUri);
if (resolved.scheme == "package") {
if (description.cache.packages != null) {
- resolved = description.cache.packages.resolve(resolved);
+ resolved = description.cache.packages!.resolve(resolved)!;
}
}
if (!seenImports.add(resolved)) {
@@ -324,7 +322,7 @@
class ExportsLintListener extends LintListener {
@override
void endExport(Token exportKeyword, Token semicolon) {
- Token exportUriToken = exportKeyword.next;
+ Token exportUriToken = exportKeyword.next!;
String exportUri = exportUriToken.lexeme;
if (exportUri.startsWith("r")) {
exportUri = exportUri.substring(2, exportUri.length - 1);
@@ -334,7 +332,7 @@
Uri resolved = uri.resolve(exportUri);
if (resolved.scheme == "package") {
if (description.cache.packages != null) {
- resolved = description.cache.packages.resolve(resolved);
+ resolved = description.cache.packages!.resolve(resolved)!;
}
}
onProblem(exportUriToken.offset, exportUriToken.lexeme.length,
diff --git a/pkg/front_end/test/member_covariance_test.dart b/pkg/front_end/test/member_covariance_test.dart
index 93ff6e4..a9af238 100644
--- a/pkg/front_end/test/member_covariance_test.dart
+++ b/pkg/front_end/test/member_covariance_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:expect/expect.dart';
import 'package:kernel/ast.dart';
import 'package:front_end/src/fasta/kernel/member_covariance.dart';
diff --git a/pkg/front_end/test/memory_file_system_test.dart b/pkg/front_end/test/memory_file_system_test.dart
index 5c20d45..eb635c7 100644
--- a/pkg/front_end/test/memory_file_system_test.dart
+++ b/pkg/front_end/test/memory_file_system_test.dart
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=--supermixin
-// @dart = 2.9
-
library front_end.test.memory_file_system_test;
import 'dart:convert';
@@ -32,8 +30,8 @@
@reflectiveTest
class FileTest extends _BaseTestNative {
- String path;
- MemoryFileSystemEntity file;
+ late String path;
+ late MemoryFileSystemEntity file;
void setUp() {
_baseSetUp();
@@ -180,7 +178,7 @@
}
abstract class MemoryFileSystemTestMixin implements _BaseTest {
- Uri tempUri;
+ late Uri tempUri;
void setUp() {
_baseSetUp();
@@ -287,13 +285,15 @@
class _BaseTestNative extends _BaseTest {
@override
final pathos.Context context = pathos.context;
- @override
- MemoryFileSystem fileSystem;
- @override
- String tempPath;
@override
- String join(String path1, String path2, [String path3, String path4]) =>
+ late MemoryFileSystem fileSystem;
+
+ @override
+ late String tempPath;
+
+ @override
+ String join(String path1, String path2, [String? path3, String? path4]) =>
pathos.join(path1, path2, path3, path4);
@override
@@ -306,13 +306,15 @@
class _BaseTestPosix extends _BaseTest {
@override
final pathos.Context context = pathos.posix;
- @override
- MemoryFileSystem fileSystem;
- @override
- String tempPath;
@override
- String join(String path1, String path2, [String path3, String path4]) =>
+ late MemoryFileSystem fileSystem;
+
+ @override
+ late String tempPath;
+
+ @override
+ String join(String path1, String path2, [String? path3, String? path4]) =>
pathos.posix.join(path1, path2, path3, path4);
@override
@@ -325,13 +327,15 @@
class _BaseTestWindows extends _BaseTest {
@override
final pathos.Context context = pathos.windows;
- @override
- MemoryFileSystem fileSystem;
- @override
- String tempPath;
@override
- String join(String path1, String path2, [String path3, String path4]) =>
+ late MemoryFileSystem fileSystem;
+
+ @override
+ late String tempPath;
+
+ @override
+ String join(String path1, String path2, [String? path3, String? path4]) =>
pathos.windows.join(path1, path2, path3, path4);
@override
diff --git a/pkg/front_end/test/messages_json_test.dart b/pkg/front_end/test/messages_json_test.dart
index 8df7698..b86e4b9 100644
--- a/pkg/front_end/test/messages_json_test.dart
+++ b/pkg/front_end/test/messages_json_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/messages/diagnostic_message.dart'
show DiagnosticMessage, getMessageUri;
@@ -24,17 +22,17 @@
Severity severity = Severity.values[i];
Code code = new Code("MyCodeName");
Message message = new Message(code, problemMessage: '');
- LocatedMessage locatedMessage1 =
+ LocatedMessage locatedMessage =
new LocatedMessage(Uri.parse("what:ever/fun_1.dart"), 117, 2, message);
FormattedMessage formattedMessage2 = new FormattedMessage(
- null,
+ locatedMessage,
"Formatted string Plain #2",
"Formatted string Colorized #2",
13,
2,
Severity.error, []);
FormattedMessage formattedMessage3 = new FormattedMessage(
- null,
+ locatedMessage,
"Formatted string Plain #3",
"Formatted string Colorized #3",
313,
@@ -42,7 +40,7 @@
Severity.error, []);
FormattedMessage formattedMessage1 = new FormattedMessage(
- locatedMessage1,
+ locatedMessage,
"Formatted string Plain",
"Formatted string Colorized",
42,
@@ -90,20 +88,20 @@
expect(a.severity, b.severity);
expect(getMessageUri(a), getMessageUri(b));
- List<Uri> uriList1 = a.involvedFiles?.toList();
- List<Uri> uriList2 = b.involvedFiles?.toList();
+ List<Uri>? uriList1 = a.involvedFiles?.toList();
+ List<Uri>? uriList2 = b.involvedFiles?.toList();
expect(uriList1?.length, uriList2?.length);
if (uriList1 != null) {
for (int i = 0; i < uriList1.length; i++) {
- expect(uriList1[i], uriList2[i]);
+ expect(uriList1[i], uriList2![i]);
}
}
- String string1 = a.codeName;
- String string2 = b.codeName;
+ String? string1 = a.codeName;
+ String? string2 = b.codeName;
expect(string1, string2);
}
-void expect(Object actual, Object expect) {
+void expect(Object? actual, Object? expect) {
if (expect != actual) throw "Expected $expect got $actual";
}
diff --git a/pkg/front_end/test/mixin_export_test.dart b/pkg/front_end/test/mixin_export_test.dart
index ff1f579..5fafac7 100644
--- a/pkg/front_end/test/mixin_export_test.dart
+++ b/pkg/front_end/test/mixin_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 file.
-// @dart = 2.9
-
import 'package:async_helper/async_helper.dart' show asyncTest;
import 'package:front_end/src/testing/compiler_common.dart';
diff --git a/pkg/front_end/test/multiple_simultaneous_compiles_test.dart b/pkg/front_end/test/multiple_simultaneous_compiles_test.dart
index df42515..f2ea8f96 100644
--- a/pkg/front_end/test/multiple_simultaneous_compiles_test.dart
+++ b/pkg/front_end/test/multiple_simultaneous_compiles_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 File, Platform;
import 'package:front_end/src/base/processed_options.dart'
@@ -27,7 +25,7 @@
List<Future> futures = [];
List<int> compilesLeft = new List<int>.filled(5, 8);
for (int i = 0; i < compilesLeft.length; i++) {
- Future<Component> compileAgain() async {
+ Future<Component?> compileAgain() async {
print("$i has ${compilesLeft[i]} left.");
if (compilesLeft[i] > 0) {
compilesLeft[i]--;
@@ -56,13 +54,13 @@
"(with the same compiler) (without crashing)");
}
-List<IncrementalCompiler> compilers = [];
+List<IncrementalCompiler?> compilers = [];
Future<Component> compile(int compilerNum, Uri uri) async {
if (compilers.length <= compilerNum) {
compilers.length = compilerNum + 1;
}
- IncrementalCompiler compiler = compilers[compilerNum];
+ IncrementalCompiler? compiler = compilers[compilerNum];
if (compiler == null) {
var options = getOptions();
compiler = new IncrementalCompiler(new CompilerContext(
diff --git a/pkg/front_end/test/packages_format_error_test.dart b/pkg/front_end/test/packages_format_error_test.dart
index a9fa5f3..a19551f 100644
--- a/pkg/front_end/test/packages_format_error_test.dart
+++ b/pkg/front_end/test/packages_format_error_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/messages/diagnostic_message.dart'
show DiagnosticMessage, getMessageCodeObject;
diff --git a/pkg/front_end/test/parser_all_suite.dart b/pkg/front_end/test/parser_all_suite.dart
index 9aab3a5..bef7873 100644
--- a/pkg/front_end/test/parser_all_suite.dart
+++ b/pkg/front_end/test/parser_all_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 file.
-// @dart = 2.9
-
import 'package:testing/testing.dart' show Chain, ChainContext, runMe;
import 'parser_suite.dart';
diff --git a/pkg/front_end/test/parser_test_listener_creator.dart b/pkg/front_end/test/parser_test_listener_creator.dart
index c2c6df4..3ea90c1 100644
--- a/pkg/front_end/test/parser_test_listener_creator.dart
+++ b/pkg/front_end/test/parser_test_listener_creator.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 File;
import 'dart:typed_data' show Uint8List;
@@ -120,15 +118,15 @@
class ParserCreatorListener extends Listener {
final StringSink out;
bool insideListenerClass = false;
- String currentMethodName;
- String latestSeenParameterTypeToken;
- List<String> parameters = <String>[];
- List<String> parameterTypes = <String>[];
+ String? currentMethodName;
+ String? latestSeenParameterTypeToken;
+ List<String> parameters = [];
+ List<String?> parameterTypes = [];
ParserCreatorListener(this.out);
@override
- void beginClassDeclaration(Token begin, Token abstractToken, Token name) {
+ void beginClassDeclaration(Token begin, Token? abstractToken, Token name) {
if (name.lexeme == "Listener") insideListenerClass = true;
}
@@ -140,23 +138,23 @@
@override
void beginMethod(
DeclarationKind declarationKind,
- Token externalToken,
- Token staticToken,
- Token covariantToken,
- Token varFinalOrConst,
- Token getOrSet,
+ Token? externalToken,
+ Token? staticToken,
+ Token? covariantToken,
+ Token? varFinalOrConst,
+ Token? getOrSet,
Token name) {
currentMethodName = name.lexeme;
}
@override
- void endClassMethod(Token getOrSet, Token beginToken, Token beginParam,
- Token beginInitializers, Token endToken) {
+ void endClassMethod(Token? getOrSet, Token beginToken, Token beginParam,
+ Token? beginInitializers, Token endToken) {
if (insideListenerClass) {
out.writeln(" @override");
out.write(" ");
Token token = beginToken;
- Token latestToken;
+ Token? latestToken;
while (true) {
if (latestToken != null && latestToken.charEnd < token.charOffset) {
out.write(" ");
@@ -171,14 +169,14 @@
throw token.runtimeType;
}
latestToken = token;
- token = token.next;
+ token = token.next!;
}
if (token is SimpleToken && token.type == TokenType.FUNCTION) {
out.write(" null;");
} else {
out.write("\n ");
- if (currentMethodName.startsWith("end")) {
+ if (currentMethodName!.startsWith("end")) {
out.write("indent--;\n ");
}
for (int i = 0; i < parameterTypes.length; i++) {
@@ -196,7 +194,7 @@
}
out.write(")');\n ");
- if (currentMethodName.startsWith("begin")) {
+ if (currentMethodName!.startsWith("begin")) {
out.write(" indent++;\n ");
}
@@ -224,17 +222,17 @@
}
@override
- void handleType(Token beginToken, Token questionMark) {
+ void handleType(Token beginToken, Token? questionMark) {
latestSeenParameterTypeToken = beginToken.lexeme;
}
@override
void endFormalParameter(
- Token thisKeyword,
- Token periodAfterThis,
+ Token? thisKeyword,
+ Token? periodAfterThis,
Token nameToken,
- Token initializerStart,
- Token initializerEnd,
+ Token? initializerStart,
+ Token? initializerEnd,
FormalParameterKind kind,
MemberKind memberKind) {
parameters.add(nameToken.lexeme);
diff --git a/pkg/front_end/test/parser_test_parser_creator.dart b/pkg/front_end/test/parser_test_parser_creator.dart
index 136e903..a23ad8a 100644
--- a/pkg/front_end/test/parser_test_parser_creator.dart
+++ b/pkg/front_end/test/parser_test_parser_creator.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';
import 'dart:typed_data';
@@ -106,14 +104,14 @@
class ParserCreatorListener extends Listener {
final StringSink out;
bool insideParserClass = false;
- String currentMethodName;
- List<String> parameters = <String>[];
- List<String> parametersNamed = <String>[];
+ String? currentMethodName;
+ List<String> parameters = [];
+ List<String?> parametersNamed = [];
ParserCreatorListener(this.out);
@override
- void beginClassDeclaration(Token begin, Token abstractToken, Token name) {
+ void beginClassDeclaration(Token begin, Token? abstractToken, Token name) {
if (name.lexeme == "Parser") insideParserClass = true;
}
@@ -125,29 +123,29 @@
@override
void beginMethod(
DeclarationKind declarationKind,
- Token externalToken,
- Token staticToken,
- Token covariantToken,
- Token varFinalOrConst,
- Token getOrSet,
+ Token? externalToken,
+ Token? staticToken,
+ Token? covariantToken,
+ Token? varFinalOrConst,
+ Token? getOrSet,
Token name) {
currentMethodName = name.lexeme;
}
@override
- void endClassConstructor(Token getOrSet, Token beginToken, Token beginParam,
- Token beginInitializers, Token endToken) {
+ void endClassConstructor(Token? getOrSet, Token beginToken, Token beginParam,
+ Token? beginInitializers, Token endToken) {
parameters.clear();
parametersNamed.clear();
currentMethodName = null;
}
@override
- void endClassMethod(Token getOrSet, Token beginToken, Token beginParam,
- Token beginInitializers, Token endToken) {
- if (insideParserClass && !currentMethodName.startsWith("_")) {
+ void endClassMethod(Token? getOrSet, Token beginToken, Token beginParam,
+ Token? beginInitializers, Token endToken) {
+ if (insideParserClass && !currentMethodName!.startsWith("_")) {
Token token = beginToken;
- Token latestToken;
+ Token? latestToken;
out.writeln(" @override");
out.write(" ");
while (true) {
@@ -156,7 +154,7 @@
out.write(" ");
}
out.write("dynamic");
- token = troubleParameterTokens[token];
+ token = troubleParameterTokens[token]!;
}
if (latestToken != null && latestToken.charEnd < token.charOffset) {
out.write(" ");
@@ -169,16 +167,15 @@
out.write(token.lexeme);
if (token is BeginToken &&
token.type == TokenType.OPEN_CURLY_BRACKET &&
- (beginParam == null ||
- beginParam.endGroup == endToken ||
- token.charOffset > beginParam.endGroup.charOffset)) {
+ (beginParam.endGroup == endToken ||
+ token.charOffset > beginParam.endGroup!.charOffset)) {
break;
}
if (token == endToken) {
throw token.runtimeType;
}
latestToken = token;
- token = token.next;
+ token = token.next!;
}
out.write("\n ");
@@ -244,17 +241,17 @@
formalParametersNestLevel--;
}
- Token currentFormalParameterToken;
+ Token? currentFormalParameterToken;
@override
- void beginFormalParameter(Token token, MemberKind kind, Token requiredToken,
- Token covariantToken, Token varFinalOrConst) {
+ void beginFormalParameter(Token token, MemberKind kind, Token? requiredToken,
+ Token? covariantToken, Token? varFinalOrConst) {
if (formalParametersNestLevel == 1) {
currentFormalParameterToken = token;
}
}
- Map<Token, Token> troubleParameterTokens = {};
+ Map<Token?, Token?> troubleParameterTokens = {};
@override
void handleIdentifier(Token token, IdentifierContext context) {
@@ -265,11 +262,11 @@
@override
void endFormalParameter(
- Token thisKeyword,
- Token periodAfterThis,
+ Token? thisKeyword,
+ Token? periodAfterThis,
Token nameToken,
- Token initializerStart,
- Token initializerEnd,
+ Token? initializerStart,
+ Token? initializerEnd,
FormalParameterKind kind,
MemberKind memberKind) {
if (formalParametersNestLevel != 1) {
diff --git a/pkg/front_end/test/precedence_info_test.dart b/pkg/front_end/test/precedence_info_test.dart
index cce5241..0508cc8 100644
--- a/pkg/front_end/test/precedence_info_test.dart
+++ b/pkg/front_end/test/precedence_info_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/scanner/scanner.dart';
import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
show AbstractScanner;
@@ -22,11 +20,11 @@
@reflectiveTest
class PrecedenceInfoTest {
void assertInfo(check(String source, Token token)) {
- void assertLexeme(String source) {
+ void assertLexeme(String? source) {
if (source == null || source.isEmpty) return;
var token = scanString(source, includeComments: true).tokens;
while (token is ErrorToken) {
- token = token.next;
+ token = token.next!;
}
check(source, token);
}
@@ -290,11 +288,11 @@
}
void test_name() {
- void assertName(String source, String name, {int offset: 0}) {
+ void assertName(String? source, String name, {int offset: 0}) {
if (source == null || source.isEmpty) return;
var token = scanString(source, includeComments: true).tokens;
while (token is ErrorToken || token.offset < offset) {
- token = token.next;
+ token = token.next!;
}
expect(token.type.name, name,
reason: 'source: $source\ntoken: ${token.lexeme}');
@@ -393,7 +391,7 @@
for (String source in lexemes) {
var token = scanString(source, includeComments: true).tokens;
while (token is ErrorToken) {
- token = token.next;
+ token = token.next!;
}
expect(token.type.precedence, precedence, reason: source);
}
diff --git a/pkg/front_end/test/read_dill_from_binary_md_git_test.dart b/pkg/front_end/test/read_dill_from_binary_md_git_test.dart
index ec6277b..30a5ebd 100644
--- a/pkg/front_end/test/read_dill_from_binary_md_git_test.dart
+++ b/pkg/front_end/test/read_dill_from_binary_md_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 File, Platform;
import 'package:kernel/target/targets.dart' show NoneTarget, TargetFlags;
diff --git a/pkg/front_end/test/relativize_test.dart b/pkg/front_end/test/relativize_test.dart
index c42fac6..ce5e36b 100644
--- a/pkg/front_end/test/relativize_test.dart
+++ b/pkg/front_end/test/relativize_test.dart
@@ -2,14 +2,12 @@
// 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/relativize.dart';
import 'package:test/test.dart';
void main() {
test('test relativeUri', () {
- void c(String expected, String base, String path, bool isWindows) {
+ void c(String expected, String base, String path, bool? isWindows) {
if (isWindows == null) {
c(expected, base, path, true);
c(expected, base, path, false);
diff --git a/pkg/front_end/test/scanner_replacement_test.dart b/pkg/front_end/test/scanner_replacement_test.dart
index dff9d26..f936db9 100644
--- a/pkg/front_end/test/scanner_replacement_test.dart
+++ b/pkg/front_end/test/scanner_replacement_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/scanner/scanner.dart' as fasta;
import 'package:_fe_analyzer_shared/src/scanner/error_token.dart' as fasta;
import 'package:_fe_analyzer_shared/src/scanner/token.dart' as analyzer;
@@ -30,7 +28,7 @@
class ScannerTest_Replacement extends ScannerTestBase {
@override
analyzer.Token scanWithListener(String source, ErrorListener listener,
- {fasta.ScannerConfiguration configuration}) {
+ {fasta.ScannerConfiguration? configuration}) {
// Process the source similar to
// pkg/analyzer/lib/src/dart/scanner/scanner.dart
// to simulate replacing the analyzer scanner
@@ -49,9 +47,9 @@
}
void _assertOpenClosePair(String source) {
- analyzer.BeginToken open = _scan(source);
- fasta.Token close = open.next;
- expect(close.next.isEof, isTrue);
+ analyzer.BeginToken open = _scan(source) as analyzer.BeginToken;
+ fasta.Token close = open.next!;
+ expect(close.next!.isEof, isTrue);
expect(open.endGroup, close);
expect(open.isSynthetic, isFalse);
expect(close.isSynthetic, isFalse);
@@ -59,9 +57,10 @@
void _assertOpenOnly(String source, String expectedCloser) {
ErrorListener listener = new ErrorListener();
- analyzer.BeginToken open = scanWithListener(source, listener);
- fasta.Token close = open.next;
- expect(close.next.isEof, isTrue);
+ analyzer.BeginToken open =
+ scanWithListener(source, listener) as analyzer.BeginToken;
+ fasta.Token close = open.next!;
+ expect(close.next!.isEof, isTrue);
expect(open.endGroup, close);
expect(open.isSynthetic, isFalse);
expect(close.isSynthetic, isTrue);
@@ -82,7 +81,7 @@
// ... but the length does *not* include the additional character
// so as to be true to the original source.
expect(token.length, source.length);
- expect(token.next.isEof, isTrue);
+ expect(token.next!.isEof, isTrue);
expect(listener.errors, hasLength(1));
TestError error = listener.errors[0];
expect(error.errorCode, ScannerErrorCode.MISSING_DIGIT);
@@ -93,8 +92,8 @@
void test_lt() {
// fasta does not automatically insert a closer for '<'
// because it could be part of an expression rather than an opener
- analyzer.BeginToken lt = _scan('<');
- expect(lt.next.isEof, isTrue);
+ analyzer.BeginToken lt = _scan('<') as analyzer.BeginToken;
+ expect(lt.next!.isEof, isTrue);
expect(lt.isSynthetic, isFalse);
}
@@ -138,16 +137,18 @@
// where both ')' are synthetic
ErrorListener listener = new ErrorListener();
var stringStart = scanWithListener(r'"${({(}}"', listener);
- analyzer.BeginToken interpolationStart = stringStart.next;
- analyzer.BeginToken openParen1 = interpolationStart.next;
- analyzer.BeginToken openBrace = openParen1.next;
- analyzer.BeginToken openParen2 = openBrace.next;
- var closeParen2 = openParen2.next;
- var closeBrace = closeParen2.next;
- var closeParen1 = closeBrace.next;
- var interpolationEnd = closeParen1.next;
- var stringEnd = interpolationEnd.next;
- var eof = stringEnd.next;
+ analyzer.BeginToken interpolationStart =
+ stringStart.next as analyzer.BeginToken;
+ analyzer.BeginToken openParen1 =
+ interpolationStart.next as analyzer.BeginToken;
+ analyzer.BeginToken openBrace = openParen1.next as analyzer.BeginToken;
+ analyzer.BeginToken openParen2 = openBrace.next as analyzer.BeginToken;
+ var closeParen2 = openParen2.next!;
+ var closeBrace = closeParen2.next!;
+ var closeParen1 = closeBrace.next!;
+ var interpolationEnd = closeParen1.next!;
+ var stringEnd = interpolationEnd.next!;
+ var eof = stringEnd.next!;
expect(interpolationStart.endToken, same(interpolationEnd));
expect(interpolationEnd.isSynthetic, isFalse);
@@ -168,14 +169,15 @@
void test_unmatched_openers() {
ErrorListener listener = new ErrorListener();
// fasta inserts missing closers except for '<'
- analyzer.BeginToken openBrace = scanWithListener('{[(<', listener);
- analyzer.BeginToken openBracket = openBrace.next;
- analyzer.BeginToken openParen = openBracket.next;
- analyzer.BeginToken openLT = openParen.next;
- var closeParen = openLT.next;
- var closeBracket = closeParen.next;
- var closeBrace = closeBracket.next;
- var eof = closeBrace.next;
+ analyzer.BeginToken openBrace =
+ scanWithListener('{[(<', listener) as analyzer.BeginToken;
+ analyzer.BeginToken openBracket = openBrace.next as analyzer.BeginToken;
+ analyzer.BeginToken openParen = openBracket.next as analyzer.BeginToken;
+ analyzer.BeginToken openLT = openParen.next as analyzer.BeginToken;
+ var closeParen = openLT.next!;
+ var closeBracket = closeParen.next!;
+ var closeBrace = closeBracket.next!;
+ var eof = closeBrace.next!;
expect(openBrace.endGroup, same(closeBrace));
expect(openBracket.endGroup, same(closeBracket));
@@ -201,13 +203,13 @@
// The default recovery strategy used by scanString
// places all error tokens at the head of the stream.
while (token.type == analyzer.TokenType.BAD_INPUT) {
- translateErrorToken(token,
- (ScannerErrorCode errorCode, int offset, List<Object> arguments) {
+ translateErrorToken(token as fasta.ErrorToken,
+ (ScannerErrorCode errorCode, int offset, List<Object>? arguments) {
listener.errors.add(new TestError(offset, errorCode, arguments));
});
- token = token.next;
+ token = token.next!;
}
- if (!token.previous.isEof) {
+ if (!token.previous!.isEof) {
new analyzer.Token.eof(-1).setNext(token);
}
return token;
@@ -217,20 +219,20 @@
void assertValidTokenStream(fasta.Token firstToken,
{bool errorsFirst: false}) {
fasta.Token token = firstToken;
- fasta.Token previous = token.previous;
+ fasta.Token previous = token.previous!;
expect(previous.isEof, isTrue, reason: 'Missing leading EOF');
expect(previous.next, token, reason: 'Invalid leading EOF');
expect(previous.previous, previous, reason: 'Invalid leading EOF');
if (errorsFirst) {
while (!token.isEof && token is fasta.ErrorToken) {
- token = token.next;
+ token = token.next!;
}
}
var isNotErrorToken = isNot(const TypeMatcher<fasta.ErrorToken>());
while (!token.isEof) {
if (errorsFirst) expect(token, isNotErrorToken);
previous = token;
- token = token.next;
+ token = token.next!;
expect(token, isNotNull, reason: previous.toString());
expect(token.previous, previous, reason: token.toString());
}
@@ -258,7 +260,7 @@
} else if (token is fasta.UnmatchedToken) {
errorStack.add(token);
}
- token = token.next;
+ token = token.next!;
}
expect(openerStack, isEmpty, reason: 'Missing closers');
expect(errorStack, isEmpty, reason: 'Extra error tokens');
diff --git a/pkg/front_end/test/scanner_test.dart b/pkg/front_end/test/scanner_test.dart
index cbf28bc..08c21e7 100644
--- a/pkg/front_end/test/scanner_test.dart
+++ b/pkg/front_end/test/scanner_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/base/errors.dart';
import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
show AbstractScanner, ScannerConfiguration;
@@ -82,7 +80,7 @@
abstract class ScannerTestBase {
Token scanWithListener(String source, ErrorListener listener,
- {ScannerConfiguration configuration});
+ {ScannerConfiguration? configuration});
void test_ampersand() {
_assertToken(TokenType.AMPERSAND, "&");
@@ -104,9 +102,9 @@
void test_angle_brackets() {
var lessThan = _scan('<String>');
- var identifier = lessThan.next;
- var greaterThan = identifier.next;
- expect(greaterThan.next.type, TokenType.EOF);
+ var identifier = lessThan.next!;
+ var greaterThan = identifier.next!;
+ expect(greaterThan.next!.type, TokenType.EOF);
// Analyzer's token streams don't consider "<" to be an opener
// but fasta does.
if (lessThan is BeginToken) {
@@ -119,8 +117,8 @@
Token token = _scan("async*");
expect(token.type.isKeyword, true);
expect(token.lexeme, 'async');
- expect(token.next.type, TokenType.STAR);
- expect(token.next.next.type, TokenType.EOF);
+ expect(token.next!.type, TokenType.STAR);
+ expect(token.next!.next!.type, TokenType.EOF);
}
void test_at() {
@@ -197,30 +195,30 @@
Token token = _scan("/* x */ /* y */ z");
expect(token.type, TokenType.IDENTIFIER);
expect(token.precedingComments, isNotNull);
- expect(token.precedingComments.value(), "/* x */");
- expect(token.precedingComments.previous, isNull);
- expect(token.precedingComments.next, isNotNull);
- expect(token.precedingComments.next.value(), "/* y */");
+ expect(token.precedingComments!.value(), "/* x */");
+ expect(token.precedingComments!.previous, isNull);
+ expect(token.precedingComments!.next, isNotNull);
+ expect(token.precedingComments!.next!.value(), "/* y */");
expect(
- token.precedingComments.next.previous, same(token.precedingComments));
- expect(token.precedingComments.next.next, isNull);
+ token.precedingComments!.next!.previous, same(token.precedingComments));
+ expect(token.precedingComments!.next!.next, isNull);
}
void test_comment_multi_consecutive_3() {
Token token = _scan("/* x */ /* y */ /* z */ a");
expect(token.type, TokenType.IDENTIFIER);
expect(token.precedingComments, isNotNull);
- expect(token.precedingComments.value(), "/* x */");
- expect(token.precedingComments.previous, isNull);
- expect(token.precedingComments.next, isNotNull);
- expect(token.precedingComments.next.value(), "/* y */");
+ expect(token.precedingComments!.value(), "/* x */");
+ expect(token.precedingComments!.previous, isNull);
+ expect(token.precedingComments!.next, isNotNull);
+ expect(token.precedingComments!.next!.value(), "/* y */");
expect(
- token.precedingComments.next.previous, same(token.precedingComments));
- expect(token.precedingComments.next.next, isNotNull);
- expect(token.precedingComments.next.next.value(), "/* z */");
- expect(token.precedingComments.next.next.previous,
- same(token.precedingComments.next));
- expect(token.precedingComments.next.next.next, isNull);
+ token.precedingComments!.next!.previous, same(token.precedingComments));
+ expect(token.precedingComments!.next!.next, isNotNull);
+ expect(token.precedingComments!.next!.next!.value(), "/* z */");
+ expect(token.precedingComments!.next!.next!.previous,
+ same(token.precedingComments!.next));
+ expect(token.precedingComments!.next!.next!.next, isNull);
}
void test_comment_multi_unterminated() {
@@ -307,9 +305,9 @@
void test_hexadecimal_missingDigit() {
var token = _assertError(ScannerErrorCode.MISSING_HEX_DIGIT, 5, "a = 0x");
expect(token.lexeme, 'a');
- token = token.next;
+ token = token.next!;
expect(token.lexeme, '=');
- token = token.next;
+ token = token.next!;
expect(token.lexeme, '0x0');
}
@@ -334,12 +332,12 @@
]);
var token = tokens;
expect(token.lexeme, 'a');
- token = token.next;
+ token = token.next!;
expect(token.lexeme, '=');
- token = token.next;
+ token = token.next!;
expect(token.type, TokenType.IDENTIFIER);
expect(token.lexeme, "Shche\u0433lov\u0429x");
- token = token.next;
+ token = token.next!;
expect(token.lexeme, ';');
}
@@ -355,12 +353,12 @@
var token = _assertError(
ScannerErrorCode.ILLEGAL_CHARACTER, 4, 'a = \u0429;', [0x429]);
expect(token.lexeme, 'a');
- token = token.next;
+ token = token.next!;
expect(token.lexeme, '=');
- token = token.next;
+ token = token.next!;
expect(token.type, TokenType.IDENTIFIER);
expect(token.lexeme, "\u0429");
- token = token.next;
+ token = token.next!;
expect(token.lexeme, ';');
}
@@ -711,41 +709,41 @@
}
void test_matching_braces() {
- BeginToken openBrace1 = _scan('{1: {2: 3}}');
- var one = openBrace1.next;
- var colon1 = one.next;
- BeginToken openBrace2 = colon1.next;
- var two = openBrace2.next;
- var colon2 = two.next;
- var three = colon2.next;
- var closeBrace1 = three.next;
- var closeBrace2 = closeBrace1.next;
- expect(closeBrace2.next.type, TokenType.EOF);
+ BeginToken openBrace1 = _scan('{1: {2: 3}}') as BeginToken;
+ var one = openBrace1.next!;
+ var colon1 = one.next!;
+ BeginToken openBrace2 = colon1.next as BeginToken;
+ var two = openBrace2.next!;
+ var colon2 = two.next!;
+ var three = colon2.next!;
+ var closeBrace1 = three.next!;
+ var closeBrace2 = closeBrace1.next!;
+ expect(closeBrace2.next!.type, TokenType.EOF);
expect(openBrace1.endToken, same(closeBrace2));
expect(openBrace2.endToken, same(closeBrace1));
}
void test_matching_brackets() {
- BeginToken openBracket1 = _scan('[1, [2]]');
- var one = openBracket1.next;
- var comma = one.next;
- BeginToken openBracket2 = comma.next;
- var two = openBracket2.next;
- var closeBracket1 = two.next;
- var closeBracket2 = closeBracket1.next;
- expect(closeBracket2.next.type, TokenType.EOF);
+ BeginToken openBracket1 = _scan('[1, [2]]') as BeginToken;
+ var one = openBracket1.next!;
+ var comma = one.next!;
+ BeginToken openBracket2 = comma.next as BeginToken;
+ var two = openBracket2.next!;
+ var closeBracket1 = two.next!;
+ var closeBracket2 = closeBracket1.next!;
+ expect(closeBracket2.next!.type, TokenType.EOF);
expect(openBracket1.endToken, same(closeBracket2));
expect(openBracket2.endToken, same(closeBracket1));
}
void test_matching_parens() {
- BeginToken openParen1 = _scan('(f(x))');
- var f = openParen1.next;
- BeginToken openParen2 = f.next;
- var x = openParen2.next;
- var closeParen1 = x.next;
- var closeParen2 = closeParen1.next;
- expect(closeParen2.next.type, TokenType.EOF);
+ BeginToken openParen1 = _scan('(f(x))') as BeginToken;
+ var f = openParen1.next!;
+ BeginToken openParen2 = f.next as BeginToken;
+ var x = openParen2.next!;
+ var closeParen1 = x.next!;
+ var closeParen2 = closeParen1.next!;
+ expect(closeParen2.next!.type, TokenType.EOF);
expect(openParen1.endToken, same(closeParen2));
expect(openParen2.endToken, same(closeParen1));
}
@@ -769,10 +767,10 @@
// In this particular case, fasta cannot find an opener for ']'
// and thus marks ']' as an error and moves on.
ErrorListener listener = new ErrorListener();
- BeginToken openParen = scanWithListener('(])', listener);
- var closeBracket = openParen.next;
- var closeParen = closeBracket.next;
- expect(closeParen.next.type, TokenType.EOF);
+ BeginToken openParen = scanWithListener('(])', listener) as BeginToken;
+ var closeBracket = openParen.next!;
+ var closeParen = closeBracket.next!;
+ expect(closeParen.next!.type, TokenType.EOF);
expect(openParen.endToken, same(closeParen));
listener.assertNoErrors();
}
@@ -792,15 +790,15 @@
// 2 recoveries).
// Both options are "equally bad" and the first choise is made.
ErrorListener listener = new ErrorListener();
- BeginToken openBracket = scanWithListener('[(])', listener);
- BeginToken openParen = openBracket.next;
- var closeParen = openParen.next;
+ BeginToken openBracket = scanWithListener('[(])', listener) as BeginToken;
+ BeginToken openParen = openBracket.next as BeginToken;
+ var closeParen = openParen.next!;
expect(closeParen.isSynthetic, isTrue);
- var closeBracket = closeParen.next;
+ var closeBracket = closeParen.next!;
expect(closeBracket.isSynthetic, isFalse);
- var closeParen2 = closeBracket.next;
+ var closeParen2 = closeBracket.next!;
expect(closeParen2.isSynthetic, isFalse);
- expect(closeParen2.next.type, TokenType.EOF);
+ expect(closeParen2.next!.type, TokenType.EOF);
expect(openBracket.endToken, same(closeBracket));
expect(openParen.endToken, same(closeParen));
listener.assertErrors([
@@ -813,17 +811,17 @@
// closer to be mismatched, which means that `([)` parses as three unmatched
// tokens.
ErrorListener listener = new ErrorListener();
- BeginToken openParen = scanWithListener('([)', listener);
- BeginToken openBracket = openParen.next;
+ BeginToken openParen = scanWithListener('([)', listener) as BeginToken;
+ BeginToken openBracket = openParen.next as BeginToken;
// When openers and closers are mismatched,
// fasta favors considering the opener to be mismatched
// and inserts synthetic closers as needed.
// `([)` is scanned as `([])` where `]` is synthetic.
- var closeBracket = openBracket.next;
+ var closeBracket = openBracket.next!;
expect(closeBracket.isSynthetic, isTrue);
- var closeParen = closeBracket.next;
+ var closeParen = closeBracket.next!;
expect(closeParen.isSynthetic, isFalse);
- expect(closeParen.next.type, TokenType.EOF);
+ expect(closeParen.next!.type, TokenType.EOF);
expect(openBracket.endToken, closeBracket);
expect(openParen.endToken, closeParen);
listener.assertErrors([
@@ -837,14 +835,14 @@
// unmatched tokens, which means that `"${({(}}"` parses as though the open
// parens are unmatched but everything else is matched.
var stringStart = _scan(r'"${({(}}"');
- BeginToken interpolationStart = stringStart.next;
- BeginToken openParen1 = interpolationStart.next;
- BeginToken openBrace = openParen1.next;
- BeginToken openParen2 = openBrace.next;
- var closeBrace = openParen2.next;
- var interpolationEnd = closeBrace.next;
- var stringEnd = interpolationEnd.next;
- expect(stringEnd.next.type, TokenType.EOF);
+ BeginToken interpolationStart = stringStart.next as BeginToken;
+ BeginToken openParen1 = interpolationStart.next as BeginToken;
+ BeginToken openBrace = openParen1.next as BeginToken;
+ BeginToken openParen2 = openBrace.next as BeginToken;
+ var closeBrace = openParen2.next!;
+ var interpolationEnd = closeBrace.next!;
+ var stringEnd = interpolationEnd.next!;
+ expect(stringEnd.next!.type, TokenType.EOF);
expect(interpolationStart.endToken, same(interpolationEnd));
expect(openParen1.endToken, isNull);
expect(openBrace.endToken, same(closeBrace));
@@ -966,12 +964,12 @@
void test_startAndEnd() {
Token token = _scan("a");
expect(token.offset, 0);
- Token previous = token.previous;
+ Token previous = token.previous!;
expect(previous.next, token);
expect(previous.previous, previous);
expect(previous.type, TokenType.EOF);
expect(previous.offset, -1);
- Token next = token.next;
+ Token next = token.next!;
expect(next.next, next);
expect(next.previous, token);
expect(next.type, TokenType.EOF);
@@ -1290,8 +1288,8 @@
Token token = _scan("sync*");
expect(token.type.isKeyword, true);
expect(token.lexeme, 'sync');
- expect(token.next.type, TokenType.STAR);
- expect(token.next.next.type, TokenType.EOF);
+ expect(token.next!.type, TokenType.STAR);
+ expect(token.next!.next!.type, TokenType.EOF);
}
void test_tilde() {
@@ -1312,10 +1310,10 @@
}
void test_unmatched_openers() {
- BeginToken openBrace = _scan('{[(');
- BeginToken openBracket = openBrace.next;
- BeginToken openParen = openBracket.next;
- expect(openParen.next.type, TokenType.EOF);
+ BeginToken openBrace = _scan('{[(') as BeginToken;
+ BeginToken openBracket = openBrace.next as BeginToken;
+ BeginToken openParen = openBracket.next as BeginToken;
+ expect(openParen.next!.type, TokenType.EOF);
expect(openBrace.endToken, isNull);
expect(openBracket.endToken, isNull);
expect(openParen.endToken, isNull);
@@ -1328,9 +1326,9 @@
Token token = _scan(source);
expect(token, isNotNull);
expect(token.type, TokenType.EOF);
- Token comment = token.precedingComments;
+ Token? comment = token.precedingComments;
expect(comment, isNotNull);
- expect(comment.type, commentType);
+ expect(comment!.type, commentType);
expect(comment.offset, 0);
expect(comment.length, source.length);
expect(comment.lexeme, source);
@@ -1342,7 +1340,7 @@
expect(token.type, TokenType.EOF);
comment = token.precedingComments;
expect(comment, isNotNull);
- expect(comment.type, commentType);
+ expect(comment!.type, commentType);
expect(comment.offset, 0);
expect(comment.length, source.length);
expect(comment.lexeme, source);
@@ -1358,7 +1356,7 @@
*/
Token _assertError(
ScannerErrorCode expectedError, int expectedOffset, String source,
- [List<Object> arguments]) {
+ [List<Object>? arguments]) {
ErrorListener listener = new ErrorListener();
var tokens = scanWithListener(source, listener);
listener.assertErrors(
@@ -1388,7 +1386,7 @@
* with the same lexeme as the original source.
*/
void _assertKeywordToken(String source,
- {ScannerConfiguration configuration}) {
+ {ScannerConfiguration? configuration}) {
Token token = _scan(source, configuration: configuration);
expect(token, isNotNull);
expect(token.type.isKeyword, true);
@@ -1407,7 +1405,7 @@
value = token.value();
expect(value is Keyword, isTrue);
expect((value as Keyword).lexeme, source);
- expect(token.next.type, TokenType.EOF);
+ expect(token.next!.type, TokenType.EOF);
}
/**
@@ -1415,7 +1413,7 @@
* token with the same lexeme as the original source.
*/
void _assertNotKeywordToken(String source,
- {ScannerConfiguration configuration}) {
+ {ScannerConfiguration? configuration}) {
Token token = _scan(source, configuration: configuration);
expect(token, isNotNull);
expect(token.type.isKeyword, false);
@@ -1428,7 +1426,7 @@
expect(token.offset, 1);
expect(token.length, source.length);
expect(token.lexeme, source);
- expect(token.next.type, TokenType.EOF);
+ expect(token.next!.type, TokenType.EOF);
}
/**
@@ -1494,10 +1492,11 @@
void _checkTokens(Token firstToken, List<Token> expectedTokens) {
expect(firstToken, isNotNull);
- Token token = firstToken;
+ Token? token = firstToken;
for (int i = 0; i < expectedTokens.length; i++) {
Token expectedToken = expectedTokens[i];
- expect(token.type, expectedToken.type, reason: "Wrong type for token $i");
+ expect(token!.type, expectedToken.type,
+ reason: "Wrong type for token $i");
expect(token.offset, expectedToken.offset,
reason: "Wrong offset for token $i");
expect(token.length, expectedToken.length,
@@ -1507,11 +1506,11 @@
token = token.next;
expect(token, isNotNull);
}
- expect(token.type, TokenType.EOF);
+ expect(token!.type, TokenType.EOF);
}
Token _scan(String source,
- {ScannerConfiguration configuration, bool ignoreErrors: false}) {
+ {ScannerConfiguration? configuration, bool ignoreErrors: false}) {
ErrorListener listener = new ErrorListener();
Token token =
scanWithListener(source, listener, configuration: configuration);
@@ -1539,7 +1538,7 @@
class TestError {
final int offset;
final ErrorCode errorCode;
- final List<Object> arguments;
+ final List<Object>? arguments;
TestError(this.offset, this.errorCode, this.arguments);
@@ -1547,7 +1546,7 @@
int get hashCode {
int h = combineHash(combineHash(0, offset), errorCode.hashCode);
if (arguments != null) {
- for (Object argument in arguments) {
+ for (Object argument in arguments!) {
h = combineHash(h, argument.hashCode);
}
}
@@ -1561,9 +1560,9 @@
errorCode == other.errorCode) {
if (arguments == null) return other.arguments == null;
if (other.arguments == null) return false;
- if (arguments.length != other.arguments.length) return false;
- for (int i = 0; i < arguments.length; i++) {
- if (arguments[i] != other.arguments[i]) return false;
+ if (arguments!.length != other.arguments!.length) return false;
+ for (int i = 0; i < arguments!.length; i++) {
+ if (arguments![i] != other.arguments![i]) return false;
}
return true;
}
@@ -1572,7 +1571,7 @@
@override
String toString() {
- var argString = arguments == null ? '' : '(${arguments.join(', ')})';
+ var argString = arguments == null ? '' : '(${arguments!.join(', ')})';
return 'Error($offset, $errorCode$argString)';
}
}
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 2cbe46e..3be6daf 100644
--- a/pkg/front_end/test/scheme_based_file_system_test.dart
+++ b/pkg/front_end/test/scheme_based_file_system_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:front_end/src/api_prototype/file_system.dart';
import 'package:front_end/src/scheme_based_file_system.dart';
@@ -16,10 +14,10 @@
var fileSystem =
new SchemeBasedFileSystem({'scheme1': fs1, 'scheme2': fs2});
- MockFileSystemEntity e1 =
- fileSystem.entityForUri(Uri.parse('scheme1:a.dart'));
- MockFileSystemEntity e2 =
- fileSystem.entityForUri(Uri.parse('scheme2:a.dart'));
+ MockFileSystemEntity e1 = fileSystem
+ .entityForUri(Uri.parse('scheme1:a.dart')) as MockFileSystemEntity;
+ MockFileSystemEntity e2 = fileSystem
+ .entityForUri(Uri.parse('scheme2:a.dart')) as MockFileSystemEntity;
expect(e1.fileSystem, fs1);
expect(e2.fileSystem, fs2);
});
diff --git a/pkg/front_end/test/severity_index_test.dart b/pkg/front_end/test/severity_index_test.dart
index 105e46b..e404f6a 100644
--- a/pkg/front_end/test/severity_index_test.dart
+++ b/pkg/front_end/test/severity_index_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/messages/severity.dart' show Severity;
/// Test that Severity has the expected indexes. Note that this is important
diff --git a/pkg/front_end/test/spell_checking_cleanup_lists.dart b/pkg/front_end/test/spell_checking_cleanup_lists.dart
index ea44732..f2b9684 100644
--- a/pkg/front_end/test/spell_checking_cleanup_lists.dart
+++ b/pkg/front_end/test/spell_checking_cleanup_lists.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';
import 'spell_checking_utils.dart' as spell;
@@ -12,7 +10,7 @@
{
spell.ensureDictionariesLoaded([spell.Dictionaries.common]);
Set<String> commonWords =
- spell.loadedDictionaries[spell.Dictionaries.common];
+ spell.loadedDictionaries![spell.Dictionaries.common]!;
for (spell.Dictionaries dictionary in spell.Dictionaries.values) {
if (dictionary == spell.Dictionaries.common) continue;
Uri uri = spell.dictionaryToUri(dictionary);
@@ -30,7 +28,7 @@
{
spell.ensureDictionariesLoaded([spell.Dictionaries.cfeCode]);
Set<String> codeWords =
- spell.loadedDictionaries[spell.Dictionaries.cfeCode];
+ spell.loadedDictionaries![spell.Dictionaries.cfeCode]!;
Uri uri = spell.dictionaryToUri(spell.Dictionaries.cfeTests);
List<String> keep = <String>[];
for (String line in new File.fromUri(uri).readAsLinesSync()) {
diff --git a/pkg/front_end/test/spell_checking_utils.dart b/pkg/front_end/test/spell_checking_utils.dart
index c08fb4c..c560644 100644
--- a/pkg/front_end/test/spell_checking_utils.dart
+++ b/pkg/front_end/test/spell_checking_utils.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 File, stdin, stdout;
import "utils/io_utils.dart";
@@ -19,17 +17,17 @@
denylist,
}
-Map<Dictionaries, Set<String>> loadedDictionaries;
+Map<Dictionaries, Set<String>>? loadedDictionaries;
SpellingResult spellcheckString(String s,
- {List<Dictionaries> dictionaries, bool splitAsCode: false}) {
+ {List<Dictionaries>? dictionaries, bool splitAsCode: false}) {
dictionaries ??= const [Dictionaries.common];
ensureDictionariesLoaded(dictionaries);
- List<String> wrongWords;
- List<List<String>> wrongWordsAlternatives;
- List<int> wrongWordsOffset;
- List<bool> wrongWordDenylisted;
+ List<String>? wrongWords;
+ List<List<String>?>? wrongWordsAlternatives;
+ List<int>? wrongWordsOffset;
+ List<bool>? wrongWordDenylisted;
List<int> wordOffsets = <int>[];
List<String> words =
splitStringIntoWords(s, wordOffsets, splitAsCode: splitAsCode);
@@ -37,7 +35,7 @@
for (int j = 0; j < dictionaries.length; j++) {
Dictionaries dictionaryType = dictionaries[j];
if (dictionaryType == Dictionaries.denylist) continue;
- Set<String> dictionary = loadedDictionaries[dictionaryType];
+ Set<String> dictionary = loadedDictionaries![dictionaryType]!;
dictionariesUnpacked.add(dictionary);
}
for (int i = 0; i < words.length; i++) {
@@ -61,7 +59,7 @@
wrongWordsOffset.add(offset);
wrongWordDenylisted ??= <bool>[];
wrongWordDenylisted
- .add(loadedDictionaries[Dictionaries.denylist].contains(word));
+ .add(loadedDictionaries![Dictionaries.denylist]!.contains(word));
}
}
@@ -69,8 +67,8 @@
wrongWordsAlternatives);
}
-List<String> findAlternatives(String word, List<Set<String>> dictionaries) {
- List<String> result;
+List<String>? findAlternatives(String word, List<Set<String>> dictionaries) {
+ List<String>? result;
bool check(String w) {
for (int j = 0; j < dictionaries.length; j++) {
@@ -81,8 +79,7 @@
}
void ok(String w) {
- result ??= <String>[];
- result.add(w);
+ (result ??= <String>[]).add(w);
}
// Delete a letter, insert a letter or change a letter and lookup.
@@ -111,10 +108,10 @@
}
class SpellingResult {
- final List<String> misspelledWords;
- final List<int> misspelledWordsOffset;
- final List<bool> misspelledWordsDenylisted;
- final List<List<String>> misspelledWordsAlternatives;
+ final List<String>? misspelledWords;
+ final List<int>? misspelledWordsOffset;
+ final List<bool>? misspelledWordsDenylisted;
+ final List<List<String>?>? misspelledWordsAlternatives;
SpellingResult(this.misspelledWords, this.misspelledWordsOffset,
this.misspelledWordsDenylisted, this.misspelledWordsAlternatives);
@@ -140,19 +137,19 @@
loadedDictionaries ??= new Map<Dictionaries, Set<String>>();
// Ensure the denylist is loaded.
- Set<String> denylistDictionary = loadedDictionaries[Dictionaries.denylist];
+ Set<String>? denylistDictionary = loadedDictionaries![Dictionaries.denylist];
if (denylistDictionary == null) {
denylistDictionary = new Set<String>();
- loadedDictionaries[Dictionaries.denylist] = denylistDictionary;
+ loadedDictionaries![Dictionaries.denylist] = denylistDictionary;
addWords(dictionaryToUri(Dictionaries.denylist), denylistDictionary);
}
for (int j = 0; j < dictionaries.length; j++) {
Dictionaries dictionaryType = dictionaries[j];
- Set<String> dictionary = loadedDictionaries[dictionaryType];
+ Set<String>? dictionary = loadedDictionaries![dictionaryType];
if (dictionary == null) {
dictionary = new Set<String>();
- loadedDictionaries[dictionaryType] = dictionary;
+ loadedDictionaries![dictionaryType] = dictionary;
addWords(dictionaryToUri(dictionaryType), dictionary);
// Check that no good words occur in the denylist.
for (String s in dictionary) {
@@ -182,7 +179,6 @@
return repoDir
.resolve("pkg/front_end/test/spell_checking_list_denylist.txt");
}
- throw "Unknown Dictionary";
}
List<String> splitStringIntoWords(String s, List<int> splitOffsets,
@@ -370,7 +366,7 @@
print("The following word(s) were reported as unknown:");
print("----------------");
- Dictionaries dictionaryToUse;
+ Dictionaries? dictionaryToUse;
if (dictionaries.contains(Dictionaries.cfeTests)) {
dictionaryToUse = Dictionaries.cfeTests;
} else if (dictionaries.contains(Dictionaries.cfeMessages)) {
@@ -391,11 +387,11 @@
for (String s in reportedWords) {
print("- $s");
String answer;
- bool add;
+ bool? add;
while (true) {
stdout.write("Do you want to add the word to the dictionary "
"$dictionaryToUse (y/n)? ");
- answer = stdin.readLineSync().trim().toLowerCase();
+ answer = stdin.readLineSync()!.trim().toLowerCase();
switch (answer) {
case "y":
case "yes":
diff --git a/pkg/front_end/test/spell_checking_utils_test.dart b/pkg/front_end/test/spell_checking_utils_test.dart
index 07c8904..b91ed891 100644
--- a/pkg/front_end/test/spell_checking_utils_test.dart
+++ b/pkg/front_end/test/spell_checking_utils_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 'spell_checking_utils.dart';
void main() {
@@ -98,7 +96,7 @@
compareLists(actualOffsets, expectedOffsets);
}
-void compareLists(List<dynamic> actual, List<dynamic> expected) {
+void compareLists(List<dynamic>? actual, List<dynamic>? expected) {
if (actual == null && expected == null) return;
if (actual == null) throw "Got null, expected $expected";
if (expected == null) throw "Expected null, got $actual";
@@ -113,7 +111,7 @@
}
void expectAlternative(
- String word, List<String> expected, Set<String> dictionary) {
- List<String> alternatives = findAlternatives(word, [dictionary]);
+ String word, List<String>? expected, Set<String> dictionary) {
+ List<String>? alternatives = findAlternatives(word, [dictionary]);
compareLists(alternatives, expected);
}
diff --git a/pkg/front_end/test/spelling_test_base.dart b/pkg/front_end/test/spelling_test_base.dart
index fd1c94a..e6d43dc 100644
--- a/pkg/front_end/test/spelling_test_base.dart
+++ b/pkg/front_end/test/spelling_test_base.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 File, Platform;
import 'dart:typed_data' show Uint8List;
@@ -37,7 +35,7 @@
final bool interactive;
final bool onlyInGit;
- SpellContext({this.interactive, this.onlyInGit});
+ SpellContext({required this.interactive, required this.onlyInGit});
// Override special handling of negative tests.
@override
@@ -75,7 +73,7 @@
dictionaries,
interactive,
'"$dartPath" "$suitePath" -DonlyInGit=$onlyInGit -Dinteractive=true');
- return null;
+ return new Future.value();
}
}
@@ -97,14 +95,13 @@
Utf8BytesScanner scanner =
new Utf8BytesScanner(bytes, includeComments: true);
Token firstToken = scanner.tokenize();
- if (firstToken == null) return null;
- Token token = firstToken;
+ Token? token = firstToken;
- List<String> errors;
+ List<String>? errors;
Source source = new Source(
scanner.lineStarts, rawBytes, description.uri, description.uri);
void addErrorMessage(
- int offset, String word, bool denylisted, List<String> alternatives) {
+ int offset, String word, bool denylisted, List<String>? alternatives) {
errors ??= <String>[];
String message;
if (denylisted) {
@@ -128,7 +125,7 @@
"- $dictionaryPathString\n";
}
Location location = source.getLocation(description.uri, offset);
- errors.add(command_line_reporting.formatErrorMessage(
+ errors!.add(command_line_reporting.formatErrorMessage(
source.getTextLine(location.line),
location,
word.length,
@@ -142,20 +139,20 @@
return pass(description);
}
if (token.precedingComments != null) {
- Token comment = token.precedingComments;
+ Token? comment = token.precedingComments;
while (comment != null) {
spell.SpellingResult spellingResult = spell.spellcheckString(
comment.lexeme,
splitAsCode: true,
dictionaries: context.dictionaries);
if (spellingResult.misspelledWords != null) {
- for (int i = 0; i < spellingResult.misspelledWords.length; i++) {
- bool denylisted = spellingResult.misspelledWordsDenylisted[i];
+ for (int i = 0; i < spellingResult.misspelledWords!.length; i++) {
+ bool denylisted = spellingResult.misspelledWordsDenylisted![i];
if (context.onlyDenylisted && !denylisted) continue;
int offset =
- comment.offset + spellingResult.misspelledWordsOffset[i];
- addErrorMessage(offset, spellingResult.misspelledWords[i],
- denylisted, spellingResult.misspelledWordsAlternatives[i]);
+ comment.offset + spellingResult.misspelledWordsOffset![i];
+ addErrorMessage(offset, spellingResult.misspelledWords![i],
+ denylisted, spellingResult.misspelledWordsAlternatives![i]);
}
}
comment = comment.next;
@@ -167,12 +164,13 @@
splitAsCode: true,
dictionaries: context.dictionaries);
if (spellingResult.misspelledWords != null) {
- for (int i = 0; i < spellingResult.misspelledWords.length; i++) {
- bool denylisted = spellingResult.misspelledWordsDenylisted[i];
+ for (int i = 0; i < spellingResult.misspelledWords!.length; i++) {
+ bool denylisted = spellingResult.misspelledWordsDenylisted![i];
if (context.onlyDenylisted && !denylisted) continue;
- int offset = token.offset + spellingResult.misspelledWordsOffset[i];
- addErrorMessage(offset, spellingResult.misspelledWords[i],
- denylisted, spellingResult.misspelledWordsAlternatives[i]);
+ int offset =
+ token.offset + spellingResult.misspelledWordsOffset![i];
+ addErrorMessage(offset, spellingResult.misspelledWords![i],
+ denylisted, spellingResult.misspelledWordsAlternatives![i]);
}
}
} else if (token is KeywordToken || token is BeginToken) {
@@ -191,7 +189,7 @@
if (errors == null) {
return pass(description);
} else {
- return fail(description, errors.join("\n\n"));
+ return fail(description, errors!.join("\n\n"));
}
}
}
diff --git a/pkg/front_end/test/spelling_test_external_targets.dart b/pkg/front_end/test/spelling_test_external_targets.dart
index 952f4b3..c2eef74 100644
--- a/pkg/front_end/test/spelling_test_external_targets.dart
+++ b/pkg/front_end/test/spelling_test_external_targets.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, FileSystemEntity;
import 'package:testing/testing.dart'
@@ -30,7 +28,7 @@
}
class SpellContextExternal extends SpellContext {
- SpellContextExternal({bool interactive, bool onlyInGit})
+ SpellContextExternal({required bool interactive, required bool onlyInGit})
: super(interactive: interactive, onlyInGit: onlyInGit);
@override
diff --git a/pkg/front_end/test/spelling_test_not_src_suite.dart b/pkg/front_end/test/spelling_test_not_src_suite.dart
index 9eb9921e..285e8fc 100644
--- a/pkg/front_end/test/spelling_test_not_src_suite.dart
+++ b/pkg/front_end/test/spelling_test_not_src_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 file.
-// @dart = 2.9
-
import 'package:testing/testing.dart' show Chain, runMe;
import 'spelling_test_base.dart';
@@ -26,7 +24,7 @@
}
class SpellContextTest extends SpellContext {
- SpellContextTest({bool interactive, bool onlyInGit})
+ SpellContextTest({required bool interactive, required bool onlyInGit})
: super(interactive: interactive, onlyInGit: onlyInGit);
@override
diff --git a/pkg/front_end/test/spelling_test_src_suite.dart b/pkg/front_end/test/spelling_test_src_suite.dart
index f77e918..066475a 100644
--- a/pkg/front_end/test/spelling_test_src_suite.dart
+++ b/pkg/front_end/test/spelling_test_src_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 file.
-// @dart = 2.9
-
import 'package:testing/testing.dart' show Chain, runMe;
import 'spelling_test_base.dart';
@@ -26,7 +24,7 @@
}
class SpellContextSource extends SpellContext {
- SpellContextSource({bool interactive, bool onlyInGit})
+ SpellContextSource({required bool interactive, required bool onlyInGit})
: super(interactive: interactive, onlyInGit: onlyInGit);
@override
diff --git a/pkg/front_end/test/split_dill_test.dart b/pkg/front_end/test/split_dill_test.dart
index 16484f2..9ff415d 100644
--- a/pkg/front_end/test/split_dill_test.dart
+++ b/pkg/front_end/test/split_dill_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, Process, ProcessResult;
import 'dart:typed_data' show Uint8List;
diff --git a/pkg/front_end/test/standard_file_system_test.dart b/pkg/front_end/test/standard_file_system_test.dart
index c22913c..5092342 100644
--- a/pkg/front_end/test/standard_file_system_test.dart
+++ b/pkg/front_end/test/standard_file_system_test.dart
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=--supermixin
-// @dart = 2.9
-
library front_end.test.standard_file_system_test;
import 'dart:convert';
@@ -31,8 +29,8 @@
@reflectiveTest
class DirectoryTest extends _BaseTest {
- String path;
- FileSystemEntity dir;
+ late String path;
+ late FileSystemEntity dir;
@override
void setUp() {
@@ -70,8 +68,8 @@
@reflectiveTest
class FileTest extends _BaseTest {
- String path;
- FileSystemEntity file;
+ late String path;
+ late FileSystemEntity file;
@override
void setUp() {
@@ -146,7 +144,7 @@
@reflectiveTest
class StandardFileSystemTest extends _BaseTest {
- Uri tempUri;
+ late Uri tempUri;
@override
void setUp() {
@@ -231,8 +229,8 @@
}
class _BaseTest {
- io.Directory tempDirectory;
- String tempPath;
+ late io.Directory tempDirectory;
+ late String tempPath;
FileSystemEntity entityForPath(String path) =>
StandardFileSystem.instance.entityForUri(p.toUri(path));
diff --git a/pkg/front_end/test/summary_generator_test.dart b/pkg/front_end/test/summary_generator_test.dart
index f3178d6..636cc41 100644
--- a/pkg/front_end/test/summary_generator_test.dart
+++ b/pkg/front_end/test/summary_generator_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:front_end/src/api_prototype/front_end.dart';
import 'package:front_end/src/testing/compiler_common.dart';
import 'package:kernel/kernel.dart';
@@ -13,19 +11,19 @@
void main() {
test('summary has no source-info by default', () async {
var summary = await summarize(['a.dart'], allSources);
- var component = loadComponentFromBytes(summary);
+ var component = loadComponentFromBytes(summary!);
// Note: the kernel representation always includes the Uri entries, but
// doesn't include the actual source here.
for (Source source in component.uriToSource.values) {
expect(source.source.length, 0);
- expect(source.lineStarts.length, 0);
+ expect(source.lineStarts!.length, 0);
}
});
test('summary includes declarations, but no method bodies', () async {
var summary = await summarize(['a.dart'], allSources);
- var component = loadComponentFromBytes(summary);
+ var component = loadComponentFromBytes(summary!);
var aLib = findLibrary(component, 'a.dart');
expect(aLib.importUri.path, '/a/b/c/a.dart');
var classA = aLib.classes.first;
@@ -51,7 +49,7 @@
var summaryD = await summarize(['d.dart'], sourcesWithABC,
additionalDills: ['a.dill', 'bc.dill']);
- checkDSummary(summaryD);
+ checkDSummary(summaryD!);
});
test('dependencies can be combined in any order', () async {
@@ -71,13 +69,13 @@
// dill files and because of how the kernel loader merges definitions.
var summaryD = await summarize(['d.dart'], sourcesWithABC,
additionalDills: ['bc.dill', 'a.dill']);
- checkDSummary(summaryD);
+ checkDSummary(summaryD!);
});
test('dependencies not included in truncated summaries', () async {
// Note: by default this test is loading the SDK from summaries.
var summaryA = await summarize(['a.dart'], allSources, truncate: true);
- var component = loadComponentFromBytes(summaryA);
+ var component = loadComponentFromBytes(summaryA!);
expect(component.libraries.length, 1);
expect(
component.libraries.single.importUri.path.endsWith('a.dart'), isTrue);
@@ -86,7 +84,7 @@
sourcesWithA['a.dill'] = summaryA;
var summaryB = await summarize(['b.dart'], sourcesWithA,
additionalDills: ['a.dill'], truncate: true);
- component = loadComponentFromBytes(summaryB);
+ component = loadComponentFromBytes(summaryB!);
expect(component.libraries.length, 1);
expect(
component.libraries.single.importUri.path.endsWith('b.dart'), isTrue);
@@ -126,7 +124,7 @@
expect(bClass.superclass, same(aClass));
var dClass = dLib.classes.firstWhere((c) => c.name == 'D');
- expect(dClass.superclass.superclass, same(bClass));
+ expect(dClass.superclass!.superclass, same(bClass));
var dInterface = dClass.implementedTypes.first.classNode;
expect(dInterface, same(aClass));
diff --git a/pkg/front_end/test/test_generator_test.dart b/pkg/front_end/test/test_generator_test.dart
index 2f009ea..73cd4e9 100644
--- a/pkg/front_end/test/test_generator_test.dart
+++ b/pkg/front_end/test/test_generator_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, File, stdout;
import 'package:front_end/src/api_prototype/compiler_options.dart';
diff --git a/pkg/front_end/test/textual_outline_test.dart b/pkg/front_end/test/textual_outline_test.dart
index 4a2af05..fd50c3f 100644
--- a/pkg/front_end/test/textual_outline_test.dart
+++ b/pkg/front_end/test/textual_outline_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:convert";
import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
@@ -17,7 +15,7 @@
void main() {
// Doesn't sort if not asked to perform modelling.
- String result = textualOutline(utf8.encode("""
+ String? result = textualOutline(utf8.encode("""
b() { print("hello"); }
a() { print("hello"); }
"""), scannerConfiguration, throwOnUnexpected: true, performModelling: false);
diff --git a/pkg/front_end/test/token_test.dart b/pkg/front_end/test/token_test.dart
index 9cf8377..301e6ec 100644
--- a/pkg/front_end/test/token_test.dart
+++ b/pkg/front_end/test/token_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/scanner/scanner.dart'
show ScannerConfiguration, scanString;
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
@@ -38,11 +36,11 @@
Token nextComment() {
while (!token.isEof) {
- Token comment = token.precedingComments;
- token = token.next;
+ Token? comment = token.precedingComments;
+ token = token.next!;
if (comment != null) return comment;
}
- return null;
+ throw new StateError("No comment found.");
}
Token comment = nextComment();
@@ -69,10 +67,10 @@
void test_isSynthetic() {
var token = scanString('/* 1 */ foo', includeComments: true).tokens;
expect(token.isSynthetic, false);
- expect(token.precedingComments.isSynthetic, false);
- expect(token.previous.isSynthetic, true);
- expect(token.next.isEof, true);
- expect(token.next.isSynthetic, true);
+ expect(token.precedingComments!.isSynthetic, false);
+ expect(token.previous!.isSynthetic, true);
+ expect(token.next!.isEof, true);
+ expect(token.next!.isSynthetic, true);
}
void test_matchesAny() {
@@ -197,11 +195,11 @@
expect(token.lexeme, 'true');
expect(token.value(), Keyword.TRUE);
// General tokens
- token = token.next;
+ token = token.next!;
expect(token.lexeme, '&');
expect(token.value(), '&');
// String tokens
- token = token.next;
+ token = token.next!;
expect(token.lexeme, '"home"');
expect(token.value(), '"home"');
}
diff --git a/pkg/front_end/test/tool/reload.dart b/pkg/front_end/test/tool/reload.dart
index 40a94a5..887ed3b 100644
--- a/pkg/front_end/test/tool/reload.dart
+++ b/pkg/front_end/test/tool/reload.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
-
/// A helper library to connect to an existing VM and trigger a hot-reload via
/// its service protocol.
///
@@ -32,12 +30,12 @@
/// An peer point used to send service protocol messages. The service
/// protocol uses JSON rpc on top of web-sockets.
json_rpc.Peer get rpc => _rpc ??= _createPeer();
- json_rpc.Peer _rpc;
+ json_rpc.Peer? _rpc;
/// The main isolate ID of the running VM. Needed to indicate to the VM which
/// isolate to reload.
FutureOr<String> get mainId async => _mainId ??= await _computeMainId();
- String _mainId;
+ String? _mainId;
RemoteVm([this.port = 8181]);
@@ -91,8 +89,8 @@
Future disconnect() async {
if (_rpc == null) return null;
this._mainId = null;
- if (!_rpc.isClosed) {
- var future = _rpc.close();
+ if (!_rpc!.isClosed) {
+ var future = _rpc!.close();
_rpc = null;
return future;
}
diff --git a/pkg/front_end/test/type_labeler_test.dart b/pkg/front_end/test/type_labeler_test.dart
index ec09139..af83e29 100644
--- a/pkg/front_end/test/type_labeler_test.dart
+++ b/pkg/front_end/test/type_labeler_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:kernel/ast.dart';
import 'package:front_end/src/fasta/kernel/type_labeler.dart';
@@ -24,7 +22,7 @@
}
});
expectations.forEach((Node node, String expected) {
- Expect.stringEquals(expected, conversions[node].join());
+ Expect.stringEquals(expected, conversions[node]!.join());
});
int newlines = "\n".allMatches(labeler.originMessages).length;
Expect.equals(bulletCount, newlines);
diff --git a/pkg/front_end/test/vm_service_coverage.dart b/pkg/front_end/test/vm_service_coverage.dart
index 0d4768a..2112bd0 100644
--- a/pkg/front_end/test/vm_service_coverage.dart
+++ b/pkg/front_end/test/vm_service_coverage.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:async';
import 'vm_service_helper.dart' as vmService;
@@ -31,12 +29,12 @@
@override
Future<void> run() async {
vmService.VM vm = await serviceClient.getVM();
- if (vm.isolates.length != 1) {
- throw "Expected 1 isolate, got ${vm.isolates.length}";
+ if (vm.isolates!.length != 1) {
+ throw "Expected 1 isolate, got ${vm.isolates!.length}";
}
- vmService.IsolateRef isolateRef = vm.isolates.single;
- await waitUntilIsolateIsRunnable(isolateRef.id);
- await serviceClient.resume(isolateRef.id);
+ vmService.IsolateRef isolateRef = vm.isolates!.single;
+ await waitUntilIsolateIsRunnable(isolateRef.id!);
+ await serviceClient.resume(isolateRef.id!);
Completer<String> cTimeout = new Completer();
Timer timer = new Timer(new Duration(minutes: 20), () {
cTimeout.complete("Timeout");
@@ -45,13 +43,13 @@
Completer<String> cRunDone = new Completer();
// ignore: unawaited_futures
- waitUntilPaused(isolateRef.id).then((value) => cRunDone.complete("Done"));
+ waitUntilPaused(isolateRef.id!).then((value) => cRunDone.complete("Done"));
await Future.any([cRunDone.future, cTimeout.future, cProcessExited.future]);
timer.cancel();
- if (!await isPausedAtExit(isolateRef.id)) {
+ if (!await isPausedAtExit(isolateRef.id!)) {
killProcess();
throw "Expected to be paused at exit, but is just paused!";
}
@@ -59,36 +57,36 @@
// Get and process coverage information.
Stopwatch stopwatch = new Stopwatch()..start();
vmService.SourceReport sourceReport = await serviceClient.getSourceReport(
- isolateRef.id, [vmService.SourceReportKind.kCoverage],
+ isolateRef.id!, [vmService.SourceReportKind.kCoverage],
forceCompile: forceCompilation);
print("Got source report from VM in ${stopwatch.elapsedMilliseconds} ms");
stopwatch.reset();
Map<Uri, Coverage> coverages = {};
- for (vmService.SourceReportRange range in sourceReport.ranges) {
- vmService.ScriptRef script = sourceReport.scripts[range.scriptIndex];
- Uri scriptUri = Uri.parse(script.uri);
+ for (vmService.SourceReportRange range in sourceReport.ranges!) {
+ vmService.ScriptRef script = sourceReport.scripts![range.scriptIndex!];
+ Uri scriptUri = Uri.parse(script.uri!);
if (!includeCoverageFor(scriptUri)) continue;
Coverage coverage = coverages[scriptUri] ??= new Coverage();
- vmService.SourceReportCoverage sourceReportCoverage = range.coverage;
+ vmService.SourceReportCoverage? sourceReportCoverage = range.coverage;
if (sourceReportCoverage == null) {
// Range not compiled. Record the range if provided.
- assert(!range.compiled);
- if (range.startPos >= 0 || range.endPos >= 0) {
+ assert(!range.compiled!);
+ if (range.startPos! >= 0 || range.endPos! >= 0) {
coverage.notCompiled
- .add(new StartEndPair(range.startPos, range.endPos));
+ .add(new StartEndPair(range.startPos!, range.endPos!));
}
continue;
}
- coverage.hits.addAll(sourceReportCoverage.hits);
- coverage.misses.addAll(sourceReportCoverage.misses);
+ coverage.hits.addAll(sourceReportCoverage.hits!);
+ coverage.misses.addAll(sourceReportCoverage.misses!);
}
print("Processed source report from VM in "
"${stopwatch.elapsedMilliseconds} ms");
stopwatch.reset();
// It's paused at exit, so resuming should allow us to exit.
- await serviceClient.resume(isolateRef.id);
+ await serviceClient.resume(isolateRef.id!);
for (MapEntry<Uri, Coverage> entry in coverages.entries) {
assert(entry.value.hits.intersection(entry.value.misses).isEmpty);
@@ -142,7 +140,7 @@
String toString() => "[$startPos - $endPos]";
@override
- int compareTo(Object other) {
+ int compareTo(dynamic other) {
if (other is! StartEndPair) return -1;
StartEndPair o = other;
return startPos - o.startPos;
diff --git a/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart b/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart
index 56b167b..3128758 100644
--- a/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart
+++ b/pkg/front_end/test/vm_service_coverage_constant_evaluator.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 'vm_service_coverage.dart' as helper;
Future<void> main(List<String> args) async {
diff --git a/pkg/front_end/test/vm_service_for_leak_detection.dart b/pkg/front_end/test/vm_service_for_leak_detection.dart
index e5782a8..44b38c4 100644
--- a/pkg/front_end/test/vm_service_for_leak_detection.dart
+++ b/pkg/front_end/test/vm_service_for_leak_detection.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';
import "vm_service_heap_helper.dart" as helper;
diff --git a/pkg/front_end/test/vm_service_heap_finder.dart b/pkg/front_end/test/vm_service_heap_finder.dart
index a098317..a5aa11f 100644
--- a/pkg/front_end/test/vm_service_heap_finder.dart
+++ b/pkg/front_end/test/vm_service_heap_finder.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";
import "vm_service_helper.dart" as vmService;
@@ -16,9 +14,9 @@
}
Future<void> main(List<String> args) async {
- String connectTo;
- String classToFind;
- String whatToDo;
+ String? connectTo;
+ String? classToFind;
+ String? whatToDo;
for (String arg in args) {
if (arg.startsWith("--url=")) {
connectTo = arg.substring("--url=".length);
@@ -62,25 +60,26 @@
String ask(String question) {
stdout.write("$question: ");
- return stdin.readLineSync();
+ return stdin.readLineSync()!;
}
class VMServiceHeapHelperPrinter extends vmService.VMServiceHelper {
- Future<void> printAllocationProfile(String isolateId, {String filter}) async {
+ Future<void> printAllocationProfile(String isolateId,
+ {String? filter}) async {
await waitUntilIsolateIsRunnable(isolateId);
vmService.AllocationProfile allocationProfile =
await serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
+ for (vmService.ClassHeapStats member in allocationProfile.members!) {
if (filter != null) {
- if (member.classRef.name != filter) continue;
+ if (member.classRef!.name != filter) continue;
} else {
- if (member.classRef.name == "") continue;
+ if (member.classRef!.name == "") continue;
if (member.instancesCurrent == 0) continue;
}
- vmService.Class c =
- await serviceClient.getObject(isolateId, member.classRef.id);
+ vmService.Class c = await serviceClient.getObject(
+ isolateId, member.classRef!.id!) as vmService.Class;
if (c.location?.script?.uri == null) continue;
- print("${member.classRef.name}: ${member.instancesCurrent}");
+ print("${member.classRef!.name}: ${member.instancesCurrent}");
}
}
@@ -89,32 +88,32 @@
await waitUntilIsolateIsRunnable(isolateId);
vmService.AllocationProfile allocationProfile =
await serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (member.classRef.name != filter) continue;
- vmService.Class c =
- await serviceClient.getObject(isolateId, member.classRef.id);
+ for (vmService.ClassHeapStats member in allocationProfile.members!) {
+ if (member.classRef!.name != filter) continue;
+ vmService.Class c = await serviceClient.getObject(
+ isolateId, member.classRef!.id!) as vmService.Class;
if (c.location?.script?.uri == null) continue;
- print("${member.classRef.name}: ${member.instancesCurrent}");
- print(c.location.script.uri);
+ print("${member.classRef!.name}: ${member.instancesCurrent}");
+ print(c.location!.script!.uri);
vmService.InstanceSet instances = await serviceClient.getInstances(
- isolateId, member.classRef.id, 10000);
+ isolateId, member.classRef!.id!, 10000);
int instanceNum = 0;
- for (vmService.ObjRef instance in instances.instances) {
+ for (vmService.ObjRef instance in instances.instances!) {
instanceNum++;
vmService.Obj receivedObject =
- await serviceClient.getObject(isolateId, instance.id);
+ await serviceClient.getObject(isolateId, instance.id!);
if (receivedObject is! vmService.Instance) continue;
vmService.Instance object = receivedObject;
- for (vmService.BoundField field in object.fields) {
- if (field.decl.name == fieldName) {
+ for (vmService.BoundField field in object.fields!) {
+ if (field.decl!.name == fieldName) {
if (field.value is vmService.Sentinel) continue;
vmService.Obj receivedValue =
await serviceClient.getObject(isolateId, field.value.id);
if (receivedValue is! vmService.Instance) continue;
- String value = (receivedValue as vmService.Instance).valueAsString;
+ String value = receivedValue.valueAsString!;
if (!fieldValues.contains(value)) continue;
- print("${instanceNum}: ${field.decl.name}: "
+ print("${instanceNum}: ${field.decl!.name}: "
"${value} --- ${instance.id}");
}
}
@@ -127,29 +126,29 @@
await waitUntilIsolateIsRunnable(isolateId);
vmService.AllocationProfile allocationProfile =
await serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (member.classRef.name != filter) continue;
- vmService.Class c =
- await serviceClient.getObject(isolateId, member.classRef.id);
+ for (vmService.ClassHeapStats member in allocationProfile.members!) {
+ if (member.classRef!.name != filter) continue;
+ vmService.Class c = await serviceClient.getObject(
+ isolateId, member.classRef!.id!) as vmService.Class;
print("Found ${c.name} (location: ${c.location})");
- print("${member.classRef.name}: "
+ print("${member.classRef!.name}: "
"(instancesCurrent: ${member.instancesCurrent})");
print("");
vmService.InstanceSet instances = await serviceClient.getInstances(
- isolateId, member.classRef.id, 10000);
- print(" => Got ${instances.instances.length} instances");
+ isolateId, member.classRef!.id!, 10000);
+ print(" => Got ${instances.instances!.length} instances");
print("");
- for (vmService.ObjRef instance in instances.instances) {
+ for (vmService.ObjRef instance in instances.instances!) {
vmService.Obj receivedObject =
- await serviceClient.getObject(isolateId, instance.id);
+ await serviceClient.getObject(isolateId, instance.id!);
print("Instance: $receivedObject");
vmService.RetainingPath retainingPath =
- await serviceClient.getRetainingPath(isolateId, instance.id, 1000);
+ await serviceClient.getRetainingPath(isolateId, instance.id!, 1000);
print("Retaining path: (length ${retainingPath.length}");
- for (int i = 0; i < retainingPath.elements.length; i++) {
- print(" [$i] = ${retainingPath.elements[i]}");
+ for (int i = 0; i < retainingPath.elements!.length; i++) {
+ print(" [$i] = ${retainingPath.elements![i]}");
}
print("");
diff --git a/pkg/front_end/test/vm_service_heap_helper.dart b/pkg/front_end/test/vm_service_heap_helper.dart
index ac5e8d9..1511a51 100644
--- a/pkg/front_end/test/vm_service_heap_helper.dart
+++ b/pkg/front_end/test/vm_service_heap_helper.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 "vm_service_helper.dart" as vmService;
class VMServiceHeapHelperSpecificExactLeakFinder
@@ -22,13 +20,13 @@
}) {
if (interests.isEmpty) throw "Empty list of interests given";
for (Interest interest in interests) {
- Map<String, List<String>> classToFields = _interests[interest.uri];
+ Map<String, List<String>>? classToFields = _interests[interest.uri];
if (classToFields == null) {
classToFields = Map<String, List<String>>();
_interests[interest.uri] = classToFields;
}
_interestsClassNames.add(interest.className);
- List<String> fields = classToFields[interest.className];
+ List<String>? fields = classToFields[interest.className];
if (fields == null) {
fields = <String>[];
classToFields[interest.className] = fields;
@@ -36,12 +34,12 @@
fields.addAll(interest.fieldNames);
}
for (Interest interest in prettyPrints) {
- Map<String, List<String>> classToFields = _prettyPrints[interest.uri];
+ Map<String, List<String>>? classToFields = _prettyPrints[interest.uri];
if (classToFields == null) {
classToFields = Map<String, List<String>>();
_prettyPrints[interest.uri] = classToFields;
}
- List<String> fields = classToFields[interest.className];
+ List<String>? fields = classToFields[interest.className];
if (fields == null) {
fields = <String>[];
classToFields[interest.className] = fields;
@@ -51,20 +49,20 @@
}
Future<void> pause() async {
- await serviceClient.pause(_isolateRef.id);
+ await serviceClient.pause(_isolateRef.id!);
}
- vmService.VM _vm;
- vmService.IsolateRef _isolateRef;
- int _iterationNumber;
+ late vmService.VM _vm;
+ late vmService.IsolateRef _isolateRef;
+ late int _iterationNumber;
int get iterationNumber => _iterationNumber;
/// Best effort check if the isolate is idle.
Future<bool> isIdle() async {
- dynamic tmp = await serviceClient.getIsolate(_isolateRef.id);
+ dynamic tmp = await serviceClient.getIsolate(_isolateRef.id!);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
- return isolate.pauseEvent.topFrame == null;
+ return isolate.pauseEvent!.topFrame == null;
}
return false;
}
@@ -72,44 +70,44 @@
@override
Future<void> run() async {
_vm = await serviceClient.getVM();
- if (_vm.isolates.length == 0) {
+ if (_vm.isolates!.length == 0) {
print("Didn't get any isolates. Will wait 1 second and retry.");
await Future.delayed(new Duration(seconds: 1));
_vm = await serviceClient.getVM();
}
- if (_vm.isolates.length != 1) {
- throw "Expected 1 isolate, got ${_vm.isolates.length}";
+ if (_vm.isolates!.length != 1) {
+ throw "Expected 1 isolate, got ${_vm.isolates!.length}";
}
- _isolateRef = _vm.isolates.single;
- await forceGC(_isolateRef.id);
+ _isolateRef = _vm.isolates!.single;
+ await forceGC(_isolateRef.id!);
- assert(await isPausedAtStart(_isolateRef.id));
- await serviceClient.resume(_isolateRef.id);
+ assert(await isPausedAtStart(_isolateRef.id!));
+ await serviceClient.resume(_isolateRef.id!);
_iterationNumber = 1;
while (true) {
if (!shouldDoAnotherIteration(_iterationNumber)) break;
- await waitUntilPaused(_isolateRef.id);
+ await waitUntilPaused(_isolateRef.id!);
print("Iteration: #$_iterationNumber");
Stopwatch stopwatch = new Stopwatch()..start();
vmService.AllocationProfile allocationProfile =
- await forceGC(_isolateRef.id);
+ await forceGC(_isolateRef.id!);
print("Forced GC in ${stopwatch.elapsedMilliseconds} ms");
stopwatch.reset();
List<Leak> leaks = [];
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (_interestsClassNames.contains(member.classRef.name)) {
- vmService.Class c =
- await serviceClient.getObject(_isolateRef.id, member.classRef.id);
- String uriString = c.location?.script?.uri;
+ for (vmService.ClassHeapStats member in allocationProfile.members!) {
+ if (_interestsClassNames.contains(member.classRef!.name)) {
+ vmService.Class c = (await serviceClient.getObject(
+ _isolateRef.id!, member.classRef!.id!)) as vmService.Class;
+ String? uriString = c.location?.script?.uri;
if (uriString == null) continue;
Uri uri = Uri.parse(uriString);
- Map<String, List<String>> uriInterest = _interests[uri];
+ Map<String, List<String>>? uriInterest = _interests[uri];
if (uriInterest == null) continue;
- List<String> fieldsForClass = uriInterest[c.name];
+ List<String>? fieldsForClass = uriInterest[c.name];
if (fieldsForClass == null) continue;
List<String> fieldsForClassPrettyPrint = fieldsForClass;
@@ -117,11 +115,11 @@
uriInterest = _prettyPrints[uri];
if (uriInterest != null) {
if (uriInterest[c.name] != null) {
- fieldsForClassPrettyPrint = uriInterest[c.name];
+ fieldsForClassPrettyPrint = uriInterest[c.name]!;
}
}
- leaks.addAll(await _findLeaks(_isolateRef, member.classRef,
+ leaks.addAll(await _findLeaks(_isolateRef, member.classRef!,
fieldsForClass, fieldsForClassPrettyPrint));
}
}
@@ -138,7 +136,7 @@
print("Looked for leaks in ${stopwatch.elapsedMilliseconds} ms");
- await serviceClient.resume(_isolateRef.id);
+ await serviceClient.resume(_isolateRef.id!);
_iterationNumber++;
}
}
@@ -149,7 +147,7 @@
List<String> fieldsForClass,
List<String> fieldsForClassPrettyPrint) async {
// Use undocumented (/ private?) method to get all instances of this class.
- vmService.InstanceRef instancesAsList = await serviceClient.callMethod(
+ vmService.InstanceRef instancesAsList = (await serviceClient.callMethod(
"_getInstancesAsArray",
isolateId: isolateRef.id,
args: {
@@ -157,7 +155,7 @@
"includeSubclasses": false,
"includeImplementors": false,
},
- );
+ )) as vmService.InstanceRef;
// Create dart code that `toString`s a class instance according to
// the fields given as wanting printed. Both for finding duplicates (1) and
@@ -165,14 +163,14 @@
// them) (2).
// 1:
- String fieldsToStringCode = classRef.name +
+ String fieldsToStringCode = classRef.name! +
"[" +
fieldsForClass
.map((value) => "$value: \"\${element.$value}\"")
.join(", ") +
"]";
// 2:
- String fieldsToStringPrettyPrintCode = classRef.name +
+ String fieldsToStringPrettyPrintCode = classRef.name! +
"[" +
fieldsForClassPrettyPrint
.map((value) => "$value: \"\${element.$value}\"")
@@ -182,9 +180,9 @@
// Expression evaluation to find duplicates: Put all entries into a map
// indexed by the `toString` code created above, mapping to list of that
// data.
- vmService.InstanceRef mappedData = await serviceClient.evaluate(
- isolateRef.id,
- instancesAsList.id,
+ vmService.InstanceRef mappedData = (await serviceClient.evaluate(
+ isolateRef.id!,
+ instancesAsList.id!,
"""
this
.fold({}, (dynamic index, dynamic element) {
@@ -194,22 +192,22 @@
return index;
})
""",
- );
+ )) as vmService.InstanceRef;
// Expression calculation to find if any of the lists created as values
// above contains more than one entry (i.e. there's a duplicate).
- vmService.InstanceRef duplicatesLengthRef = await serviceClient.evaluate(
- isolateRef.id,
- mappedData.id,
+ vmService.InstanceRef duplicatesLengthRef = (await serviceClient.evaluate(
+ isolateRef.id!,
+ mappedData.id!,
"""
this
.values
.where((dynamic element) => (element.length > 1) as bool)
.length
""",
- );
- vmService.Instance duplicatesLength =
- await serviceClient.getObject(isolateRef.id, duplicatesLengthRef.id);
- int duplicates = int.tryParse(duplicatesLength.valueAsString);
+ )) as vmService.InstanceRef;
+ vmService.Instance duplicatesLength = (await serviceClient.getObject(
+ isolateRef.id!, duplicatesLengthRef.id!)) as vmService.Instance;
+ int? duplicates = int.tryParse(duplicatesLength.valueAsString!);
if (duplicates != 0) {
// There are duplicates. Expression calculation to encode the duplication
// data (both the string that caused it to be a duplicate and the pretty
@@ -219,9 +217,9 @@
// e.g. encode the string "string" as "6:string" (length 6, string),
// and the list ["foo", "bar"] as "2:3:foo:3:bar" (2 entries, length 3,
// foo, length 3, bar).
- vmService.ObjRef duplicatesDataRef = await serviceClient.evaluate(
- isolateRef.id,
- mappedData.id,
+ vmService.ObjRef duplicatesDataRef = (await serviceClient.evaluate(
+ isolateRef.id!,
+ mappedData.id!,
"""
this
.entries
@@ -237,11 +235,11 @@
return "\${keyPart}:\${valuePart1}:\${valuePart2}";
}).join(":")
""",
- );
+ )) as vmService.ObjRef;
if (duplicatesDataRef is! vmService.InstanceRef) {
if (duplicatesDataRef is vmService.ErrorRef) {
- vmService.Error error = await serviceClient.getObject(
- isolateRef.id, duplicatesDataRef.id);
+ vmService.Error error = (await serviceClient.getObject(
+ isolateRef.id!, duplicatesDataRef.id!)) as vmService.Error;
throw "Leak found, but trying to evaluate pretty printing "
"didn't go as planned.\n"
"Got error with message "
@@ -254,9 +252,9 @@
}
}
- vmService.Instance duplicatesData =
- await serviceClient.getObject(isolateRef.id, duplicatesDataRef.id);
- String encodedData = duplicatesData.valueAsString;
+ vmService.Instance duplicatesData = (await serviceClient.getObject(
+ isolateRef.id!, duplicatesDataRef.id!)) as vmService.Instance;
+ String encodedData = duplicatesData.valueAsString!;
try {
return parseEncodedLeakString(encodedData);
} catch (e) {
diff --git a/pkg/front_end/test/vm_service_heap_helper_test.dart b/pkg/front_end/test/vm_service_heap_helper_test.dart
index 8a7df50..3879d88 100644
--- a/pkg/front_end/test/vm_service_heap_helper_test.dart
+++ b/pkg/front_end/test/vm_service_heap_helper_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:async';
import 'dart:developer';
import 'dart:io';
@@ -144,9 +142,9 @@
Completer<List<String>> completer = new Completer<List<String>>();
LeakFinderTest({
- List<helper.Interest> interests,
- List<helper.Interest> prettyPrints,
- bool throwOnPossibleLeak,
+ required List<helper.Interest> interests,
+ required List<helper.Interest> prettyPrints,
+ required bool throwOnPossibleLeak,
}) : super(
interests: interests,
prettyPrints: prettyPrints,
diff --git a/pkg/front_end/test/vm_service_helper.dart b/pkg/front_end/test/vm_service_helper.dart
index c796b41..f2d3cb6 100644
--- a/pkg/front_end/test/vm_service_helper.dart
+++ b/pkg/front_end/test/vm_service_helper.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:convert";
import "dart:io";
@@ -14,7 +12,7 @@
export "package:vm_service/vm_service_io.dart";
class VMServiceHelper {
- vmService.VmService _serviceClient;
+ late vmService.VmService _serviceClient;
vmService.VmService get serviceClient => _serviceClient;
VMServiceHelper();
@@ -34,7 +32,7 @@
Future<bool> waitUntilPaused(String isolateId) async {
int nulls = 0;
while (true) {
- bool result = await isPaused(isolateId);
+ bool? result = await isPaused(isolateId);
if (result == null) {
nulls++;
if (nulls > 5) {
@@ -50,11 +48,11 @@
}
}
- Future<bool> isPaused(String isolateId) async {
+ Future<bool?> isPaused(String isolateId) async {
dynamic tmp = await _serviceClient.getIsolate(isolateId);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
- if (isolate.pauseEvent.kind != "Resume") return true;
+ if (isolate.pauseEvent!.kind != "Resume") return true;
return false;
}
return null;
@@ -64,7 +62,7 @@
dynamic tmp = await _serviceClient.getIsolate(isolateId);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
- return isolate.pauseEvent.kind == "PauseStart";
+ return isolate.pauseEvent!.kind == "PauseStart";
}
return false;
}
@@ -73,7 +71,7 @@
dynamic tmp = await _serviceClient.getIsolate(isolateId);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
- return isolate.pauseEvent.kind == "PauseExit";
+ return isolate.pauseEvent!.kind == "PauseExit";
}
return false;
}
@@ -91,13 +89,13 @@
rethrow;
}
if (allocationProfile.dateLastServiceGC != null &&
- allocationProfile.dateLastServiceGC >= expectGcAfter) {
+ allocationProfile.dateLastServiceGC! >= expectGcAfter) {
return allocationProfile;
}
}
}
- Future<bool> isIsolateRunnable(String isolateId) async {
+ Future<bool?> isIsolateRunnable(String isolateId) async {
dynamic tmp = await _serviceClient.getIsolate(isolateId);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
@@ -109,7 +107,7 @@
Future<void> waitUntilIsolateIsRunnable(String isolateId) async {
int nulls = 0;
while (true) {
- bool result = await isIsolateRunnable(isolateId);
+ bool? result = await isIsolateRunnable(isolateId);
if (result == null) {
nulls++;
if (nulls > 5) {
@@ -127,11 +125,11 @@
Future<String> getIsolateId() async {
vmService.VM vm = await _serviceClient.getVM();
- if (vm.isolates.length != 1) {
- throw "Expected 1 isolate, got ${vm.isolates.length}";
+ if (vm.isolates!.length != 1) {
+ throw "Expected 1 isolate, got ${vm.isolates!.length}";
}
- vmService.IsolateRef isolateRef = vm.isolates.single;
- return isolateRef.id;
+ vmService.IsolateRef isolateRef = vm.isolates!.single;
+ return isolateRef.id!;
}
}
@@ -150,14 +148,14 @@
}
abstract class LaunchingVMServiceHelper extends VMServiceHelper {
- Process _process;
+ late Process _process;
Process get process => _process;
bool _started = false;
Future<void> start(List<String> scriptAndArgs,
- {void stdoutReceiver(String line),
- void stderrReceiver(String line)}) async {
+ {void Function(String line)? stdoutReceiver,
+ void Function(String line)? stderrReceiver}) async {
if (_started) throw "Already started";
_started = true;
_process = await Process.start(
diff --git a/pkg/front_end/tool/generate_ast_coverage.dart b/pkg/front_end/tool/generate_ast_coverage.dart
index d3a299b..833e707 100644
--- a/pkg/front_end/tool/generate_ast_coverage.dart
+++ b/pkg/front_end/tool/generate_ast_coverage.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';
import 'ast_model.dart';
@@ -21,7 +19,7 @@
new File.fromUri(output).writeAsStringSync(result);
}
-Future<String> generateAstCoverage(Uri repoDir, [AstModel astModel]) async {
+Future<String> generateAstCoverage(Uri repoDir, [AstModel? astModel]) async {
astModel ??= await deriveAstModel(repoDir);
return generateVisitor(astModel, new CoverageVisitorStrategy());
}
@@ -47,7 +45,7 @@
@override
void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
- AstClass superAstClass = astClass.superclass;
+ AstClass? superAstClass = astClass.superclass;
while (superAstClass != null && !superAstClass.isInterchangeable) {
superAstClass = superAstClass.superclass;
}
@@ -61,13 +59,13 @@
@override
void handleVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {
- AstClass superAstClass = astClass.superclass;
+ AstClass? superAstClass = astClass.superclass;
while (superAstClass != null && !superAstClass.isInterchangeable) {
superAstClass = superAstClass.superclass;
}
if (superAstClass == astModel.constantClass) {
// Constants are only visited as references.
- String innerName = superAstClass.name;
+ String innerName = superAstClass!.name;
(nestedClassNames[innerName] ??= {}).add(astClass.name);
sb.writeln('''
visited.add(${innerName}Kind.${astClass.name});
diff --git a/pkg/test_runner/lib/src/compiler_configuration.dart b/pkg/test_runner/lib/src/compiler_configuration.dart
index cb968a9..a32b394 100644
--- a/pkg/test_runner/lib/src/compiler_configuration.dart
+++ b/pkg/test_runner/lib/src/compiler_configuration.dart
@@ -828,6 +828,9 @@
if (_configuration.useQemu) '--no-use-integer-division',
if (arguments.contains('--print-flow-graph-optimized'))
'--redirect-isolate-log-to=$tempDir/out.il',
+ if (arguments.contains('--print-flow-graph-optimized') &&
+ (_configuration.isMinified || arguments.contains('--obfuscate')))
+ '--save-obfuscation_map=$tempDir/renames.json',
..._replaceDartFiles(arguments, tempKernelFile(tempDir)),
];
@@ -844,6 +847,7 @@
var args = [
arguments.firstWhere((arg) => arg.endsWith('_il_test.dart')),
'$tempDir/out.il',
+ if (arguments.contains('--obfuscate')) '$tempDir/renames.json',
];
return CompilationCommand('compare_il', tempDir, bootstrapDependencies(),
@@ -965,9 +969,10 @@
List<String> computeCompilerArguments(
TestFile testFile, List<String> vmOptions, List<String> args) {
return [
- if (testFile.ilMatches.isNotEmpty) ...[
+ if (testFile.isVmIntermediateLanguageTest) ...[
'--print-flow-graph-optimized',
- '--print-flow-graph-filter=${testFile.ilMatches.join(',')}'
+ '--print-flow-graph-as-json',
+ '--print-flow-graph-filter=@pragma',
],
if (_enableAsserts) '--enable_asserts',
...filterVmOptions(vmOptions),
diff --git a/pkg/test_runner/lib/src/test_file.dart b/pkg/test_runner/lib/src/test_file.dart
index 8b61d68..3c71e5b 100644
--- a/pkg/test_runner/lib/src/test_file.dart
+++ b/pkg/test_runner/lib/src/test_file.dart
@@ -211,10 +211,7 @@
throw FormatException('Unknown feature "$name" in test $filePath');
});
- var ilMatches = filePath.endsWith('_il_test.dart')
- ? _parseStringOption(filePath, contents, r'MatchIL\[AOT\]',
- allowMultiple: true)
- : const <String>[];
+ final isVmIntermediateLanguageTest = filePath.endsWith('_il_test.dart');
// VM options.
var vmOptions = <List<String>>[];
@@ -341,7 +338,7 @@
sharedObjects: sharedObjects,
otherResources: otherResources,
experiments: experiments,
- ilMatches: ilMatches);
+ isVmIntermediateLanguageTest: isVmIntermediateLanguageTest);
}
/// A special fake test file for representing a VM unit test written in C++.
@@ -363,7 +360,7 @@
sharedObjects = [],
otherResources = [],
experiments = [],
- ilMatches = [],
+ isVmIntermediateLanguageTest = false,
super(null, null, []);
TestFile._(Path suiteDirectory, Path path, List<StaticError> expectedErrors,
@@ -384,7 +381,7 @@
this.sharedObjects,
this.otherResources,
this.experiments,
- this.ilMatches = const <String>[]})
+ this.isVmIntermediateLanguageTest = false})
: super(suiteDirectory, path, expectedErrors) {
assert(!isMultitest || dartOptions.isEmpty);
}
@@ -403,6 +400,7 @@
final bool hasRuntimeError;
final bool hasStaticWarning;
final bool hasCrash;
+ final bool isVmIntermediateLanguageTest;
/// The features that a test configuration must support in order to run this
/// test.
@@ -411,9 +409,6 @@
/// requirements, the test is implicitly skipped.
final List<Feature> requirements;
- /// List of functions which will have their IL verified (in AOT mode).
- final List<String> ilMatches;
-
final List<String> sharedOptions;
final List<String> dartOptions;
final List<String> dart2jsOptions;
@@ -479,6 +474,7 @@
final bool hasStaticWarning;
final bool hasSyntaxError;
bool get hasCrash => _origin.hasCrash;
+ bool get isVmIntermediateLanguageTest => _origin.isVmIntermediateLanguageTest;
_MultitestFile(this._origin, Path path, this.multitestKey,
List<StaticError> expectedErrors,
@@ -493,7 +489,6 @@
String get packages => _origin.packages;
List<Feature> get requirements => _origin.requirements;
- List<String> get ilMatches => _origin.ilMatches;
List<String> get dart2jsOptions => _origin.dart2jsOptions;
List<String> get dartOptions => _origin.dartOptions;
List<String> get ddcOptions => _origin.ddcOptions;
diff --git a/pkg/vm/bin/compare_il.dart b/pkg/vm/bin/compare_il.dart
index 9d9febd..8196a5e 100644
--- a/pkg/vm/bin/compare_il.dart
+++ b/pkg/vm/bin/compare_il.dart
@@ -5,185 +5,157 @@
// This is a helper script which performs IL matching for AOT IL tests.
// See runtime/docs/infra/il_tests.md for more information.
+import 'dart:collection';
+import 'dart:convert';
import 'dart:io';
+import 'dart:mirrors';
-void main(List<String> args) {
- if (args.length != 2) {
- throw 'Usage: compare_il <*_il_test.dart> <output.il>';
+import 'package:collection/collection.dart';
+
+import 'package:vm/testing/il_matchers.dart';
+
+void main(List<String> args) async {
+ getName = MirrorSystem.getName;
+
+ if (args.length < 2 || args.length > 3) {
+ throw 'Usage: compare_il <*_il_test.dart> <output.il> [<renames.json>]';
}
final testFile = args[0];
final ilFile = args[1];
+ final renamesFile = args.length == 3 ? args[2] : null;
- final graphs = _extractGraphs(ilFile);
+ final rename = _loadRenames(renamesFile);
+ final graphs = _loadGraphs(ilFile, rename);
+ final tests = await _loadTestCases(testFile);
- final expectations = _extractExpectations(testFile);
+ Map<String, FlowGraph> findMatchingGraphs(String name) {
+ final suffix = '_${rename(name)}';
+ return graphs.entries.firstWhere((f) => f.key.contains(suffix)).value;
+ }
- for (var expectation in expectations.entries) {
- // Find a graph for this expectation. We expect that function names are
- // unique enough to identify a specific graph.
- final graph =
- graphs.entries.singleWhere((e) => e.key.contains(expectation.key));
-
- // Extract the list of opcodes, ignoring irrelevant things like
- // ParallelMove.
- final gotOpcodesIgnoringMoves = graph.value
- .where((instr) => instr.opcode != 'ParallelMove')
- .map((instr) => instr.opcode)
- .toList();
-
- // Check that expectations are the prefix of gotOpcodesIgnoringMoves.
- print('Matching ${graph.key}');
- for (var i = 0; i < expectation.value.length; i++) {
- final gotOpcode = gotOpcodesIgnoringMoves[i];
- final expectedOpcode = expectation.value[i];
- if (gotOpcode != expectedOpcode) {
- throw 'Failed to match graph of ${graph.key} to '
- 'expectations for ${expectation.key} at instruction ${i}: '
- 'got ${gotOpcode} expected ${expectedOpcode}';
- }
- }
- print('... ok');
+ for (var test in tests) {
+ test.run(findMatchingGraphs(test.name));
}
exit(0); // Success.
}
-// IL instruction extracted from flow graph dump.
-class Instruction {
- final String raw;
+class TestCase {
+ final String name;
+ final String phasesFilter;
+ final LibraryMirror library;
- Instruction(this.raw);
+ late final phases =
+ phasesFilter.split(',').expand(_expandPhasePattern).toList();
- String get opcode {
- final match = instructionPattern.firstMatch(raw)!;
- final op = match.namedGroup('opcode')!;
- final blockType = match.namedGroup('block_type');
+ TestCase({
+ required this.name,
+ required this.phasesFilter,
+ required this.library,
+ });
- // Handle blocks which look like "B%d[%s]".
- if (blockType != null) {
- return blockTypes[blockType]!;
- }
-
- // Handle parallel moves specially.
- if (op.startsWith('ParallelMove')) {
- return 'ParallelMove';
- }
-
- // Handle branches.
- if (op.startsWith(branchIfPrefix)) {
- return 'Branch(${op.substring(branchIfPrefix.length)})';
- }
-
- // Normal instruction.
- return op;
+ void run(Map<String, FlowGraph> graphs) {
+ print('matching IL (${phases.join(', ')}) for $name');
+ library.invoke(MirrorSystem.getSymbol('matchIL\$$name'),
+ phases.map((phase) => graphs[phase]!).toList());
+ print('... ok');
}
- @override
- String toString() => 'Instruction($opcode)';
+ /// Parses phase filter components (same format as --compiler-passes flag).
+ static List<String> _expandPhasePattern(String pattern) {
+ bool printBefore = false, printAfter = false;
+ switch (pattern[0]) {
+ case '[':
+ printBefore = true;
+ break;
+ case ']':
+ printAfter = true;
+ break;
+ case '*':
+ printBefore = printAfter = true;
+ break;
+ }
- static final instructionPattern = RegExp(
- r'^\s*\d+:\s+(v\d+ <- )?(?<opcode>[^:[(]+(?<block_type>\[[\w ]+\])?)');
+ final phaseName =
+ (printBefore || printAfter) ? pattern.substring(1) : pattern;
- static const blockTypes = {
- '[join]': 'JoinEntry',
- '[target]': 'TargetEntry',
- '[graph]': 'GraphEntry',
- '[function entry]': 'FunctionEntry'
- };
+ if (!printBefore && !printAfter) {
+ printAfter = true;
+ }
- static const branchIfPrefix = 'Branch if ';
+ return [
+ if (printBefore) 'Before $phaseName',
+ if (printAfter) 'After $phaseName',
+ ];
+ }
}
-Map<String, List<Instruction>> _extractGraphs(String ilFile) {
- final graphs = <String, List<Instruction>>{};
+/// Extracts test cases from the given file by looking for functions
+/// marked with @pragma('vm:testing:print-flow-graph', ...).
+Future<Set<TestCase>> _loadTestCases(String testFile) async {
+ final mirrorSystem = currentMirrorSystem();
+ final library =
+ await mirrorSystem.isolate.loadUri(File(testFile).absolute.uri);
- final reader = LineReader(ilFile);
+ pragma? getPragma(DeclarationMirror decl, String name) => decl.metadata
+ .map((m) => m.reflectee)
+ .whereType<pragma>()
+ .firstWhereOrNull((p) => p.name == name);
- var instructions = <Instruction>[];
- while (reader.hasMore) {
- if (reader.testNext('*** BEGIN CFG')) {
- reader.next(); // Skip phase name.
- final functionName = reader.next();
- while (!reader.testNext('*** END CFG')) {
- var curr = reader.next();
+ final cases = LinkedHashSet<TestCase>(
+ equals: (a, b) => a.name == b.name,
+ hashCode: (a) => a.name.hashCode,
+ );
- // If instruction line ends with '{' search for a matching '}' (it will
- // be on its own line).
- if (curr.endsWith('{')) {
- do {
- curr += '\n' + reader.current;
- } while (reader.next() != '}');
- }
-
- instructions.add(Instruction(curr));
- }
-
- graphs[functionName] = instructions;
- instructions = <Instruction>[];
- } else {
- reader.next();
+ void processDeclaration(DeclarationMirror decl) {
+ final p = getPragma(decl, 'vm:testing:print-flow-graph');
+ if (p != null) {
+ final name = MirrorSystem.getName(decl.simpleName);
+ final added = cases.add(TestCase(
+ name: name,
+ phasesFilter: (p.options as String?) ?? 'AllocateRegisters',
+ library: library,
+ ));
+ if (!added) throw 'duplicate test case with name $name';
}
}
+ for (var decl in library.declarations.values) {
+ if (decl is ClassMirror) {
+ decl.declarations.values.forEach(processDeclaration);
+ } else {
+ processDeclaration(decl);
+ }
+ }
+
+ return cases;
+}
+
+Map<String, Map<String, FlowGraph>> _loadGraphs(String ilFile, Renamer rename) {
+ final graphs = <String, Map<String, FlowGraph>>{};
+
+ for (var graph in File(ilFile).readAsLinesSync()) {
+ final m = jsonDecode(graph) as Map<String, dynamic>;
+ graphs.putIfAbsent(m['f'], () => {})[m['p']] =
+ FlowGraph(m['b'], m['desc'], rename: rename);
+ }
+
return graphs;
}
-Map<String, List<String>> _extractExpectations(String testFile) {
- final expectations = <String, List<String>>{};
-
- final reader = LineReader(testFile);
-
- final matchILPattern = RegExp(r'^// MatchIL\[AOT\]=(?<value>.*)$');
- final matcherPattern = RegExp(r'^// __ (?<value>.*)$');
-
- var matchers = <String>[];
- while (reader.hasMore) {
- var functionName = reader.matchNext(matchILPattern);
- if (functionName != null) {
- // Read comment block which follows `// MatchIL[AOT]=...`.
- while (reader.hasMore && reader.current.startsWith('//')) {
- final match = matcherPattern.firstMatch(reader.next());
- if (match != null) {
- matchers.add(match.namedGroup('value')!);
- }
- }
- expectations[functionName] = matchers;
- matchers = <String>[];
- } else {
- reader.next();
- }
+Renamer _loadRenames(String? renamesFile) {
+ // Load renames map if present.
+ if (renamesFile == null) {
+ return (v) => v;
}
- return expectations;
-}
+ final list =
+ (jsonDecode(File(renamesFile).readAsStringSync()) as List).cast<String>();
-class LineReader {
- final List<String> lines;
- int lineno = 0;
+ final renamesMap = <String, String>{
+ for (var i = 0; i < list.length; i += 2) list[i]: list[i + 1],
+ };
- LineReader(String path) : lines = File(path).readAsLinesSync();
-
- String get current => lines[lineno];
-
- bool get hasMore => lineno < lines.length;
-
- String next() {
- final curr = current;
- lineno++;
- return curr;
- }
-
- bool testNext(String expected) {
- if (current == expected) {
- next();
- return true;
- }
- return false;
- }
-
- String? matchNext(RegExp pattern) {
- final m = pattern.firstMatch(current);
- return m?.namedGroup('value');
- }
+ return (v) => renamesMap[v] ?? v;
}
diff --git a/pkg/vm/lib/testing/il_matchers.dart b/pkg/vm/lib/testing/il_matchers.dart
new file mode 100644
index 0000000..dd5eb8f
--- /dev/null
+++ b/pkg/vm/lib/testing/il_matchers.dart
@@ -0,0 +1,430 @@
+// 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.
+
+/// A library to facilitate programmatic matching against flow graphs
+/// collected during IL tests. See runtime/docs/infra/il_tests.md for more
+/// info.
+
+typedef Renamer = String Function(String);
+
+/// Flow graph parsed from --print-flow-graph-as-json output.
+class FlowGraph {
+ final List<dynamic> blocks;
+ final Map<String, InstructionDescriptor> descriptors;
+ final Renamer rename;
+
+ FlowGraph(this.blocks, Map<String, dynamic> desc, {required this.rename})
+ : descriptors = {
+ for (var e in desc.entries)
+ e.key: InstructionDescriptor.fromJson(e.value)
+ };
+
+ /// Match the sequence of blocks in this flow graph against the given
+ /// sequence of matchers: `expected[i]` is expected to match `blocks[i]`,
+ /// but there can be more blocks in the graph than matchers (the suffix is
+ /// ignored).
+ ///
+ /// If [env] is provided it will be used as matching environment, otherwise
+ /// a fresh instance of [Env] will be created and used.
+ ///
+ /// This function returns the populated matching environment.
+ Env match(List<Matcher> expected, {Env? env}) {
+ env ??= Env(rename: rename, descriptors: descriptors);
+
+ for (var i = 0; i < expected.length; i++) {
+ expected[i].match(env, blocks[i]).expectMatched('failed to match');
+ }
+
+ return env;
+ }
+}
+
+class InstructionDescriptor {
+ final List<String> attributes;
+ final Map<String, int> attributeIndex;
+
+ InstructionDescriptor.fromJson(List<dynamic> attrs)
+ : this._(attrs.map((v) => _demangle(v)).toList());
+
+ InstructionDescriptor._(List<String> attrs)
+ : attributes = attrs.cast<String>(),
+ attributeIndex = {for (var i = 0; i < attrs.length; i++) attrs[i]: i};
+
+ static String _demangle(String v) {
+ final prefixLen = v.startsWith('&') ? 1 : 0;
+ final suffixLen = v.endsWith('()') ? 2 : 0;
+ return v.substring(prefixLen, v.length - suffixLen);
+ }
+}
+
+/// Matching environment.
+///
+/// This is fundamentally just a name to id mapping which allows to track
+/// correspondence between names given to some matchers and blocks/instructions
+/// which matched those matchers.
+///
+/// This object is also used to carry around auxiliary information which might
+/// be needed for matching, e.g. [Renamer].
+class Env {
+ final Map<String, InstructionDescriptor> descriptors;
+ final Renamer rename;
+ final Map<String, int> nameToId = {};
+
+ Env({required this.rename, required this.descriptors});
+
+ void bind(String name, Map<String, dynamic> instrOrBlock) {
+ final id = instrOrBlock['v'] ?? instrOrBlock['b'];
+
+ if (nameToId.containsKey(name) && nameToId[name] != id) {
+ throw 'Binding mismatch for $name: got ${nameToId[name]} and $id';
+ }
+
+ nameToId[name] = id;
+ }
+}
+
+abstract class Matcher {
+ /// Try matching this matcher against the given value. Returns
+ /// [MatchStatus.matched] if match succeeded and an instance of
+ /// [MatchStatus.fail] otherwise.
+ MatchStatus match(Env e, dynamic v);
+}
+
+class MatchStatus {
+ final String? message;
+
+ const MatchStatus._matched() : message = null;
+ const MatchStatus.fail(String message) : message = message;
+
+ bool get isMatch => message == null;
+ bool get isFail => message != null;
+
+ static const MatchStatus matched = MatchStatus._matched();
+
+ void expectMatched(String s) {
+ if (message != null) {
+ throw 'Failed to match: $message';
+ }
+ }
+}
+
+/// Matcher which always succeeds.
+class _AnyMatcher implements Matcher {
+ const _AnyMatcher();
+
+ @override
+ MatchStatus match(Env e, v) => MatchStatus.matched;
+
+ @override
+ String toString() {
+ return '*';
+ }
+}
+
+/// Matcher which updates matching environment when it succeeds.
+class _BoundMatcher implements Matcher {
+ final String name;
+ final Matcher nested;
+
+ _BoundMatcher(this.name, this.nested);
+
+ @override
+ MatchStatus match(Env e, dynamic v) {
+ final result = nested.match(e, v);
+ if (result.isMatch) {
+ e.bind(name, v);
+ }
+ return result;
+ }
+
+ @override
+ String toString() {
+ return '$name <- $nested';
+ }
+}
+
+/// Matcher which matches a specified value [v].
+class _EqualsMatcher implements Matcher {
+ final dynamic v;
+
+ _EqualsMatcher(this.v);
+
+ @override
+ MatchStatus match(Env e, v) {
+ if (this.v == v) {
+ return MatchStatus.matched;
+ }
+
+ // Some instructions refer to obfuscated names, try to rename
+ // the expectation and try again.
+ if (this.v is String && v is String && e.rename(this.v) == v) {
+ return MatchStatus.matched;
+ }
+
+ return this.v == v
+ ? MatchStatus.matched
+ : MatchStatus.fail('expected ${this.v} got $v');
+ }
+
+ @override
+ String toString() => '$v';
+}
+
+/// Matcher which matches the value which is equivalent to the binding
+/// with the given [name] in the matching environment.
+///
+/// If this matcher is [binding] then it will populate the binding in the
+/// matching environment if the [name] is not bound yet on the first call
+/// to [match]. Otherwise if the [name] is not bound when [match] is called
+/// an exception will be thrown.
+///
+/// Binding matchers are used when we might see the use of a value before its
+/// definition (e.g. we usually use the name of the block in the `Goto` or
+/// `Branch` before we see the block itself).
+class _RefMatcher implements Matcher {
+ final String name;
+ final bool binding;
+
+ _RefMatcher(this.name, {this.binding = false});
+
+ @override
+ MatchStatus match(Env e, v) {
+ if (e.nameToId.containsKey(name)) {
+ return e.nameToId[name] == v
+ ? MatchStatus.matched
+ : MatchStatus.fail(
+ 'expected $name to bind to ${e.nameToId[name]} but got $v');
+ }
+
+ if (!binding) {
+ throw UnimplementedError('Unbound reference to ${name}');
+ }
+
+ e.nameToId[name] = v;
+ return MatchStatus.matched;
+ }
+
+ @override
+ String toString() {
+ return name;
+ }
+}
+
+/// A wrapper which matches a list of matchers against a list of values.
+class _ListMatcher implements Matcher {
+ final List<Matcher> expected;
+
+ _ListMatcher(this.expected);
+
+ @override
+ MatchStatus match(Env e, dynamic got) {
+ if (got is! List) {
+ return MatchStatus.fail('expected List, got ${got.runtimeType}');
+ }
+
+ if (expected.length > got.length) {
+ return MatchStatus.fail(
+ 'expected at least ${expected.length} elements got ${got.length}');
+ }
+
+ for (var i = 0; i < expected.length; i++) {
+ final result = expected[i].match(e, got[i]);
+ if (result.isFail) {
+ return MatchStatus.fail(
+ 'mismatch at index ${i}, expected ${expected[i]} '
+ 'got ${got[i]}: ${result.message}');
+ }
+ }
+
+ if (expected.last is _AnyMatcher || expected.length == got.length) {
+ return MatchStatus.matched;
+ }
+
+ return MatchStatus.fail(
+ 'expected exactly ${expected.length} elements got ${got.length}');
+ }
+
+ @override
+ String toString() => '[${expected.join(',')}]';
+}
+
+/// A matcher which matches a block of the specified [kind] and contents.
+///
+/// Contents are specified as a sequence of matchers ([body]). For each of
+/// those matchers a matching block is expected to contain at least one
+/// instruction that matches it. Matching is done in order: first we scan
+/// the block until we find the match for the first matcher in body, then
+/// we continue scanning until we find the match for the second and so on.
+class _BlockMatcher implements Matcher {
+ final String kind;
+ final List<Matcher> body;
+
+ _BlockMatcher(this.kind, [this.body = const []]);
+
+ @override
+ MatchStatus match(Env e, covariant Map<String, dynamic> block) {
+ if (block['o'] != '${kind}Entry') {
+ return MatchStatus.fail(
+ 'Expected block of kind ${kind} got ${block['o']} '
+ 'when matching B${block['b']}');
+ }
+
+ final gotBody = [...?block['d'], ...?block['is']];
+
+ var matcherIndex = 0;
+ for (int i = 0; i < gotBody.length && matcherIndex < body.length; i++) {
+ if (body[matcherIndex].match(e, gotBody[i]).isMatch) {
+ matcherIndex++;
+ }
+ }
+ if (matcherIndex != body.length) {
+ return MatchStatus.fail('Unmatched instruction: ${body[matcherIndex]} '
+ 'in block B${block['b']}');
+ }
+ return MatchStatus.matched;
+ }
+}
+
+/// A matcher for instruction's named attributes.
+///
+/// Attributes are resolved to their indices through [Env.descriptors].
+class _AttributesMatcher implements Matcher {
+ final String op;
+ final Map<String, Matcher> matchers;
+
+ _ListMatcher? impl;
+
+ _AttributesMatcher(this.op, this.matchers);
+
+ @override
+ MatchStatus match(Env e, dynamic v) {
+ impl ??= _ListMatcher(e.descriptors[op]!.attributes
+ .map((name) => matchers[name] ?? const _AnyMatcher())
+ .toList());
+ return impl!.match(e, v);
+ }
+}
+
+/// Matcher which matches an instruction with opcode [op] and properties
+/// specified in [matchers] map.
+class InstructionMatcher implements Matcher {
+ final String op;
+ final Map<String, Matcher> matchers;
+
+ InstructionMatcher(
+ {required String op, List<Matcher>? data, List<Matcher>? inputs})
+ : this._(op: op, matchers: {
+ if (data != null) 'd': _ListMatcher(data),
+ if (inputs != null) 'i': _ListMatcher(inputs),
+ });
+
+ InstructionMatcher._({
+ required this.op,
+ required this.matchers,
+ });
+
+ @override
+ MatchStatus match(Env e, covariant Map<String, dynamic> instr) {
+ if (instr['o'] != op) {
+ return MatchStatus.fail('expected instruction ${op} got ${instr['o']}');
+ }
+
+ for (var entry in matchers.entries) {
+ final result = entry.value.match(e, instr[entry.key]);
+ if (result.isFail) {
+ return result;
+ }
+ }
+
+ return MatchStatus.matched;
+ }
+
+ @override
+ String toString() {
+ return '$op($matchers)';
+ }
+}
+
+/// This class uses `noSuchMethod` to allow writing code like
+///
+/// ```
+/// match.Op(in0, ..., inN, attr0: a0, ..., attrK: aK)
+/// ```
+///
+/// This will produce an instruction matcher which matches opcode `Op` and
+/// expects `in0, ..., inN` to match instructions inputs, while `a0, ...`
+/// matchers are expected to match attributes with names `attr0, ...`.
+class Matchers {
+ _BlockMatcher block(String kind, [List<dynamic> body = const []]) {
+ return _BlockMatcher(kind, List<Matcher>.from(body));
+ }
+
+ final _AnyMatcher any = const _AnyMatcher();
+
+ InstructionMatcher Goto(String dest) =>
+ InstructionMatcher._(op: 'Goto', matchers: {
+ 's': _ListMatcher([_blockRef(dest)])
+ });
+
+ InstructionMatcher Branch(InstructionMatcher compare,
+ {String? ifTrue, String? ifFalse}) =>
+ InstructionMatcher._(op: 'Branch', matchers: {
+ 'cc': compare,
+ 's': _ListMatcher([
+ ifTrue != null ? _blockRef(ifTrue) : any,
+ ifFalse != null ? _blockRef(ifFalse) : any,
+ ]),
+ });
+
+ @override
+ Object? noSuchMethod(Invocation invocation) {
+ final data = {
+ for (var e in invocation.namedArguments.entries)
+ getName(e.key): Matchers._toAttributeMatcher(e.value),
+ };
+ final inputs =
+ invocation.positionalArguments.map(Matchers._toInputMatcher).toList();
+ final op = getName(invocation.memberName);
+ return InstructionMatcher._(op: op, matchers: {
+ if (data.isNotEmpty) 'd': _AttributesMatcher(op, data),
+ if (inputs.isNotEmpty) 'i': _ListMatcher(inputs),
+ });
+ }
+
+ static Matcher _blockRef(String name) => _RefMatcher(name, binding: true);
+
+ static Matcher _toAttributeMatcher(dynamic v) {
+ if (v is Matcher) {
+ return v;
+ } else {
+ return _EqualsMatcher(v);
+ }
+ }
+
+ static Matcher _toInputMatcher(dynamic v) {
+ if (v is Matcher) {
+ return v;
+ } else if (v is String) {
+ return _RefMatcher(v);
+ } else {
+ throw ArgumentError.value(
+ v, 'v', 'Expected either a Matcher or a String (binding name)');
+ }
+ }
+}
+
+/// Extension which enables `'name' << matcher` syntax for creating bound
+/// matchers.
+extension BindingExtension on String {
+ Matcher operator <<(Matcher matcher) {
+ return _BoundMatcher(this, matcher);
+ }
+}
+
+final dynamic match = Matchers();
+
+/// This file should not depend on dart:mirrors because it is imported into
+/// tests, which are compiled in AOT mode. So instead we let compare_il driver
+/// set this field.
+late String Function(Symbol) getName;
diff --git a/pkg/vm/tool/compare_il b/pkg/vm/tool/compare_il
index 65ad55e..4973042 100755
--- a/pkg/vm/tool/compare_il
+++ b/pkg/vm/tool/compare_il
@@ -26,10 +26,6 @@
# TODO(kustermann): For windows as well as for hosts running on arm, our
# checked-in dart binaries must be adjusted.
-if [[ `uname` == 'Darwin' ]]; then
- DART="$SDK_DIR/tools/sdks/dart-sdk/bin/dart"
-else
- DART="$SDK_DIR/tools/sdks/dart-sdk/bin/dart"
-fi
+DART="$SDK_DIR/tools/sdks/dart-sdk/bin/dart"
exec "$DART" $DART_VM_FLAGS "${SDK_DIR}/pkg/vm/bin/compare_il.dart" $@
diff --git a/runtime/docs/infra/il_tests.md b/runtime/docs/infra/il_tests.md
index 8400b6e..94c4500 100644
--- a/runtime/docs/infra/il_tests.md
+++ b/runtime/docs/infra/il_tests.md
@@ -11,76 +11,47 @@
IL tests are placed in files ending with `_il_test.dart`.
-Each IL test should contain one or more _IL matching blocks_, which have the
-following format:
+Each IL test should contain one or more of the functions marked with a
+`@pragma('vm:testing:print-flow-graph'[, 'phases filter'])`.
+
+These functions will have their IL dumped at points specified by the
+_phases filter_ (if present, `]AllocateRegisters` by default), which follows
+the same syntax as `--compiler-passes=` flag and dumped IL will be compared
+against the expectations, which are specified programmatically using
+`package:vm/testing/il_matchers.dart` helpers. A function named `foo` has
+its IL expectations in the function called `matchIL$foo` in the same file.
```dart
-// MatchIL[AOT]=functionName
-// comment
-// __ op
-// comment
-// __ op
-// __ op
-// __ op
+import 'package:vm/testing/il_matchers.dart';
+
+@pragma('vm:testing:print-flow-graph')
+void foo() {
+}
+
+/// Expectations for [foo].
+void matchIL$foo(FlowGraph graph) {
+ graph.match([/* expectations */]);
+}
```
-Each section starts with a `// MatchIL[AOT]=functionName` line which contains
-the name (or a substring of a name) of the function for which IL should be
-matched.
-
-`// MatchIL[AOT]=...` line is followed by some number of comment lines `//`,
-where lines starting with `// __ ` specify _an instruction matcher_ and the rest
-are ignored (they just act as normal comments).
-
-`gen_snapshot` will be instructed (via `--print-flow-graph-optimized` and
-`--print-flow-graph-filter=functionName,...` flags) to dump IL for all
-functions names specified in IL matching blocks.
-
-After that `pkg/vm/tool/compare_il` script will be used to compare the dumps
-to actual expectations: by checking that dumped flow graph starts with the
-expected sequence of commands (ignoring some instructions like `ParallelMove`).
+Actual matching is done by the `pkg/vm/tool/compare_il` script.
## Example
```dart
-// MatchIL[AOT]=factorial
-// __ GraphEntry
-// __ FunctionEntry
-// __ CheckStackOverflow
-// __ Branch(EqualityCompare)
@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph')
int factorial(int value) => value == 1 ? value : value * factorial(value - 1);
-```
-This test specifies that the graph for `factorial` should start with a sequence
-`GraphEntry`, `FunctionEntry`, `CheckStackOverflow`, `Branch(EqualityCompare)`.
-
-If the graph has a different shape the test will fail, e.g. given the graph
-
-```
-*** BEGIN CFG
-After AllocateRegisters
-==== file:///.../src/dart/sdk/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart_::_factorial (RegularFunction)
- 0: B0[graph]:0 {
- v3 <- Constant(#1) [1, 1] T{_Smi}
- v19 <- UnboxedConstant(#1 int64) T{_Smi}
+void matchIL$factorial(FlowGraph graph) {
+ // Expected a graph which starts with GraphEntry block followed by a
+ // FunctionEntry block. FunctionEntry block should contain a Branch()
+ // instruction, with EqualityCompare as a comparison.
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '==')),
+ ]),
+ ]);
}
- 2: B1[function entry]:2 {
- v2 <- Parameter(0) [-9223372036854775808, 9223372036854775807] T{int}
-}
- 4: CheckStackOverflow:8(stack=0, loop=0)
- 5: ParallelMove rcx <- S+2
- 6: v17 <- BoxInt64(v2) [-9223372036854775808, 9223372036854775807] T{int}
- 7: ParallelMove rax <- rax
- 8: Branch if StrictCompare(===, v17 T{int}, v3) T{bool} goto (3, 4)
-```
-
-we will get:
-
-```
-Unhandled exception:
-Failed to match graph of ==== file:///.../src/dart/sdk/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart_::_factorial (RegularFunction) to expectations for factorial at instruction 3: got BoxInt64 expected Branch(EqualityCompare)
-#0 main (file:///.../src/dart/sdk/pkg/vm/bin/compare_il.dart:37:9)
-#1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:285:32)
-#2 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:187:12)
-```
+```
\ No newline at end of file
diff --git a/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart b/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart
index 9baf000..8447554 100644
--- a/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart
+++ b/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart
@@ -5,14 +5,21 @@
// Test that we emit EqualityCompare rather than StrictCompare+BoxInt64
// when comparing non-nullable integer to a Smi.
-// MatchIL[AOT]=factorial
-// __ GraphEntry
-// __ FunctionEntry
-// __ CheckStackOverflow
-// __ Branch(EqualityCompare)
+import 'package:vm/testing/il_matchers.dart';
+
@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph')
int factorial(int value) => value == 1 ? value : value * factorial(value - 1);
+void matchIL$factorial(FlowGraph graph) {
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '==')),
+ ]),
+ ]);
+}
+
void main() {
print(factorial(4));
}
diff --git a/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart b/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart
new file mode 100644
index 0000000..722da15
--- /dev/null
+++ b/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart
@@ -0,0 +1,718 @@
+// 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:vm/testing/il_matchers.dart';
+
+bool shouldPrint = false;
+
+@pragma('vm:never-inline')
+void blackhole(Object v) {
+ if (shouldPrint) {
+ print(v);
+ }
+}
+
+class A {}
+
+class A0 extends A {
+ final double x;
+ final String str;
+
+ A0(this.x, this.str);
+
+ // Use [x] to prevent the field from being shaken out. We would like
+ // [x] to occupy the same location that [A1.str] takes so that
+ // type confusion / incorrect LICM would cause a crash when we load
+ // `o.[A1.str].length` on an object of type [A0]
+ String toString() => 'A0($x)';
+}
+
+class A1 extends A {
+ final String str;
+
+ A1(this.str);
+}
+
+class H<T> {
+ final T data;
+ H(this.data);
+}
+
+abstract class B<T extends A> {
+ final T v;
+
+ B(this.v);
+
+ int load(H<T> h);
+ int loadWithNamedParam({required H<T> h});
+
+ @pragma('vm:never-inline')
+ @pragma('vm:testing:print-flow-graph', '*LICM')
+ int testNarrowingThroughThisCallWithPositionalParam(H<T> h) {
+ var result = 0;
+ for (var i = 0; i < 10; i++) {
+ // We will perform polymorphic inlining of `load` because `this`
+ // is known to be either B0 or B1. In both cases we will have
+ // v.str.length loads fully inlined because inlined bodies
+ // have precise type information for v.
+ // Then we will hoist v.str.length out of the loop past
+ // class-id comparisons generated by the inlining leading
+ // to incorrect code which will crash.
+ result += load(h);
+ }
+ return result;
+ }
+
+ @pragma('vm:never-inline')
+ @pragma('vm:testing:print-flow-graph', '*LICM')
+ int testNarrowingThroughThisCallWithNamedParams(H<T> h) {
+ var result = 0;
+ for (var i = 0; i < 10; i++) {
+ // We will perform polymorphic inlining of `load` because `this`
+ // is known to be either B0 or B1. In both cases we will have
+ // v.str.length loads fully inlined because inlined bodies
+ // have precise type information for v.
+ // Then we will hoist v.str.length out of the loop past
+ // class-id comparisons generated by the inlining leading
+ // to incorrect code which will crash.
+ result += loadWithNamedParam(h: h);
+ }
+ return result;
+ }
+}
+
+class BImpl<T extends A> extends B<T> {
+ BImpl(T v) : super(v);
+
+ // These methods do not matter. They can just return 0.
+ int load(H<T> h) => 0;
+ int loadWithNamedParam({required H<T> h}) => 0;
+}
+
+class B0 extends B<A0> {
+ B0(A0 a) : super(a);
+
+ int load(H<A0> h) {
+ return h.data.str.length;
+ }
+
+ int loadWithNamedParam({String? a, required H<A0> h, String? z}) {
+ return h.data.str.length;
+ }
+}
+
+class B1 extends B<A1> {
+ B1(A1 a) : super(a);
+
+ int load(H<A1> h) {
+ return h.data.str.length;
+ }
+
+ int loadWithNamedParam({String? a, required H<A1> h, String? z}) {
+ return h.data.str.length;
+ }
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIsCheckOnSubclass(B b) {
+ int sum = 0;
+ b.v.toString();
+ for (var i = 0; i < 2; i++) {
+ if (b is B1) {
+ sum += b.v.str.length;
+ }
+ }
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIsCheckWithTypeArgPolymorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ if (b is B<A1>) {
+ sum += b.v.str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIsCheckWithTypeArgMonomorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ if (b is B<A1>) {
+ sum += b.v.str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughAsCheckWithTypeArgPolymorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ // Branch with a side-effect to avoid b as B<A1> hoisting.
+ if (sum == 42) throw '42';
+ sum += (b as B<A1>).v.str.length;
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ // Branch with a side-effect to avoid b as B<A1> hoisting.
+ if (sum == 42) throw '42';
+ sum += (sum == 22 ? b as B<A1> : b as B<A1>).v.str.length;
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIndexedLoadFromGrowableArray(List<A> l) {
+ int sum = 0;
+ final v = l[0];
+ for (var i = 0; i < 2; i++) {
+ if (l is List<A1>) {
+ sum += l[0].str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIndexedLoadFromFixedArray(List<A> l) {
+ int sum = 0;
+ final v = l[0];
+ for (var i = 0; i < 2; i++) {
+ if (l is List<A1>) {
+ sum += l[0].str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+void main(List<String> args) {
+ shouldPrint = args.contains("shouldPrint");
+
+ // Prevent shaking of BImpl.load and BImpl.loadWithNamed, if these methods
+ // are shaked (because they are not used) that would inhibit polymorphic
+ // inlining at testNarrowingThroughThisCall{,WithNamedParams}
+ BImpl(A1("")).load(H(A1("")));
+ BImpl(A1("")).loadWithNamedParam(h: H(A1("")));
+
+ for (var i = 0; i < 2; i++) {
+ final a1 = A1(i.toString());
+ final a0 = A0(i.toDouble(), i.toString());
+ B1(a1).testNarrowingThroughThisCallWithPositionalParam(H(a1));
+ B0(a0).testNarrowingThroughThisCallWithPositionalParam(H(a0));
+ B1(a1).testNarrowingThroughThisCallWithNamedParams(H(a1));
+ B0(a0).testNarrowingThroughThisCallWithNamedParams(H(a0));
+ testNarrowingThroughIsCheckOnSubclass(B1(a1));
+ testNarrowingThroughIsCheckOnSubclass(B0(a0));
+ testNarrowingThroughIsCheckWithTypeArgPolymorphic(B1(a1));
+ testNarrowingThroughIsCheckWithTypeArgPolymorphic(B0(a0));
+ testNarrowingThroughIsCheckWithTypeArgMonomorphic(BImpl<A1>(a1));
+ testNarrowingThroughIsCheckWithTypeArgMonomorphic(BImpl<A0>(a0));
+ testNarrowingThroughAsCheckWithTypeArgPolymorphic(B1(a1));
+ try {
+ testNarrowingThroughAsCheckWithTypeArgPolymorphic(B0(a0));
+ throw "Should be unreachable";
+ } on TypeError catch (e) {}
+ testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(B1(a1));
+ try {
+ testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(B0(a0));
+ throw "Should be unreachable";
+ } on TypeError catch (e) {}
+ testNarrowingThroughIndexedLoadFromGrowableArray([a1]);
+ testNarrowingThroughIndexedLoadFromGrowableArray([a0]);
+ testNarrowingThroughIndexedLoadFromFixedArray(
+ List<A1>.filled(1, a1, growable: false));
+ testNarrowingThroughIndexedLoadFromFixedArray(
+ List<A0>.filled(1, a0, growable: false));
+ }
+}
+
+void matchIL$testNarrowingThroughThisCallWithPositionalParam(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'h_raw' << match.Parameter(index: 1),
+ 'h' << match.AssertAssignable('h_raw', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_cid' << match.LoadClassId('this'),
+ match.Branch(match.StrictCompare('this_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('this'),
+ // This redefinition was inserted by inlining.
+ 'h_' << match.Redefinition('h'),
+ 'v0' << match.LoadField('h_', slot: 'data'),
+ 'v1' << match.LoadField('v0', slot: 'str'),
+ 'v2' << match.LoadField('v1', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'h_raw' << match.Parameter(index: 1),
+ 'h' << match.AssertAssignable('h_raw', match.any),
+ 'this_cid' << match.LoadClassId('this'), // Hoisted.
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.StrictCompare('this_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ // After LICM redefinitions are removed.
+ 'v0' << match.LoadField('h', slot: 'data'),
+ 'v1' << match.LoadField('v0', slot: 'str'),
+ 'v2' << match.LoadField('v1', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughThisCallWithNamedParams(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ // Graph shape is basically the same.
+ matchIL$testNarrowingThroughThisCallWithPositionalParam(
+ beforeLICM, afterLICM);
+}
+
+void matchIL$testNarrowingThroughIsCheckOnSubclass(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'b_cid' << match.LoadClassId('b'),
+ match.Branch(match.StrictCompare('b_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('b'),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ 'b_cid' << match.LoadClassId('b'), // Hoisted.
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.StrictCompare('b_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughIsCheckWithTypeArgMonomorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'b_is_B<A1>' << match.InstanceOf('b', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('b_is_B<A1>', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('b'),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'b_is_B<A1>' << match.InstanceOf('b', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('b_is_B<A1>', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughAsCheckWithTypeArgPolymorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B5', ifFalse: 'B6'),
+ ]),
+ 'B5' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B6' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B7' <<
+ match.block('Join', [
+ // This redefinition was inserted by PhiInstr::Canonicalize
+ // which removed Phi of two AssertAssignables above.
+ match.Redefinition('b'),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B5', ifFalse: 'B6'),
+ ]),
+ 'B5' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B6' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B7' <<
+ match.block('Join', [
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughIndexedLoadFromGrowableArray(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a.data' << match.LoadField('this', slot: 'GrowableObjectArray.data'),
+ 'a.data[0]' << match.LoadIndexed('a.data', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a.data[0]*' << match.Phi('a.data[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('this'),
+ // This redefinition was inserted by load forwarding.
+ 'a.data[0]**' << match.Redefinition('a.data[0]*'),
+ 'a.data[0].str' << match.LoadField('a.data[0]**', slot: 'str'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a.data' << match.LoadField('this', slot: 'GrowableObjectArray.data'),
+ 'a.data[0]' << match.LoadIndexed('a.data', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a.data[0]*' << match.Phi('a.data[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'a.data[0].str' << match.LoadField('a.data[0]*', slot: 'str'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughIsCheckWithTypeArgPolymorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ matchIL$testNarrowingThroughIsCheckWithTypeArgMonomorphic(
+ beforeLICM, afterLICM);
+}
+
+void matchIL$testNarrowingThroughIndexedLoadFromFixedArray(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a[0]' << match.LoadIndexed('this', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a[0]*' << match.Phi('a[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('this'),
+ // This redefinition was inserted by load forwarding.
+ 'a[0]**' << match.Redefinition('a[0]*'),
+ 'a[0].str' << match.LoadField('a[0]**', slot: 'str'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a[0]' << match.LoadIndexed('this', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a[0]*' << match.Phi('a[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'a[0].str' << match.LoadField('a[0]*', slot: 'str'),
+ ]),
+ ], env: env);
+}
diff --git a/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart b/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart
index 9baf000..8447554 100644
--- a/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart
+++ b/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart
@@ -5,14 +5,21 @@
// Test that we emit EqualityCompare rather than StrictCompare+BoxInt64
// when comparing non-nullable integer to a Smi.
-// MatchIL[AOT]=factorial
-// __ GraphEntry
-// __ FunctionEntry
-// __ CheckStackOverflow
-// __ Branch(EqualityCompare)
+import 'package:vm/testing/il_matchers.dart';
+
@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph')
int factorial(int value) => value == 1 ? value : value * factorial(value - 1);
+void matchIL$factorial(FlowGraph graph) {
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '==')),
+ ]),
+ ]);
+}
+
void main() {
print(factorial(4));
}
diff --git a/runtime/tests/vm/dart_2/flutter_regress_91370_il_test.dart b/runtime/tests/vm/dart_2/flutter_regress_91370_il_test.dart
new file mode 100644
index 0000000..722da15
--- /dev/null
+++ b/runtime/tests/vm/dart_2/flutter_regress_91370_il_test.dart
@@ -0,0 +1,718 @@
+// 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:vm/testing/il_matchers.dart';
+
+bool shouldPrint = false;
+
+@pragma('vm:never-inline')
+void blackhole(Object v) {
+ if (shouldPrint) {
+ print(v);
+ }
+}
+
+class A {}
+
+class A0 extends A {
+ final double x;
+ final String str;
+
+ A0(this.x, this.str);
+
+ // Use [x] to prevent the field from being shaken out. We would like
+ // [x] to occupy the same location that [A1.str] takes so that
+ // type confusion / incorrect LICM would cause a crash when we load
+ // `o.[A1.str].length` on an object of type [A0]
+ String toString() => 'A0($x)';
+}
+
+class A1 extends A {
+ final String str;
+
+ A1(this.str);
+}
+
+class H<T> {
+ final T data;
+ H(this.data);
+}
+
+abstract class B<T extends A> {
+ final T v;
+
+ B(this.v);
+
+ int load(H<T> h);
+ int loadWithNamedParam({required H<T> h});
+
+ @pragma('vm:never-inline')
+ @pragma('vm:testing:print-flow-graph', '*LICM')
+ int testNarrowingThroughThisCallWithPositionalParam(H<T> h) {
+ var result = 0;
+ for (var i = 0; i < 10; i++) {
+ // We will perform polymorphic inlining of `load` because `this`
+ // is known to be either B0 or B1. In both cases we will have
+ // v.str.length loads fully inlined because inlined bodies
+ // have precise type information for v.
+ // Then we will hoist v.str.length out of the loop past
+ // class-id comparisons generated by the inlining leading
+ // to incorrect code which will crash.
+ result += load(h);
+ }
+ return result;
+ }
+
+ @pragma('vm:never-inline')
+ @pragma('vm:testing:print-flow-graph', '*LICM')
+ int testNarrowingThroughThisCallWithNamedParams(H<T> h) {
+ var result = 0;
+ for (var i = 0; i < 10; i++) {
+ // We will perform polymorphic inlining of `load` because `this`
+ // is known to be either B0 or B1. In both cases we will have
+ // v.str.length loads fully inlined because inlined bodies
+ // have precise type information for v.
+ // Then we will hoist v.str.length out of the loop past
+ // class-id comparisons generated by the inlining leading
+ // to incorrect code which will crash.
+ result += loadWithNamedParam(h: h);
+ }
+ return result;
+ }
+}
+
+class BImpl<T extends A> extends B<T> {
+ BImpl(T v) : super(v);
+
+ // These methods do not matter. They can just return 0.
+ int load(H<T> h) => 0;
+ int loadWithNamedParam({required H<T> h}) => 0;
+}
+
+class B0 extends B<A0> {
+ B0(A0 a) : super(a);
+
+ int load(H<A0> h) {
+ return h.data.str.length;
+ }
+
+ int loadWithNamedParam({String? a, required H<A0> h, String? z}) {
+ return h.data.str.length;
+ }
+}
+
+class B1 extends B<A1> {
+ B1(A1 a) : super(a);
+
+ int load(H<A1> h) {
+ return h.data.str.length;
+ }
+
+ int loadWithNamedParam({String? a, required H<A1> h, String? z}) {
+ return h.data.str.length;
+ }
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIsCheckOnSubclass(B b) {
+ int sum = 0;
+ b.v.toString();
+ for (var i = 0; i < 2; i++) {
+ if (b is B1) {
+ sum += b.v.str.length;
+ }
+ }
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIsCheckWithTypeArgPolymorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ if (b is B<A1>) {
+ sum += b.v.str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIsCheckWithTypeArgMonomorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ if (b is B<A1>) {
+ sum += b.v.str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughAsCheckWithTypeArgPolymorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ // Branch with a side-effect to avoid b as B<A1> hoisting.
+ if (sum == 42) throw '42';
+ sum += (b as B<A1>).v.str.length;
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(B b) {
+ int sum = 0;
+ final v = b.v;
+ for (var i = 0; i < 2; i++) {
+ // Branch with a side-effect to avoid b as B<A1> hoisting.
+ if (sum == 42) throw '42';
+ sum += (sum == 22 ? b as B<A1> : b as B<A1>).v.str.length;
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIndexedLoadFromGrowableArray(List<A> l) {
+ int sum = 0;
+ final v = l[0];
+ for (var i = 0; i < 2; i++) {
+ if (l is List<A1>) {
+ sum += l[0].str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph', '*LICM')
+int testNarrowingThroughIndexedLoadFromFixedArray(List<A> l) {
+ int sum = 0;
+ final v = l[0];
+ for (var i = 0; i < 2; i++) {
+ if (l is List<A1>) {
+ sum += l[0].str.length;
+ }
+ }
+ blackhole(v);
+ return sum;
+}
+
+void main(List<String> args) {
+ shouldPrint = args.contains("shouldPrint");
+
+ // Prevent shaking of BImpl.load and BImpl.loadWithNamed, if these methods
+ // are shaked (because they are not used) that would inhibit polymorphic
+ // inlining at testNarrowingThroughThisCall{,WithNamedParams}
+ BImpl(A1("")).load(H(A1("")));
+ BImpl(A1("")).loadWithNamedParam(h: H(A1("")));
+
+ for (var i = 0; i < 2; i++) {
+ final a1 = A1(i.toString());
+ final a0 = A0(i.toDouble(), i.toString());
+ B1(a1).testNarrowingThroughThisCallWithPositionalParam(H(a1));
+ B0(a0).testNarrowingThroughThisCallWithPositionalParam(H(a0));
+ B1(a1).testNarrowingThroughThisCallWithNamedParams(H(a1));
+ B0(a0).testNarrowingThroughThisCallWithNamedParams(H(a0));
+ testNarrowingThroughIsCheckOnSubclass(B1(a1));
+ testNarrowingThroughIsCheckOnSubclass(B0(a0));
+ testNarrowingThroughIsCheckWithTypeArgPolymorphic(B1(a1));
+ testNarrowingThroughIsCheckWithTypeArgPolymorphic(B0(a0));
+ testNarrowingThroughIsCheckWithTypeArgMonomorphic(BImpl<A1>(a1));
+ testNarrowingThroughIsCheckWithTypeArgMonomorphic(BImpl<A0>(a0));
+ testNarrowingThroughAsCheckWithTypeArgPolymorphic(B1(a1));
+ try {
+ testNarrowingThroughAsCheckWithTypeArgPolymorphic(B0(a0));
+ throw "Should be unreachable";
+ } on TypeError catch (e) {}
+ testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(B1(a1));
+ try {
+ testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(B0(a0));
+ throw "Should be unreachable";
+ } on TypeError catch (e) {}
+ testNarrowingThroughIndexedLoadFromGrowableArray([a1]);
+ testNarrowingThroughIndexedLoadFromGrowableArray([a0]);
+ testNarrowingThroughIndexedLoadFromFixedArray(
+ List<A1>.filled(1, a1, growable: false));
+ testNarrowingThroughIndexedLoadFromFixedArray(
+ List<A0>.filled(1, a0, growable: false));
+ }
+}
+
+void matchIL$testNarrowingThroughThisCallWithPositionalParam(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'h_raw' << match.Parameter(index: 1),
+ 'h' << match.AssertAssignable('h_raw', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_cid' << match.LoadClassId('this'),
+ match.Branch(match.StrictCompare('this_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('this'),
+ // This redefinition was inserted by inlining.
+ 'h_' << match.Redefinition('h'),
+ 'v0' << match.LoadField('h_', slot: 'data'),
+ 'v1' << match.LoadField('v0', slot: 'str'),
+ 'v2' << match.LoadField('v1', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'h_raw' << match.Parameter(index: 1),
+ 'h' << match.AssertAssignable('h_raw', match.any),
+ 'this_cid' << match.LoadClassId('this'), // Hoisted.
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.StrictCompare('this_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ // After LICM redefinitions are removed.
+ 'v0' << match.LoadField('h', slot: 'data'),
+ 'v1' << match.LoadField('v0', slot: 'str'),
+ 'v2' << match.LoadField('v1', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughThisCallWithNamedParams(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ // Graph shape is basically the same.
+ matchIL$testNarrowingThroughThisCallWithPositionalParam(
+ beforeLICM, afterLICM);
+}
+
+void matchIL$testNarrowingThroughIsCheckOnSubclass(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'b_cid' << match.LoadClassId('b'),
+ match.Branch(match.StrictCompare('b_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('b'),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ 'b_cid' << match.LoadClassId('b'), // Hoisted.
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.StrictCompare('b_cid', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughIsCheckWithTypeArgMonomorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'b_is_B<A1>' << match.InstanceOf('b', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('b_is_B<A1>', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('b'),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'b_is_B<A1>' << match.InstanceOf('b', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('b_is_B<A1>', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughAsCheckWithTypeArgPolymorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughPhiOfAsChecksWithTypeArgPolymorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B5', ifFalse: 'B6'),
+ ]),
+ 'B5' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B6' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B7' <<
+ match.block('Join', [
+ // This redefinition was inserted by PhiInstr::Canonicalize
+ // which removed Phi of two AssertAssignables above.
+ match.Redefinition('b'),
+ // This redefinition was inserted by load forwarding.
+ 'b.v*' << match.Redefinition('b.v'),
+ 'v0' << match.LoadField('b.v*', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'b' << match.Parameter(index: 0),
+ 'b.v' << match.LoadField('b', slot: 'v'),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B3', ifFalse: 'B4'),
+ ]),
+ 'B3' << match.block('Target', [match.Throw(match.any)]),
+ 'B4' <<
+ match.block('Target', [
+ match.Branch(match.EqualityCompare(match.any, match.any, kind: '=='),
+ ifTrue: 'B5', ifFalse: 'B6'),
+ ]),
+ 'B5' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B6' <<
+ match.block('Target', [
+ match.AssertAssignable('b', match.any, match.any, match.any),
+ match.Goto('B7'),
+ ]),
+ 'B7' <<
+ match.block('Join', [
+ 'v0' << match.LoadField('b.v', slot: 'str'),
+ 'v1' << match.LoadField('v0', slot: 'String.length'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughIndexedLoadFromGrowableArray(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a.data' << match.LoadField('this', slot: 'GrowableObjectArray.data'),
+ 'a.data[0]' << match.LoadIndexed('a.data', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a.data[0]*' << match.Phi('a.data[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('this'),
+ // This redefinition was inserted by load forwarding.
+ 'a.data[0]**' << match.Redefinition('a.data[0]*'),
+ 'a.data[0].str' << match.LoadField('a.data[0]**', slot: 'str'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a.data' << match.LoadField('this', slot: 'GrowableObjectArray.data'),
+ 'a.data[0]' << match.LoadIndexed('a.data', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a.data[0]*' << match.Phi('a.data[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'a.data[0].str' << match.LoadField('a.data[0]*', slot: 'str'),
+ ]),
+ ], env: env);
+}
+
+void matchIL$testNarrowingThroughIsCheckWithTypeArgPolymorphic(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ matchIL$testNarrowingThroughIsCheckWithTypeArgMonomorphic(
+ beforeLICM, afterLICM);
+}
+
+void matchIL$testNarrowingThroughIndexedLoadFromFixedArray(
+ FlowGraph beforeLICM, FlowGraph afterLICM) {
+ final env = beforeLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a[0]' << match.LoadIndexed('this', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a[0]*' << match.Phi('a[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ match.Redefinition('this'),
+ // This redefinition was inserted by load forwarding.
+ 'a[0]**' << match.Redefinition('a[0]*'),
+ 'a[0].str' << match.LoadField('a[0]**', slot: 'str'),
+ ]),
+ ]);
+
+ afterLICM.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'this' << match.Parameter(index: 0),
+ 'a[0]' << match.LoadIndexed('this', match.any),
+ match.Goto('B1'),
+ ]),
+ 'B1' <<
+ match.block('Join', [
+ 'a[0]*' << match.Phi('a[0]', match.any),
+ match.CheckStackOverflow(),
+ match.Branch(match.RelationalOp(match.any, match.any, kind: '<'),
+ ifTrue: 'B2'),
+ ]),
+ 'B2' <<
+ match.block('Target', [
+ 'this_is_List' << match.InstanceOf('this', match.any, match.any),
+ match.Branch(
+ match.StrictCompare('this_is_List', match.any, kind: '==='),
+ ifTrue: 'B3'),
+ ]),
+ 'B3' <<
+ match.block('Target', [
+ 'a[0].str' << match.LoadField('a[0]*', slot: 'str'),
+ ]),
+ ], env: env);
+}
diff --git a/runtime/vm/compiler/api/print_filter.cc b/runtime/vm/compiler/api/print_filter.cc
index be9762c..4abe1e9 100644
--- a/runtime/vm/compiler/api/print_filter.cc
+++ b/runtime/vm/compiler/api/print_filter.cc
@@ -7,24 +7,46 @@
#include "vm/compiler/api/print_filter.h"
#include "vm/flags.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/compiler_pass.h"
#include "vm/object.h"
+#endif
+#include "vm/symbols.h"
namespace dart {
DEFINE_FLAG(charp,
print_flow_graph_filter,
- NULL,
+ nullptr,
"Print only IR of functions with matching names");
namespace compiler {
// Checks whether function's name matches the given filter, which is
// a comma-separated list of strings.
-static bool PassesFilter(const char* filter, const Function& function) {
- if (filter == NULL) {
+static bool PassesFilter(const char* filter,
+ const Function& function,
+ uint8_t** compiler_pass_filter) {
+ if (filter == nullptr) {
return true;
}
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ if (strcmp(filter, "@pragma") == 0) {
+ Object& pass_filter = Object::Handle();
+ const auto has_pragma =
+ Library::FindPragma(dart::Thread::Current(), /*only_core=*/false,
+ function, Symbols::vm_testing_print_flow_graph(),
+ /*multiple=*/false, &pass_filter);
+ if (has_pragma && !pass_filter.IsNull() &&
+ compiler_pass_filter != nullptr) {
+ *compiler_pass_filter = dart::CompilerPass::ParseFiltersFromPragma(
+ String::Cast(pass_filter).ToCString());
+ }
+ return has_pragma;
+ }
+#endif
+
char* save_ptr; // Needed for strtok_r.
const char* scrubbed_name =
String::Handle(function.QualifiedScrubbedName()).ToCString();
@@ -36,9 +58,9 @@
strncpy(filter_buffer, filter, len); // strtok modifies arg 1.
char* token = strtok_r(filter_buffer, ",", &save_ptr);
bool found = false;
- while (token != NULL) {
- if ((strstr(function_name, token) != NULL) ||
- (strstr(scrubbed_name, token) != NULL)) {
+ while (token != nullptr) {
+ if ((strstr(function_name, token) != nullptr) ||
+ (strstr(scrubbed_name, token) != nullptr)) {
found = true;
break;
}
@@ -53,15 +75,17 @@
}
}
}
- token = strtok_r(NULL, ",", &save_ptr);
+ token = strtok_r(nullptr, ",", &save_ptr);
}
delete[] filter_buffer;
return found;
}
-bool PrintFilter::ShouldPrint(const Function& function) {
- return PassesFilter(FLAG_print_flow_graph_filter, function);
+bool PrintFilter::ShouldPrint(const Function& function,
+ uint8_t** compiler_pass_filter /* = nullptr */) {
+ return PassesFilter(FLAG_print_flow_graph_filter, function,
+ compiler_pass_filter);
}
} // namespace compiler
diff --git a/runtime/vm/compiler/api/print_filter.h b/runtime/vm/compiler/api/print_filter.h
index 604fa9c..95d7a39 100644
--- a/runtime/vm/compiler/api/print_filter.h
+++ b/runtime/vm/compiler/api/print_filter.h
@@ -19,7 +19,8 @@
class PrintFilter : public AllStatic {
public:
- static bool ShouldPrint(const Function& function);
+ static bool ShouldPrint(const Function& function,
+ uint8_t** compiler_pass_filter = nullptr);
};
} // namespace compiler
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index b0195987..1dbf213 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -56,7 +56,10 @@
loop_invariant_loads_(nullptr),
captured_parameters_(new (zone()) BitVector(zone(), variable_count())),
inlining_id_(-1),
- should_print_(FlowGraphPrinter::ShouldPrint(parsed_function.function())) {
+ should_print_(false) {
+ should_print_ = FlowGraphPrinter::ShouldPrint(parsed_function.function(),
+ &compiler_pass_filters_);
+
direct_parameters_size_ = ParameterOffsetAt(
function(), num_direct_parameters_, /*last_slot*/ false);
DiscoverBlocks();
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index c847bab..2c82c13 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -469,6 +469,9 @@
void RenameUsesDominatedByRedefinitions();
bool should_print() const { return should_print_; }
+ const uint8_t* compiler_pass_filters() const {
+ return compiler_pass_filters_;
+ }
//
// High-level utilities.
@@ -632,6 +635,7 @@
intptr_t inlining_id_;
bool should_print_;
+ uint8_t* compiler_pass_filters_ = nullptr;
const Array* coverage_array_ = &Array::empty_array();
};
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 2ba0335..4ca03ed 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -1135,18 +1135,18 @@
// Returns true if the value represents a constant.
bool Value::BindsToConstant() const {
- return definition()->IsConstant();
+ return definition()->OriginalDefinition()->IsConstant();
}
// Returns true if the value represents constant null.
bool Value::BindsToConstantNull() const {
- ConstantInstr* constant = definition()->AsConstant();
+ ConstantInstr* constant = definition()->OriginalDefinition()->AsConstant();
return (constant != NULL) && constant->value().IsNull();
}
const Object& Value::BoundConstant() const {
ASSERT(BindsToConstant());
- ConstantInstr* constant = definition()->AsConstant();
+ ConstantInstr* constant = definition()->OriginalDefinition()->AsConstant();
ASSERT(constant != NULL);
return constant->value();
}
@@ -1207,7 +1207,7 @@
// ==== Support for visiting flow graphs.
#define DEFINE_ACCEPT(ShortName, Attrs) \
- void ShortName##Instr::Accept(FlowGraphVisitor* visitor) { \
+ void ShortName##Instr::Accept(InstructionVisitor* visitor) { \
visitor->Visit##ShortName(this); \
}
@@ -2545,7 +2545,8 @@
if (!HasUses() && !flow_graph->is_licm_allowed()) {
return NULL;
}
- if ((constrained_type() != nullptr) && Type()->IsEqualTo(value()->Type())) {
+ if (constrained_type() != nullptr &&
+ constrained_type()->IsEqualTo(value()->Type())) {
return value()->definition();
}
return this;
@@ -5919,8 +5920,43 @@
}
}
+static bool AllInputsAreRedefinitions(PhiInstr* phi) {
+ for (intptr_t i = 0; i < phi->InputCount(); i++) {
+ if (phi->InputAt(i)->definition()->RedefinedValue() == nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
Definition* PhiInstr::Canonicalize(FlowGraph* flow_graph) {
Definition* replacement = GetReplacementForRedundantPhi();
+ if (replacement != nullptr && flow_graph->is_licm_allowed() &&
+ AllInputsAreRedefinitions(this)) {
+ // If we are replacing a Phi which has redefinitions as all of its inputs
+ // then to maintain the redefinition chain we are going to insert a
+ // redefinition. If any input is *not* a redefinition that means that
+ // whatever properties were infered for a Phi also hold on a path
+ // that does not pass through any redefinitions so there is no need
+ // to redefine this value.
+ auto zone = flow_graph->zone();
+ auto redef = new (zone) RedefinitionInstr(new (zone) Value(replacement));
+ flow_graph->InsertAfter(block(), redef, /*env=*/nullptr, FlowGraph::kValue);
+
+ // Redefinition is not going to dominate the block entry itself, so we
+ // have to handle environment uses at the block entry specially.
+ Value* next_use;
+ for (Value* use = env_use_list(); use != nullptr; use = next_use) {
+ next_use = use->next_use();
+ if (use->instruction() == block()) {
+ use->RemoveFromUseList();
+ use->set_definition(replacement);
+ replacement->AddEnvUse(use);
+ }
+ }
+ return redef;
+ }
+
return (replacement != nullptr) ? replacement : this;
}
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 62c5fb4..4e7efb6 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -11,6 +11,7 @@
#endif // defined(DART_PRECOMPILED_RUNTIME)
#include <memory>
+#include <tuple>
#include <utility>
#include "vm/allocation.h"
@@ -54,6 +55,7 @@
class FlowGraphVisitor;
class ForwardInstructionIterator;
class Instruction;
+class InstructionVisitor;
class LocalVariable;
class LoopInfo;
class ParsedFunction;
@@ -537,7 +539,7 @@
// Functions required in all concrete instruction classes.
#define DECLARE_INSTRUCTION_NO_BACKEND(type) \
virtual Tag tag() const { return k##type; } \
- virtual void Accept(FlowGraphVisitor* visitor); \
+ virtual void Accept(InstructionVisitor* visitor); \
DEFINE_INSTRUCTION_TYPE_CHECK(type)
#define DECLARE_INSTRUCTION_BACKEND() \
@@ -564,9 +566,13 @@
#define PRINT_TO_SUPPORT virtual void PrintTo(BaseTextBuffer* f) const;
#define PRINT_OPERANDS_TO_SUPPORT \
virtual void PrintOperandsTo(BaseTextBuffer* f) const;
+#define DECLARE_ATTRIBUTES(...) \
+ auto GetAttributes() const { return std::make_tuple(__VA_ARGS__); } \
+ static auto GetAttributeNames() { return std::make_tuple(#__VA_ARGS__); }
#else
#define PRINT_TO_SUPPORT
#define PRINT_OPERANDS_TO_SUPPORT
+#define DECLARE_ATTRIBUTES(...)
#endif // defined(INCLUDE_IL_PRINTER)
// Together with CidRange, this represents a mapping from a range of class-ids
@@ -867,7 +873,7 @@
}
// Visiting support.
- virtual void Accept(FlowGraphVisitor* visitor) = 0;
+ virtual void Accept(InstructionVisitor* visitor) = 0;
Instruction* previous() const { return previous_; }
void set_previous(Instruction* instr) {
@@ -2566,6 +2572,7 @@
block_(block) {}
DECLARE_INSTRUCTION(Parameter)
+ DECLARE_ATTRIBUTES(index())
intptr_t index() const { return index_; }
intptr_t param_offset() const { return param_offset_; }
@@ -3254,6 +3261,7 @@
virtual TokenPosition token_pos() const { return token_pos_; }
Token::Kind kind() const { return kind_; }
+ DECLARE_ATTRIBUTES(kind())
virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right) = 0;
@@ -3762,6 +3770,8 @@
virtual Value* RedefinedValue() const;
+ virtual void InferRange(RangeAnalysis* analysis, Range* range);
+
PRINT_OPERANDS_TO_SUPPORT
private:
@@ -4098,6 +4108,9 @@
Code::EntryKind entry_kind() const { return entry_kind_; }
void set_entry_kind(Code::EntryKind value) { entry_kind_ = value; }
+ void mark_as_call_on_this() { is_call_on_this_ = true; }
+ bool is_call_on_this() const { return is_call_on_this_; }
+
DEFINE_INSTRUCTION_TYPE_CHECK(InstanceCallBase);
bool receiver_is_not_smi() const { return receiver_is_not_smi_; }
@@ -4144,6 +4157,7 @@
bool has_unique_selector_;
Code::EntryKind entry_kind_ = Code::EntryKind::kNormal;
bool receiver_is_not_smi_ = false;
+ bool is_call_on_this_ = false;
DISALLOW_COPY_AND_ASSIGN(InstanceCallBaseInstr);
};
@@ -4254,6 +4268,9 @@
new_call->set_result_type(call->result_type());
new_call->set_entry_kind(call->entry_kind());
new_call->set_has_unique_selector(call->has_unique_selector());
+ if (call->is_call_on_this()) {
+ new_call->mark_as_call_on_this();
+ }
return new_call;
}
@@ -6674,6 +6691,8 @@
bool IsPotentialUnboxedDartFieldLoad() const;
DECLARE_INSTRUCTION(LoadField)
+ DECLARE_ATTRIBUTES(&slot())
+
virtual CompileType ComputeType() const;
virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); }
@@ -9664,9 +9683,27 @@
DISALLOW_COPY_AND_ASSIGN(Environment);
};
+class InstructionVisitor : public ValueObject {
+ public:
+ InstructionVisitor() {}
+ virtual ~InstructionVisitor() {}
+
+// Visit functions for instruction classes, with an empty default
+// implementation.
+#define DECLARE_VISIT_INSTRUCTION(ShortName, Attrs) \
+ virtual void Visit##ShortName(ShortName##Instr* instr) {}
+
+ FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InstructionVisitor);
+};
+
// Visitor base class to visit each instruction and computation in a flow
// graph as defined by a reversed list of basic blocks.
-class FlowGraphVisitor : public ValueObject {
+class FlowGraphVisitor : public InstructionVisitor {
public:
explicit FlowGraphVisitor(const GrowableArray<BlockEntryInstr*>& block_order)
: current_iterator_(NULL), block_order_(&block_order) {}
@@ -9680,15 +9717,6 @@
// instructions in order from the block entry to exit.
virtual void VisitBlocks();
-// Visit functions for instruction classes, with an empty default
-// implementation.
-#define DECLARE_VISIT_INSTRUCTION(ShortName, Attrs) \
- virtual void Visit##ShortName(ShortName##Instr* instr) {}
-
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
-
-#undef DECLARE_VISIT_INSTRUCTION
-
protected:
void set_block_order(const GrowableArray<BlockEntryInstr*>& block_order) {
block_order_ = &block_order;
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index 1c8b3f8..df13a3a 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -4,6 +4,8 @@
#include "vm/compiler/backend/il_printer.h"
+#include <tuple>
+
#include "vm/compiler/api/print_filter.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/linearscan.h"
@@ -20,19 +22,195 @@
false,
"Calls display a unary, sorted-by count form of ICData");
DEFINE_FLAG(bool, print_environments, false, "Print SSA environments.");
+DEFINE_FLAG(bool,
+ print_flow_graph_as_json,
+ false,
+ "Use machine readable output when printing IL graphs.");
DECLARE_FLAG(bool, trace_inlining_intervals);
-bool FlowGraphPrinter::ShouldPrint(const Function& function) {
- return compiler::PrintFilter::ShouldPrint(function);
+class IlTestPrinter : public AllStatic {
+ public:
+ static void PrintGraph(const char* phase, FlowGraph* flow_graph) {
+ JSONWriter writer;
+ writer.OpenObject();
+ writer.PrintProperty("p", phase);
+ writer.PrintProperty("f", flow_graph->function().ToFullyQualifiedCString());
+ writer.OpenArray("b");
+ for (auto block : flow_graph->reverse_postorder()) {
+ PrintBlock(&writer, block);
+ }
+ writer.CloseArray();
+ writer.OpenObject("desc");
+ AttributesSerializer(&writer).WriteDescriptors();
+ writer.CloseObject();
+ writer.CloseObject();
+ THR_Print("%s\n", writer.ToCString());
+ }
+
+ static void PrintBlock(JSONWriter* writer, BlockEntryInstr* block) {
+ writer->OpenObject();
+ writer->PrintProperty64("b", block->block_id());
+ writer->PrintProperty("o", block->DebugName());
+ if (auto block_with_defs = block->AsBlockEntryWithInitialDefs()) {
+ if (block_with_defs->initial_definitions() != nullptr &&
+ block_with_defs->initial_definitions()->length() > 0) {
+ writer->OpenArray("d");
+ for (auto defn : *block_with_defs->initial_definitions()) {
+ if (defn->IsConstant() && !defn->HasUses()) continue;
+ PrintInstruction(writer, defn);
+ }
+ writer->CloseArray();
+ }
+ }
+ writer->OpenArray("is");
+ if (auto join = block->AsJoinEntry()) {
+ for (PhiIterator it(join); !it.Done(); it.Advance()) {
+ PrintInstruction(writer, it.Current());
+ }
+ }
+ for (auto instr : block->instructions()) {
+ PrintInstruction(writer, instr);
+ }
+ writer->CloseArray();
+ writer->CloseObject();
+ }
+
+ static void PrintInstruction(JSONWriter* writer,
+ Instruction* instr,
+ const char* name = nullptr) {
+ writer->OpenObject(name);
+ if (auto defn = instr->AsDefinition()) {
+ if (defn->ssa_temp_index() != -1) {
+ writer->PrintProperty("v", defn->ssa_temp_index());
+ }
+ }
+ writer->PrintProperty("o", instr->DebugName());
+ if (auto branch = instr->AsBranch()) {
+ PrintInstruction(writer, branch->comparison(), "cc");
+ } else {
+ if (instr->InputCount() != 0) {
+ writer->OpenArray("i");
+ for (intptr_t i = 0; i < instr->InputCount(); i++) {
+ writer->PrintValue(instr->InputAt(i)->definition()->ssa_temp_index());
+ }
+ writer->CloseArray();
+ } else if (instr->ArgumentCount() != 0 &&
+ instr->GetPushArguments() != nullptr) {
+ writer->OpenArray("i");
+ for (intptr_t i = 0; i < instr->ArgumentCount(); i++) {
+ writer->PrintValue(
+ instr->ArgumentValueAt(i)->definition()->ssa_temp_index());
+ }
+ writer->CloseArray();
+ }
+ AttributesSerializer serializer(writer);
+ instr->Accept(&serializer);
+ }
+ if (instr->SuccessorCount() > 0) {
+ writer->OpenArray("s");
+ for (auto succ : instr->successors()) {
+ writer->PrintValue(succ->block_id());
+ }
+ writer->CloseArray();
+ }
+ writer->CloseObject();
+ }
+
+ template <typename T>
+ class HasGetAttributes {
+ template <typename U>
+ static std::true_type test(decltype(&U::GetAttributes));
+ template <typename U>
+ static std::false_type test(...);
+
+ public:
+ static constexpr bool value = decltype(test<T>(0))::value;
+ };
+
+ class AttributesSerializer : public InstructionVisitor {
+ public:
+ explicit AttributesSerializer(JSONWriter* writer) : writer_(writer) {}
+
+ void WriteDescriptors() {
+#define DECLARE_VISIT_INSTRUCTION(ShortName, Attrs) \
+ WriteDescriptor<ShortName##Instr>(#ShortName);
+
+ FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+ }
+
+#define DECLARE_VISIT_INSTRUCTION(ShortName, Attrs) \
+ virtual void Visit##ShortName(ShortName##Instr* instr) { Write(instr); }
+
+ FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+ private:
+ void WriteAttribute(const char* value) { writer_->PrintValue(value); }
+
+ void WriteAttribute(intptr_t value) { writer_->PrintValue(value); }
+
+ void WriteAttribute(Token::Kind kind) {
+ writer_->PrintValue(Token::Str(kind));
+ }
+
+ void WriteAttribute(const Slot* slot) { writer_->PrintValue(slot->Name()); }
+
+ template <typename... Ts>
+ void WriteTuple(const std::tuple<Ts...>& tuple) {
+ std::apply([&](Ts const&... elements) { WriteAttribute(elements...); },
+ tuple);
+ }
+
+ template <typename T,
+ typename = typename std::enable_if_t<HasGetAttributes<T>::value>>
+ void Write(T* instr) {
+ writer_->OpenArray("d");
+ WriteTuple(instr->GetAttributes());
+ writer_->CloseArray();
+ }
+
+ void Write(Instruction* instr) {
+ // Default, do nothing.
+ }
+
+ template <typename T>
+ void WriteDescriptor(
+ const char* name,
+ typename std::enable_if_t<HasGetAttributes<T>::value>* = 0) {
+ writer_->OpenArray(name);
+ WriteTuple(T::GetAttributeNames());
+ writer_->CloseArray();
+ }
+
+ template <typename T>
+ void WriteDescriptor(
+ const char* name,
+ typename std::enable_if_t<!HasGetAttributes<T>::value>* = 0) {}
+
+ JSONWriter* writer_;
+ };
+};
+
+bool FlowGraphPrinter::ShouldPrint(
+ const Function& function,
+ uint8_t** compiler_pass_filter /* = nullptr */) {
+ return compiler::PrintFilter::ShouldPrint(function, compiler_pass_filter);
}
void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) {
LogBlock lb;
- THR_Print("*** BEGIN CFG\n%s\n", phase);
- FlowGraphPrinter printer(*flow_graph);
- printer.PrintBlocks();
- THR_Print("*** END CFG\n");
+ if (FLAG_print_flow_graph_as_json) {
+ IlTestPrinter::PrintGraph(phase, flow_graph);
+ } else {
+ THR_Print("*** BEGIN CFG\n%s\n", phase);
+ FlowGraphPrinter printer(*flow_graph);
+ printer.PrintBlocks();
+ THR_Print("*** END CFG\n");
+ }
fflush(stdout);
}
@@ -1286,7 +1464,9 @@
UNREACHABLE();
}
-bool FlowGraphPrinter::ShouldPrint(const Function& function) {
+bool FlowGraphPrinter::ShouldPrint(
+ const Function& function,
+ uint8_t** compiler_pass_filter /* = nullptr */) {
return false;
}
diff --git a/runtime/vm/compiler/backend/il_printer.h b/runtime/vm/compiler/backend/il_printer.h
index 96268c8..3784083 100644
--- a/runtime/vm/compiler/backend/il_printer.h
+++ b/runtime/vm/compiler/backend/il_printer.h
@@ -56,7 +56,8 @@
static void PrintCidRangeData(const CallTargets& ic_data,
intptr_t num_checks_to_print = kPrintAll);
- static bool ShouldPrint(const Function& function);
+ static bool ShouldPrint(const Function& function,
+ uint8_t** compiler_pass_filter = nullptr);
private:
const Function& function_;
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index 2523166..38de868 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -629,18 +629,128 @@
const Function& caller_function_;
};
+static bool IsAThisCallThroughAnUncheckedEntryPoint(Definition* call) {
+ if (auto instance_call = call->AsInstanceCallBase()) {
+ return (instance_call->entry_kind() == Code::EntryKind::kUnchecked) &&
+ instance_call->is_call_on_this();
+ }
+ return false;
+}
+
+// Helper which returns true if callee potentially has a more specific
+// parameter type and thus a redefinition needs to be inserted.
+static bool CalleeParameterTypeMightBeMoreSpecific(
+ BitVector* is_generic_covariant_impl,
+ const FunctionType& interface_target_signature,
+ const FunctionType& callee_signature,
+ intptr_t first_arg_index,
+ intptr_t arg_index) {
+ if (arg_index > first_arg_index && is_generic_covariant_impl != nullptr &&
+ is_generic_covariant_impl->Contains(arg_index - first_arg_index)) {
+ const intptr_t param_index = arg_index - first_arg_index;
+ const intptr_t num_named_params =
+ callee_signature.NumOptionalNamedParameters();
+ const intptr_t num_params = callee_signature.NumParameters();
+ if (num_named_params == 0 &&
+ param_index >= interface_target_signature.NumParameters()) {
+ // An optional positional parameter which was added in the callee but
+ // not present in the interface target.
+ return false;
+ }
+
+ // Check if this argument corresponds to a named parameter. In this case
+ // we need to find correct index based on the name.
+ intptr_t interface_target_param_index = param_index;
+ if (num_named_params > 0 &&
+ (num_params - num_named_params) <= param_index) {
+ // This is a named parameter.
+ const String& name =
+ String::Handle(callee_signature.ParameterNameAt(param_index));
+ interface_target_param_index = -1;
+ for (intptr_t i = interface_target_signature.NumParameters() -
+ interface_target_signature.NumOptionalNamedParameters(),
+ n = interface_target_signature.NumParameters();
+ i < n; i++) {
+ if (interface_target_signature.ParameterNameAt(i) == name.ptr()) {
+ interface_target_param_index = i;
+ break;
+ }
+ }
+
+ // This is a named parameter which was added in the callee.
+ if (interface_target_param_index == -1) {
+ return false;
+ }
+ }
+ const AbstractType& callee_parameter_type =
+ AbstractType::Handle(callee_signature.ParameterTypeAt(param_index));
+ const AbstractType& interface_target_parameter_type =
+ AbstractType::Handle(interface_target_signature.ParameterTypeAt(
+ interface_target_param_index));
+ if (interface_target_parameter_type.ptr() != callee_parameter_type.ptr()) {
+ // This a conservative approximation.
+ return true;
+ }
+ }
+ return false;
+}
+
static void ReplaceParameterStubs(Zone* zone,
FlowGraph* caller_graph,
InlinedCallData* call_data,
const TargetInfo* target_info) {
const bool is_polymorphic = call_data->call->IsPolymorphicInstanceCall();
+ const bool no_checks =
+ IsAThisCallThroughAnUncheckedEntryPoint(call_data->call);
ASSERT(is_polymorphic == (target_info != NULL));
FlowGraph* callee_graph = call_data->callee_graph;
auto callee_entry = callee_graph->graph_entry()->normal_entry();
+ const Function& callee = callee_graph->function();
+
+ FunctionType& interface_target_signature = FunctionType::Handle();
+ FunctionType& callee_signature = FunctionType::Handle(callee.signature());
+
+ // If we are inlining a call on this and we are going to skip parameter checks
+ // then a situation can arise when parameter type in the callee has a narrower
+ // type than what interface target specifies, e.g.
+ //
+ // class A<T> {
+ // void f(T v);
+ // void g(T v) { f(v); }
+ // }
+ // class B extends A<X> { void f(X v) { ... } }
+ //
+ // Conside when B.f is inlined into a callsite in A.g (e.g. due to polymorphic
+ // inlining). v is known to be X within the body of B.f, but not guaranteed to
+ // be X outside of it. Thus we must ensure that all operations with v that
+ // depend on its type being X are pinned to stay within the inlined body.
+ //
+ // We achieve that by inserting redefinitions for parameters which potentially
+ // have narrower types in callee compared to those in the interface target of
+ // the call.
+ BitVector* is_generic_covariant_impl = nullptr;
+ if (no_checks && callee.IsRegularFunction()) {
+ const Function& interface_target =
+ call_data->call->AsInstanceCallBase()->interface_target();
+
+ callee_signature = callee.signature();
+ interface_target_signature = interface_target.signature();
+
+ // If signatures match then there is nothing to do.
+ if (interface_target.signature() != callee.signature()) {
+ const intptr_t num_params = callee.NumParameters();
+ BitVector is_covariant(zone, num_params);
+ is_generic_covariant_impl = new (zone) BitVector(zone, num_params);
+
+ kernel::ReadParameterCovariance(callee_graph->function(), &is_covariant,
+ is_generic_covariant_impl);
+ }
+ }
// Replace each stub with the actual argument or the caller's constant.
// Nulls denote optional parameters for which no actual was given.
const intptr_t first_arg_index = call_data->first_arg_index;
+
// When first_arg_index > 0, the stub and actual argument processed in the
// first loop iteration represent a passed-in type argument vector.
GrowableArray<Value*>* arguments = call_data->arguments;
@@ -654,17 +764,29 @@
}
for (intptr_t i = 0; i < arguments->length(); ++i) {
Value* actual = (*arguments)[i];
- Definition* defn = NULL;
- if (is_polymorphic && (i == first_arg_index)) {
- // Replace the receiver argument with a redefinition to prevent code from
- // the inlined body from being hoisted above the inlined entry.
+ Definition* defn = nullptr;
+
+ // Replace the receiver argument with a redefinition to prevent code from
+ // the inlined body from being hoisted above the inlined entry.
+ const bool is_polymorphic_receiver =
+ (is_polymorphic && (i == first_arg_index));
+
+ if (actual == nullptr) {
+ ASSERT(!is_polymorphic_receiver);
+ continue;
+ }
+
+ if (is_polymorphic_receiver ||
+ CalleeParameterTypeMightBeMoreSpecific(
+ is_generic_covariant_impl, interface_target_signature,
+ callee_signature, first_arg_index, i)) {
RedefinitionInstr* redefinition =
new (zone) RedefinitionInstr(actual->Copy(zone));
redefinition->set_ssa_temp_index(caller_graph->alloc_ssa_temp_index());
if (FlowGraph::NeedsPairLocation(redefinition->representation())) {
caller_graph->alloc_ssa_temp_index();
}
- if (target_info->IsSingleCid()) {
+ if (is_polymorphic_receiver && target_info->IsSingleCid()) {
redefinition->UpdateType(CompileType::FromCid(target_info->cid_start));
}
redefinition->InsertAfter(callee_entry);
@@ -674,13 +796,12 @@
callee_entry->ReplaceInEnvironment(
call_data->parameter_stubs->At(first_arg_stub_index + i),
actual->definition());
- } else if (actual != NULL) {
+ } else {
defn = actual->definition();
}
- if (defn != NULL) {
- call_data->parameter_stubs->At(first_arg_stub_index + i)
- ->ReplaceUsesWith(defn);
- }
+
+ call_data->parameter_stubs->At(first_arg_stub_index + i)
+ ->ReplaceUsesWith(defn);
}
// Replace remaining constants with uses by constants in the caller's
@@ -688,7 +809,7 @@
auto defns = callee_graph->graph_entry()->initial_definitions();
for (intptr_t i = 0; i < defns->length(); ++i) {
ConstantInstr* constant = (*defns)[i]->AsConstant();
- if (constant != NULL && constant->HasUses()) {
+ if (constant != nullptr && constant->HasUses()) {
constant->ReplaceUsesWith(caller_graph->GetConstant(constant->value()));
}
}
@@ -696,12 +817,12 @@
defns = callee_graph->graph_entry()->normal_entry()->initial_definitions();
for (intptr_t i = 0; i < defns->length(); ++i) {
ConstantInstr* constant = (*defns)[i]->AsConstant();
- if (constant != NULL && constant->HasUses()) {
+ if (constant != nullptr && constant->HasUses()) {
constant->ReplaceUsesWith(caller_graph->GetConstant(constant->value()));
}
SpecialParameterInstr* param = (*defns)[i]->AsSpecialParameter();
- if (param != NULL && param->HasUses()) {
+ if (param != nullptr && param->HasUses()) {
switch (param->kind()) {
case SpecialParameterInstr::kContext: {
ASSERT(!is_polymorphic);
@@ -1422,9 +1543,8 @@
// Plug result in the caller graph.
InlineExitCollector* exit_collector = call_data->exit_collector;
exit_collector->PrepareGraphs(callee_graph);
- exit_collector->ReplaceCall(callee_function_entry);
-
ReplaceParameterStubs(zone(), caller_graph_, call_data, NULL);
+ exit_collector->ReplaceCall(callee_function_entry);
ASSERT(!call_data->call->HasPushArguments());
}
diff --git a/runtime/vm/compiler/backend/inliner_test.cc b/runtime/vm/compiler/backend/inliner_test.cc
index f5c59aa..4760a70 100644
--- a/runtime/vm/compiler/backend/inliner_test.cc
+++ b/runtime/vm/compiler/backend/inliner_test.cc
@@ -188,7 +188,8 @@
{kMatchAndMoveStoreIndexed, &store_instr},
}));
- RELEASE_ASSERT(unbox_instr->InputAt(0)->definition() == value_param);
+ RELEASE_ASSERT(unbox_instr->InputAt(0)->definition()->OriginalDefinition() ==
+ value_param);
RELEASE_ASSERT(store_instr->InputAt(0)->definition() == list_param);
RELEASE_ASSERT(store_instr->InputAt(2)->definition() == unbox_instr);
RELEASE_ASSERT(unbox_instr->is_truncating());
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index 74a7257..7d02f87 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -3091,7 +3091,22 @@
}
}
+void AssertAssignableInstr::InferRange(RangeAnalysis* analysis, Range* range) {
+ const Range* value_range = value()->definition()->range();
+ if (!Range::IsUnknown(value_range)) {
+ *range = *value_range;
+ } else {
+ *range = Range::Full(RangeBoundary::kRangeBoundaryInt64);
+ }
+}
+
static bool IsRedundantBasedOnRangeInformation(Value* index, Value* length) {
+ if (index->BindsToSmiConstant() && length->BindsToSmiConstant()) {
+ const auto index_val = index->BoundSmiConstant();
+ const auto length_val = length->BoundSmiConstant();
+ return (0 <= index_val && index_val < length_val);
+ }
+
// Range of the index is unknown can't decide if the check is redundant.
Range* index_range = index->definition()->range();
if (index_range == nullptr) {
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 75a83b4..67f8112 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1921,6 +1921,72 @@
(array_store->class_id() == kTypedDataFloat32x4ArrayCid));
}
+ static bool AlreadyPinnedByRedefinition(Definition* replacement,
+ Definition* redefinition) {
+ Definition* defn = replacement;
+ if (auto load_field = replacement->AsLoadField()) {
+ defn = load_field->instance()->definition();
+ } else if (auto load_indexed = replacement->AsLoadIndexed()) {
+ defn = load_indexed->array()->definition();
+ }
+
+ Value* unwrapped;
+ while ((unwrapped = defn->RedefinedValue()) != nullptr) {
+ if (defn == redefinition) {
+ return true;
+ }
+ defn = unwrapped->definition();
+ }
+
+ return false;
+ }
+
+ Definition* ReplaceLoad(Definition* load, Definition* replacement) {
+ // When replacing a load from a generic field or from an array element
+ // check if instance we are loading from is redefined. If it is then
+ // we need to ensure that replacement is not going to break the
+ // dependency chain.
+ Definition* redef = nullptr;
+ if (auto load_field = load->AsLoadField()) {
+ auto instance = load_field->instance()->definition();
+ if (instance->RedefinedValue() != nullptr) {
+ if ((load_field->slot().kind() ==
+ Slot::Kind::kGrowableObjectArray_data ||
+ (load_field->slot().IsDartField() &&
+ !AbstractType::Handle(load_field->slot().field().type())
+ .IsInstantiated()))) {
+ redef = instance;
+ }
+ }
+ } else if (auto load_indexed = load->AsLoadIndexed()) {
+ if (load_indexed->class_id() == kArrayCid ||
+ load_indexed->class_id() == kImmutableArrayCid) {
+ auto instance = load_indexed->array()->definition();
+ if (instance->RedefinedValue() != nullptr) {
+ redef = instance;
+ }
+ }
+ }
+ if (redef != nullptr && !AlreadyPinnedByRedefinition(replacement, redef)) {
+ // Original load had a redefined instance and replacement does not
+ // depend on the same redefinition. Create a redefinition
+ // of the replacement to keep the dependency chain.
+ auto replacement_redefinition =
+ new (zone()) RedefinitionInstr(new (zone()) Value(replacement));
+ if (redef->IsDominatedBy(replacement)) {
+ graph_->InsertAfter(redef, replacement_redefinition, /*env=*/nullptr,
+ FlowGraph::kValue);
+ } else {
+ graph_->InsertBefore(load, replacement_redefinition, /*env=*/nullptr,
+ FlowGraph::kValue);
+ }
+ replacement = replacement_redefinition;
+ }
+
+ load->ReplaceUsesWith(replacement);
+ return replacement;
+ }
+
// Compute sets of loads generated and killed by each block.
// Additionally compute upwards exposed and generated loads for each block.
// Exposed loads are those that can be replaced if a corresponding
@@ -2142,7 +2208,7 @@
defn->ssa_temp_index(), replacement->ssa_temp_index());
}
- defn->ReplaceUsesWith(replacement);
+ ReplaceLoad(defn, replacement);
instr_it.RemoveCurrentFromGraph();
forwarded_ = true;
continue;
@@ -2515,7 +2581,7 @@
load->ssa_temp_index(), replacement->ssa_temp_index());
}
- load->ReplaceUsesWith(replacement);
+ replacement = ReplaceLoad(load, replacement);
load->RemoveFromGraph();
load->SetReplacement(replacement);
forwarded_ = true;
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index 8e110dd..fbae33b 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -366,9 +366,14 @@
}
void FlowGraphTypePropagator::VisitAssertAssignable(
- AssertAssignableInstr* instr) {
- SetTypeOf(instr->value()->definition(),
- new (zone()) CompileType(instr->ComputeType()));
+ AssertAssignableInstr* check) {
+ auto defn = check->value()->definition();
+ SetTypeOf(defn, new (zone()) CompileType(check->ComputeType()));
+ if (check->ssa_temp_index() == -1) {
+ flow_graph_->AllocateSSAIndexes(check);
+ GrowTypes(check->ssa_temp_index() + 1);
+ }
+ FlowGraph::RenameDominatedUses(defn, check, check);
}
void FlowGraphTypePropagator::VisitAssertBoolean(AssertBooleanInstr* instr) {
diff --git a/runtime/vm/compiler/compiler_pass.cc b/runtime/vm/compiler/compiler_pass.cc
index 0d91ec1..4bbce2c 100644
--- a/runtime/vm/compiler/compiler_pass.cc
+++ b/runtime/vm/compiler/compiler_pass.cc
@@ -75,8 +75,9 @@
}
CompilerPass* CompilerPass::passes_[CompilerPass::kNumPasses] = {NULL};
+uint8_t CompilerPass::flags_[CompilerPass::kNumPasses] = {0};
-DEFINE_OPTION_HANDLER(CompilerPass::ParseFilters,
+DEFINE_OPTION_HANDLER(CompilerPass::ParseFiltersFromFlag,
compiler_passes,
"List of comma separated compilation passes flags. "
"Use -Name to disable a pass, Name to print IL after it. "
@@ -110,7 +111,18 @@
"\n"
"List of compiler passes:\n";
-void CompilerPass::ParseFilters(const char* filter) {
+void CompilerPass::ParseFiltersFromFlag(const char* filter) {
+ ParseFilters(filter, flags_);
+}
+
+uint8_t* CompilerPass::ParseFiltersFromPragma(const char* filter) {
+ auto flags =
+ ThreadState::Current()->zone()->Alloc<uint8_t>(CompilerPass::kNumPasses);
+ ParseFilters(filter, flags);
+ return flags;
+}
+
+void CompilerPass::ParseFilters(const char* filter, uint8_t* pass_flags) {
if (filter == NULL || *filter == 0) {
return;
}
@@ -126,11 +138,7 @@
}
// Clear all flags.
- for (intptr_t i = 0; i < kNumPasses; i++) {
- if (passes_[i] != NULL) {
- passes_[i]->flags_ = 0;
- }
- }
+ memset(pass_flags, 0, CompilerPass::kNumPasses);
for (const char *start = filter, *end = filter; *end != 0;
start = (end + 1)) {
@@ -144,54 +152,58 @@
continue;
}
- uint8_t flags = 0;
- if (*start == '-') {
- flags = kDisabled;
- } else if (*start == ']') {
- flags = kTraceAfter;
- } else if (*start == '[') {
- flags = kTraceBefore;
- } else if (*start == '*') {
- flags = kTraceBeforeOrAfter;
+ ParseOneFilter(start, end, pass_flags);
+ }
+}
+
+void CompilerPass::ParseOneFilter(const char* start,
+ const char* end,
+ uint8_t* pass_flags) {
+ uint8_t flags = 0;
+ if (*start == '-') {
+ flags = kDisabled;
+ } else if (*start == ']') {
+ flags = kTraceAfter;
+ } else if (*start == '[') {
+ flags = kTraceBefore;
+ } else if (*start == '*') {
+ flags = kTraceBeforeOrAfter;
+ }
+ if (flags == 0) {
+ flags |= kTraceAfter;
+ } else {
+ start++; // Skip the modifier
+ }
+
+ size_t suffix = 0;
+ if (end[-1] == '+') {
+ if (start == (end - 1)) {
+ OS::PrintErr("Sticky modifier '+' should follow pass name\n");
+ return;
}
- if (flags == 0) {
- flags |= kTraceAfter;
+ flags |= kSticky;
+ suffix = 1;
+ }
+
+ size_t length = (end - start) - suffix;
+ if (length != 0) {
+ char* pass_name = Utils::StrNDup(start, length);
+ CompilerPass* pass = FindPassByName(pass_name);
+ if (pass != NULL) {
+ pass_flags[pass->id()] |= flags;
} else {
- start++; // Skip the modifier
+ OS::PrintErr("Unknown compiler pass: %s\n", pass_name);
}
-
- size_t suffix = 0;
- if (end[-1] == '+') {
- if (start == (end - 1)) {
- OS::PrintErr("Sticky modifier '+' should follow pass name\n");
- continue;
- }
- flags |= kSticky;
- suffix = 1;
- }
-
- size_t length = (end - start) - suffix;
- if (length != 0) {
- char* pass_name = Utils::StrNDup(start, length);
- CompilerPass* pass = FindPassByName(pass_name);
- if (pass != NULL) {
- pass->flags_ |= flags;
- } else {
- OS::PrintErr("Unknown compiler pass: %s\n", pass_name);
- }
- free(pass_name);
- } else if (flags == kTraceBeforeOrAfter) {
- for (intptr_t i = 0; i < kNumPasses; i++) {
- if (passes_[i] != NULL) {
- passes_[i]->flags_ = kTraceAfter;
- }
- }
+ free(pass_name);
+ } else if (flags == kTraceBeforeOrAfter) {
+ for (intptr_t i = 0; i < kNumPasses; i++) {
+ pass_flags[i] = kTraceAfter;
}
}
}
void CompilerPass::Run(CompilerPassState* state) const {
- if (IsFlagSet(kDisabled)) {
+ if ((flags() & kDisabled) != 0) {
return;
}
@@ -231,8 +243,11 @@
void CompilerPass::PrintGraph(CompilerPassState* state,
Flag mask,
intptr_t round) const {
- const intptr_t current_flags = flags() | state->sticky_flags;
FlowGraph* flow_graph = state->flow_graph();
+ const uint8_t* graph_flags = flow_graph->compiler_pass_filters();
+ const uint8_t current_flags =
+ (graph_flags != nullptr ? graph_flags[id()] : flags()) |
+ state->sticky_flags;
if ((FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) &&
flow_graph->should_print() && ((current_flags & mask) != 0)) {
@@ -558,6 +573,7 @@
flow_graph->function().set_inlining_depth(state->inlining_depth);
// Remove redefinitions for the rest of the pipeline.
flow_graph->RemoveRedefinitions();
+ flow_graph->Canonicalize();
});
COMPILER_PASS(GenerateCode, { state->graph_compiler->CompileGraph(); });
diff --git a/runtime/vm/compiler/compiler_pass.h b/runtime/vm/compiler/compiler_pass.h
index 9afa960..01a7194 100644
--- a/runtime/vm/compiler/compiler_pass.h
+++ b/runtime/vm/compiler/compiler_pass.h
@@ -115,13 +115,15 @@
static constexpr intptr_t kNumPasses = 0 COMPILER_PASS_LIST(ADD_ONE);
#undef ADD_ONE
- CompilerPass(Id id, const char* name) : id_(id), name_(name), flags_(0) {
+ CompilerPass(Id id, const char* name) : id_(id), name_(name) {
ASSERT(passes_[id] == NULL);
passes_[id] = this;
// By default print the final flow-graph after the register allocation.
if (id == kAllocateRegisters) {
- flags_ = kTraceAfter;
+ flags_[id] = kTraceAfter;
+ } else {
+ flags_[id] = 0;
}
}
virtual ~CompilerPass() {}
@@ -136,15 +138,18 @@
void Run(CompilerPassState* state) const;
- intptr_t flags() const { return flags_; }
+ uint8_t flags() const { return flags_[id()]; }
const char* name() const { return name_; }
Id id() const { return id_; }
- bool IsFlagSet(Flag flag) const { return (flags() & flag) != 0; }
-
static CompilerPass* Get(Id id) { return passes_[id]; }
- static void ParseFilters(const char* filter);
+ static void ParseFiltersFromFlag(const char* filter);
+ static uint8_t* ParseFiltersFromPragma(const char* filter);
+ static void ParseFilters(const char* filter, uint8_t* flags);
+ static void ParseOneFilter(const char* start,
+ const char* end,
+ uint8_t* flags);
enum PipelineMode { kJIT, kAOT };
@@ -196,10 +201,10 @@
void PrintGraph(CompilerPassState* state, Flag mask, intptr_t round) const;
static CompilerPass* passes_[];
+ static uint8_t flags_[];
Id id_;
const char* name_;
- intptr_t flags_;
};
} // namespace dart
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 08e04f9..c1daced 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -1627,11 +1627,13 @@
const InferredTypeMetadata* result_type,
bool use_unchecked_entry,
const CallSiteAttributesMetadata* call_site_attrs,
- bool receiver_is_not_smi) {
+ bool receiver_is_not_smi,
+ bool is_call_on_this) {
return flow_graph_builder_->InstanceCall(
position, name, kind, type_args_len, argument_count, argument_names,
checked_argument_count, interface_target, tearoff_interface_target,
- result_type, use_unchecked_entry, call_site_attrs, receiver_is_not_smi);
+ result_type, use_unchecked_entry, call_site_attrs, receiver_is_not_smi,
+ is_call_on_this);
}
Fragment StreamingFlowGraphBuilder::ThrowException(TokenPosition position) {
@@ -2420,7 +2422,8 @@
const TokenPosition position = ReadPosition(); // read position.
if (p != nullptr) *p = position;
- if (PeekTag() == kThisExpression) {
+ const bool is_call_on_this = PeekTag() == kThisExpression;
+ if (is_call_on_this) {
is_unchecked_call = true;
}
instructions += BuildExpression(); // read receiver.
@@ -2464,7 +2467,8 @@
Array::null_array(), kNumArgsChecked, interface_target,
Function::null_function(),
/*result_type=*/nullptr,
- /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes);
+ /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes,
+ /*receiver_not_smi=*/false, is_call_on_this);
}
instructions += Drop(); // Drop result of the setter invocation.
@@ -2928,7 +2932,8 @@
// Take note of whether the invocation is against the receiver of the current
// function: in this case, we may skip some type checks in the callee.
- if ((PeekTag() == kThisExpression) && !is_dynamic) {
+ const bool is_call_on_this = (PeekTag() == kThisExpression) && !is_dynamic;
+ if (is_call_on_this) {
is_unchecked_call = true;
}
instructions += BuildExpression(); // read receiver.
@@ -3035,12 +3040,12 @@
argument_names, ICData::kNoRebind, &result_type,
type_args_len, /*use_unchecked_entry=*/is_unchecked_call);
} else {
- instructions +=
- InstanceCall(position, *mangled_name, token_kind, type_args_len,
- argument_count, argument_names, checked_argument_count,
- *interface_target, Function::null_function(), &result_type,
- /*use_unchecked_entry=*/is_unchecked_call,
- &call_site_attributes, result_type.ReceiverNotInt());
+ instructions += InstanceCall(
+ position, *mangled_name, token_kind, type_args_len, argument_count,
+ argument_names, checked_argument_count, *interface_target,
+ Function::null_function(), &result_type,
+ /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes,
+ result_type.ReceiverNotInt(), is_call_on_this);
}
// Drop temporaries preserving result on the top of the stack.
@@ -3880,12 +3885,10 @@
// We do not care whether the 'as' cast as implicitly added by the frontend
// or explicitly written by the user, in both cases we use an assert
// assignable.
- instructions += LoadLocal(MakeTemporary());
instructions += B->AssertAssignableLoadTypeArguments(
position, type,
is_type_error ? Symbols::Empty() : Symbols::InTypeCast(),
AssertAssignableInstr::kInsertedByFrontend);
- instructions += Drop();
}
return instructions;
}
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index 0f40918..47ed58b 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -199,7 +199,8 @@
const InferredTypeMetadata* result_type = nullptr,
bool use_unchecked_entry = false,
const CallSiteAttributesMetadata* call_site_attrs = nullptr,
- bool receiver_is_not_smi = false);
+ bool receiver_is_not_smi = false,
+ bool is_call_on_this = false);
Fragment ThrowException(TokenPosition position);
Fragment BooleanNegate();
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index efd2f8e..c993d10 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -347,7 +347,8 @@
const InferredTypeMetadata* result_type,
bool use_unchecked_entry,
const CallSiteAttributesMetadata* call_site_attrs,
- bool receiver_is_not_smi) {
+ bool receiver_is_not_smi,
+ bool is_call_on_this) {
const intptr_t total_count = argument_count + (type_args_len > 0 ? 1 : 0);
InputsArray* arguments = GetArguments(total_count);
InstanceCallInstr* call = new (Z) InstanceCallInstr(
@@ -360,6 +361,9 @@
if (use_unchecked_entry) {
call->set_entry_kind(Code::EntryKind::kUnchecked);
}
+ if (is_call_on_this) {
+ call->mark_as_call_on_this();
+ }
if (call_site_attrs != nullptr && call_site_attrs->receiver_type != nullptr &&
call_site_attrs->receiver_type->IsInstantiated()) {
call->set_receivers_static_type(call_site_attrs->receiver_type);
@@ -1769,7 +1773,7 @@
if (auto const alloc = definition->AsAllocateClosure()) {
return !alloc->known_function().IsNull();
}
- return definition->IsLoadLocal();
+ return definition->IsLoadLocal() || definition->IsAssertAssignable();
}
Fragment FlowGraphBuilder::EvaluateAssertion() {
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 6dfc141..42573d0 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -191,7 +191,8 @@
const InferredTypeMetadata* result_type = nullptr,
bool use_unchecked_entry = false,
const CallSiteAttributesMetadata* call_site_attrs = nullptr,
- bool receiver_is_not_smi = false);
+ bool receiver_is_not_smi = false,
+ bool is_call_on_this = false);
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index ee81b99..50640ad 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -470,7 +470,8 @@
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
V(vm_unsafe_no_interrupts, "vm:unsafe:no-interrupts") \
- V(vm_external_name, "vm:external-name")
+ V(vm_external_name, "vm:external-name") \
+ V(vm_testing_print_flow_graph, "vm:testing:print-flow-graph")
// Contains a list of frequently used strings in a canonicalized form. This
// list is kept in the vm_isolate in order to share the copy across isolates
diff --git a/tools/VERSION b/tools/VERSION
index fb4f871..c188fa2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 16
+PRERELEASE 17
PRERELEASE_PATCH 0
\ No newline at end of file