Version 2.16.0-21.0.dev
Merge commit '2007dadb9c66c1f859de5c851ea4dc5f072b5746' into 'dev'
diff --git a/pkg/front_end/test/fasta/messages_suite.dart b/pkg/front_end/test/fasta/messages_suite.dart
index 99579c9..df8fbb5 100644
--- a/pkg/front_end/test/fasta/messages_suite.dart
+++ b/pkg/front_end/test/fasta/messages_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:convert" show utf8;
import 'dart:io' show File, Platform;
@@ -66,23 +64,23 @@
final YamlMap data;
- final Example example;
+ final Example? example;
- final String problem;
+ final String? problem;
MessageTestDescription(this.uri, this.shortName, this.name, this.data,
this.example, this.problem);
}
class Configuration {
- final NnbdMode nnbdMode;
+ final NnbdMode? nnbdMode;
final Set<InvocationMode> invocationModes;
const Configuration(this.nnbdMode, this.invocationModes);
CompilerOptions apply(CompilerOptions options) {
if (nnbdMode != null) {
- options.nnbdMode = nnbdMode;
+ options.nnbdMode = nnbdMode!;
}
options.invocationModes = invocationModes;
return options;
@@ -125,7 +123,7 @@
[spell.Dictionaries.cfeMessages],
interactive,
'"$dartPath" "$suitePath" -DfastOnly=true -Dinteractive=true');
- return null;
+ return new Future.value();
}
MessageTestSuite(this.fastOnly, this.interactive)
@@ -151,30 +149,30 @@
Uri uri = suite.uri.resolve("messages.yaml");
File file = new File.fromUri(uri);
String fileContent = file.readAsStringSync();
- YamlMap messages = loadYamlNode(fileContent, sourceUrl: uri);
+ YamlMap messages = loadYamlNode(fileContent, sourceUrl: uri) as YamlMap;
for (String name in messages.keys) {
- YamlNode messageNode = messages.nodes[name];
- var message = messageNode.value;
+ YamlMap messageNode = messages.nodes[name] as YamlMap;
+ dynamic message = messageNode.value;
if (message is String) continue;
List<String> unknownKeys = <String>[];
bool exampleAllowMoreCodes = false;
List<Example> examples = <Example>[];
- String externalTest;
+ String? externalTest;
bool frontendInternal = false;
- List<String> analyzerCodes;
- Severity severity;
- YamlNode badSeverity;
- YamlNode unnecessarySeverity;
+ List<String>? analyzerCodes;
+ Severity? severity;
+ YamlNode? badSeverity;
+ YamlNode? unnecessarySeverity;
List<String> badHasPublishedDocsValue = <String>[];
- List<String> spellingMessages;
+ List<String>? spellingMessages;
const String spellingPostMessage = "\nIf the word(s) look okay, update "
"'spell_checking_list_messages.txt' or "
"'spell_checking_list_common.txt'.";
- Configuration configuration;
- Map<ExperimentalFlag, bool> experimentalFlags;
+ Configuration? configuration;
+ Map<ExperimentalFlag, bool>? experimentalFlags;
- Source source;
+ Source? source;
List<String> formatSpellingMistakes(spell.SpellingResult spellResult,
int offset, String message, String messageForDenyListed) {
if (source == null) {
@@ -189,23 +187,23 @@
source = new Source(lineStarts, bytes, uri, uri);
}
List<String> result = <String>[];
- for (int i = 0; i < spellResult.misspelledWords.length; i++) {
- Location location = source.getLocation(
- uri, offset + spellResult.misspelledWordsOffset[i]);
- bool denylisted = spellResult.misspelledWordsDenylisted[i];
+ for (int i = 0; i < spellResult.misspelledWords!.length; i++) {
+ Location location = source!
+ .getLocation(uri, offset + spellResult.misspelledWordsOffset![i]);
+ bool denylisted = spellResult.misspelledWordsDenylisted![i];
String messageToUse = message;
if (denylisted) {
messageToUse = messageForDenyListed;
- reportedWordsDenylisted.add(spellResult.misspelledWords[i]);
+ reportedWordsDenylisted.add(spellResult.misspelledWords![i]);
} else {
- reportedWords.add(spellResult.misspelledWords[i]);
+ reportedWords.add(spellResult.misspelledWords![i]);
}
result.add(command_line_reporting.formatErrorMessage(
- source.getTextLine(location.line),
+ source!.getTextLine(location.line),
location,
- spellResult.misspelledWords[i].length,
+ spellResult.misspelledWords![i].length,
relativize(uri),
- "$messageToUse: '${spellResult.misspelledWords[i]}'."));
+ "$messageToUse: '${spellResult.misspelledWords![i]}'."));
}
return result;
}
@@ -280,11 +278,12 @@
break;
case "bytes":
- YamlList list = node;
+ YamlList list = node as YamlList;
if (list.first is List) {
- for (YamlList bytes in list.nodes) {
+ for (YamlNode bytes in list.nodes) {
int i = 0;
- examples.add(new BytesExample("bytes${++i}", name, bytes));
+ examples.add(
+ new BytesExample("bytes${++i}", name, bytes as YamlList));
}
} else {
examples.add(new BytesExample("bytes", name, list));
@@ -355,7 +354,7 @@
case "configuration":
if (value is String) {
- NnbdMode nnbdMode;
+ NnbdMode? nnbdMode;
Set<InvocationMode> invocationModes = {};
for (String part in value.split(',')) {
if (part.isEmpty) continue;
@@ -364,7 +363,8 @@
} else if (part == "nnbd-strong") {
nnbdMode = NnbdMode.Strong;
} else {
- InvocationMode invocationMode = InvocationMode.fromName(part);
+ InvocationMode? invocationMode =
+ InvocationMode.fromName(part);
if (invocationMode != null) {
invocationModes.add(invocationMode);
} else {
@@ -418,7 +418,7 @@
}
MessageTestDescription createDescription(
- String subName, Example example, String problem,
+ String subName, Example? example, String? problem,
{location}) {
String shortName = "$name/$subName";
if (problem != null) {
@@ -467,7 +467,7 @@
badSeverity != null
? "Unknown severity: '${badSeverity.value}'."
: null,
- location: badSeverity?.span?.start);
+ location: badSeverity?.span.start);
yield createDescription(
"unnecessarySeverity",
@@ -475,7 +475,7 @@
unnecessarySeverity != null
? "The 'ERROR' severity is the default and not necessary."
: null,
- location: unnecessarySeverity?.span?.start);
+ location: unnecessarySeverity?.span.start);
yield createDescription(
"spelling",
@@ -528,7 +528,7 @@
var span = example.node.span;
StringBuffer buffer = new StringBuffer();
buffer
- ..write(relativize(span.sourceUrl))
+ ..write(relativize(span.sourceUrl!))
..write(":")
..write(span.start.line + 1)
..write(":")
@@ -537,7 +537,7 @@
..write(message);
buffer.write("\n${span.text}");
for (DiagnosticMessage message in messages) {
- buffer.write("\nCode: ${getMessageCodeObject(message).name}");
+ buffer.write("\nCode: ${getMessageCodeObject(message)!.name}");
buffer.write("\n > ");
buffer.write(
message.plainTextFormatted.join("\n").replaceAll("\n", "\n > "));
@@ -554,9 +554,9 @@
bool allowMoreCodes = false;
- Configuration configuration;
+ late Configuration configuration;
- Map<ExperimentalFlag, bool> experimentalFlags;
+ Map<ExperimentalFlag, bool>? experimentalFlags;
Example(this.name, this.expectedCode);
@@ -677,7 +677,7 @@
});
return scriptFiles;
} else {
- return {mainFilename: new Script.fromSource(script)};
+ return {mainFilename: new Script.fromSource(script as String)};
}
}
}
@@ -709,7 +709,7 @@
throw "Framework failure: "
"Wanted to create wrapper file, but the file already exists!";
}
- Script originalMainScript = scriptFiles[example.mainFilename];
+ Script originalMainScript = scriptFiles[example.mainFilename]!;
String preamble = originalMainScript.preamble;
scriptFiles[mainFilename] = new Script.fromSource("""
${preamble}part "${example.mainFilename}";
@@ -718,7 +718,7 @@
// Modify the original main file to be part of the wrapper and add lots of
// gunk so every actual position in the file is not a valid position in the
// wrapper.
- String originalMainSource = originalMainScript.sourceWithoutPreamble;
+ String? originalMainSource = originalMainScript.sourceWithoutPreamble;
String partPrefix = """
${preamble}part of "${mainFilename}";
// La la la la la la la la la la la la la.
@@ -746,14 +746,15 @@
YamlNode get node => example.node;
}
-class Validate extends Step<MessageTestDescription, Example, MessageTestSuite> {
+class Validate
+ extends Step<MessageTestDescription, Example?, MessageTestSuite> {
const Validate();
@override
String get name => "validate";
@override
- Future<Result<Example>> run(
+ Future<Result<Example?>> run(
MessageTestDescription description, MessageTestSuite suite) {
if (description.problem != null) {
return new Future.value(fail(null, description.problem));
@@ -763,14 +764,14 @@
}
}
-class Compile extends Step<Example, Null, MessageTestSuite> {
+class Compile extends Step<Example?, Null, MessageTestSuite> {
const Compile();
@override
String get name => "compile";
@override
- Future<Result<Null>> run(Example example, MessageTestSuite suite) async {
+ Future<Result<Null>> run(Example? example, MessageTestSuite suite) async {
if (example == null) return pass(null);
String dir = "${example.expectedCode}/${example.name}";
example.scripts.forEach((String fileName, Script script) {
@@ -812,14 +813,14 @@
if (example.allowMoreCodes) {
List<DiagnosticMessage> messagesFiltered = <DiagnosticMessage>[];
for (DiagnosticMessage message in messages) {
- if (getMessageCodeObject(message).name == example.expectedCode) {
+ if (getMessageCodeObject(message)!.name == example.expectedCode) {
messagesFiltered.add(message);
}
}
messages = messagesFiltered;
}
for (DiagnosticMessage message in messages) {
- if (getMessageCodeObject(message).name != example.expectedCode) {
+ if (getMessageCodeObject(message)!.name != example.expectedCode) {
unexpectedMessages.add(message);
}
}
@@ -868,7 +869,7 @@
class Script {
final Uint8List bytes;
final String preamble;
- final String sourceWithoutPreamble;
+ final String? sourceWithoutPreamble;
Script(this.bytes, this.preamble, this.sourceWithoutPreamble);
diff --git a/pkg/front_end/test/fasta/object_supertype_test.dart b/pkg/front_end/test/fasta/object_supertype_test.dart
index b588191..819a3a3 100644
--- a/pkg/front_end/test/fasta/object_supertype_test.dart
+++ b/pkg/front_end/test/fasta/object_supertype_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:convert" show json;
import "package:_fe_analyzer_shared/src/messages/diagnostic_message.dart"
@@ -77,13 +75,13 @@
Future<void> test() async {
Set<String> normalErrors = (await outline("class Object {"))
- .map((DiagnosticMessage message) => getMessageCodeObject(message).name)
+ .map((DiagnosticMessage message) => getMessageCodeObject(message)!.name)
.toSet();
Future<void> check(String objectHeader, List<Code> expectedCodes) async {
List<DiagnosticMessage> messages = (await outline(objectHeader))
.where((DiagnosticMessage message) =>
- !normalErrors.contains(getMessageCodeObject(message).name))
+ !normalErrors.contains(getMessageCodeObject(message)!.name))
.toList();
Expect.setEquals(
expectedCodes,
diff --git a/pkg/front_end/test/fasta/reexport_test.dart b/pkg/front_end/test/fasta/reexport_test.dart
index 5972e6a..0e46523 100644
--- a/pkg/front_end/test/fasta/reexport_test.dart
+++ b/pkg/front_end/test/fasta/reexport_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" show compileUnit;
diff --git a/pkg/front_end/test/fasta/sdk_test.dart b/pkg/front_end/test/fasta/sdk_test.dart
index 54caf22..c448d5d 100644
--- a/pkg/front_end/test/fasta/sdk_test.dart
+++ b/pkg/front_end/test/fasta/sdk_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.sdk_test;
import 'testing/suite.dart';
diff --git a/pkg/front_end/test/fasta/super_mixins_test.dart b/pkg/front_end/test/fasta/super_mixins_test.dart
index 8f97ec7..2ef4893 100644
--- a/pkg/front_end/test/fasta/super_mixins_test.dart
+++ b/pkg/front_end/test/fasta/super_mixins_test.dart
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
-// @dart = 2.9
-
library fasta.test.incremental_dynamic_test;
import "package:_fe_analyzer_shared/src/messages/diagnostic_message.dart"
@@ -83,7 +81,7 @@
Expect.equals(Severity.error, message.severity);
Expect.identical(codeSuperclassHasNoMethod, getMessageCodeObject(message));
Expect.isTrue(message.plainTextFormatted.length == 1);
- names.add(getMessageArguments(message)['name']);
+ names.add(getMessageArguments(message)!['name']);
};
}
diff --git a/pkg/front_end/tool/_fasta/entry_points.dart b/pkg/front_end/tool/_fasta/entry_points.dart
index 2aa92c9..70b5606 100644
--- a/pkg/front_end/tool/_fasta/entry_points.dart
+++ b/pkg/front_end/tool/_fasta/entry_points.dart
@@ -126,7 +126,7 @@
}
class BatchCompiler {
- final Stream<String> lines;
+ final Stream<String>? lines;
Uri? platformUri;
@@ -137,7 +137,7 @@
BatchCompiler(this.lines);
Future<void> run() async {
- await for (String line in lines) {
+ await for (String line in lines!) {
try {
if (await batchCompileArguments(
new List<String>.from(jsonDecode(line)))) {
diff --git a/pkg/vm/bin/compare_il.dart b/pkg/vm/bin/compare_il.dart
index 8196a5e..5b9caa3 100644
--- a/pkg/vm/bin/compare_il.dart
+++ b/pkg/vm/bin/compare_il.dart
@@ -138,7 +138,7 @@
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);
+ FlowGraph(m['b'], m['desc'], m['flags'], rename: rename);
}
return graphs;
diff --git a/pkg/vm/lib/testing/il_matchers.dart b/pkg/vm/lib/testing/il_matchers.dart
index dd5eb8f..1c2f883 100644
--- a/pkg/vm/lib/testing/il_matchers.dart
+++ b/pkg/vm/lib/testing/il_matchers.dart
@@ -12,14 +12,18 @@
class FlowGraph {
final List<dynamic> blocks;
final Map<String, InstructionDescriptor> descriptors;
+ final Map<String, dynamic> flags;
final Renamer rename;
- FlowGraph(this.blocks, Map<String, dynamic> desc, {required this.rename})
+ FlowGraph(this.blocks, Map<String, dynamic> desc, this.flags,
+ {required this.rename})
: descriptors = {
for (var e in desc.entries)
e.key: InstructionDescriptor.fromJson(e.value)
};
+ bool get soundNullSafety => flags['nnbd'];
+
/// 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
@@ -33,11 +37,34 @@
env ??= Env(rename: rename, descriptors: descriptors);
for (var i = 0; i < expected.length; i++) {
- expected[i].match(env, blocks[i]).expectMatched('failed to match');
+ final result = expected[i].match(env, blocks[i]);
+ if (result.isFail) {
+ print('Failed to match: ${result.message}');
+ dump();
+ throw 'Failed to match';
+ }
}
return env;
}
+
+ void dump() {
+ for (var block in blocks) {
+ print('B${block['b']}[${block['o']}]');
+ for (var instr in [...?block['d'], ...?block['is']]) {
+ final v = instr['v'] ?? -1;
+ final prefix = v != -1 ? 'v$v <- ' : '';
+ final inputs = instr['i']?.map((v) => 'v$v').join(', ') ?? '';
+ final attrs = descriptors[instr['o']]
+ ?.attributeIndex
+ .entries
+ .map((e) => '${e.key}: ${instr['d'][e.value]}')
+ .join(',');
+ final attrsWrapped = attrs != null ? '[$attrs]' : '';
+ print(' ${prefix}${instr['o']}$attrsWrapped($inputs)');
+ }
+ }
+ }
}
class InstructionDescriptor {
@@ -304,6 +331,11 @@
.toList());
return impl!.match(e, v);
}
+
+ @override
+ String toString() {
+ return matchers.toString();
+ }
}
/// Matcher which matches an instruction with opcode [op] and properties
diff --git a/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart b/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart
index 722da15..afa6bcf 100644
--- a/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart
+++ b/runtime/tests/vm/dart/flutter_regress_91370_il_test.dart
@@ -221,8 +221,8 @@
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());
+ final a1 = A1("$i");
+ final a0 = A0(i.toDouble(), "$i");
B1(a1).testNarrowingThroughThisCallWithPositionalParam(H(a1));
B0(a0).testNarrowingThroughThisCallWithPositionalParam(H(a0));
B1(a1).testNarrowingThroughThisCallWithNamedParams(H(a1));
@@ -601,7 +601,6 @@
]),
'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'),
@@ -617,8 +616,13 @@
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'),
+ 'a.data[0]*' << match.Redefinition('a.data[0]'),
+ if (!beforeLICM.soundNullSafety)
+ 'a.data[0]*!' << match.CheckNull('a.data[0]*'),
+ 'a.data[0].str' <<
+ match.LoadField(
+ beforeLICM.soundNullSafety ? 'a.data[0]*' : 'a.data[0]*!',
+ slot: 'str'),
]),
]);
@@ -632,7 +636,6 @@
]),
'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'),
@@ -646,7 +649,12 @@
]),
'B3' <<
match.block('Target', [
- 'a.data[0].str' << match.LoadField('a.data[0]*', slot: 'str'),
+ if (!beforeLICM.soundNullSafety)
+ 'a.data[0]!' << match.CheckNull('a.data[0]'),
+ 'a.data[0].str' <<
+ match.LoadField(
+ beforeLICM.soundNullSafety ? 'a.data[0]' : 'a.data[0]!',
+ slot: 'str'),
]),
], env: env);
}
@@ -668,7 +676,6 @@
]),
'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'),
@@ -684,8 +691,11 @@
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'),
+ 'a[0]*' << match.Redefinition('a[0]'),
+ if (!beforeLICM.soundNullSafety) 'a[0]*!' << match.CheckNull('a[0]*'),
+ 'a[0].str' <<
+ match.LoadField(beforeLICM.soundNullSafety ? 'a[0]*' : 'a[0]*!',
+ slot: 'str'),
]),
]);
@@ -698,7 +708,6 @@
]),
'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'),
@@ -712,7 +721,10 @@
]),
'B3' <<
match.block('Target', [
- 'a[0].str' << match.LoadField('a[0]*', slot: 'str'),
+ if (!beforeLICM.soundNullSafety) 'a[0]!' << match.CheckNull('a[0]'),
+ 'a[0].str' <<
+ match.LoadField(beforeLICM.soundNullSafety ? 'a[0]' : 'a[0]!',
+ slot: 'str'),
]),
], env: env);
}
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
index 722da15..afa6bcf 100644
--- a/runtime/tests/vm/dart_2/flutter_regress_91370_il_test.dart
+++ b/runtime/tests/vm/dart_2/flutter_regress_91370_il_test.dart
@@ -221,8 +221,8 @@
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());
+ final a1 = A1("$i");
+ final a0 = A0(i.toDouble(), "$i");
B1(a1).testNarrowingThroughThisCallWithPositionalParam(H(a1));
B0(a0).testNarrowingThroughThisCallWithPositionalParam(H(a0));
B1(a1).testNarrowingThroughThisCallWithNamedParams(H(a1));
@@ -601,7 +601,6 @@
]),
'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'),
@@ -617,8 +616,13 @@
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'),
+ 'a.data[0]*' << match.Redefinition('a.data[0]'),
+ if (!beforeLICM.soundNullSafety)
+ 'a.data[0]*!' << match.CheckNull('a.data[0]*'),
+ 'a.data[0].str' <<
+ match.LoadField(
+ beforeLICM.soundNullSafety ? 'a.data[0]*' : 'a.data[0]*!',
+ slot: 'str'),
]),
]);
@@ -632,7 +636,6 @@
]),
'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'),
@@ -646,7 +649,12 @@
]),
'B3' <<
match.block('Target', [
- 'a.data[0].str' << match.LoadField('a.data[0]*', slot: 'str'),
+ if (!beforeLICM.soundNullSafety)
+ 'a.data[0]!' << match.CheckNull('a.data[0]'),
+ 'a.data[0].str' <<
+ match.LoadField(
+ beforeLICM.soundNullSafety ? 'a.data[0]' : 'a.data[0]!',
+ slot: 'str'),
]),
], env: env);
}
@@ -668,7 +676,6 @@
]),
'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'),
@@ -684,8 +691,11 @@
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'),
+ 'a[0]*' << match.Redefinition('a[0]'),
+ if (!beforeLICM.soundNullSafety) 'a[0]*!' << match.CheckNull('a[0]*'),
+ 'a[0].str' <<
+ match.LoadField(beforeLICM.soundNullSafety ? 'a[0]*' : 'a[0]*!',
+ slot: 'str'),
]),
]);
@@ -698,7 +708,6 @@
]),
'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'),
@@ -712,7 +721,10 @@
]),
'B3' <<
match.block('Target', [
- 'a[0].str' << match.LoadField('a[0]*', slot: 'str'),
+ if (!beforeLICM.soundNullSafety) 'a[0]!' << match.CheckNull('a[0]'),
+ 'a[0].str' <<
+ match.LoadField(beforeLICM.soundNullSafety ? 'a[0]' : 'a[0]!',
+ slot: 'str'),
]),
], env: env);
}
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 11fe806..7049265 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -5686,43 +5686,48 @@
class ProgramSerializationRoots : public SerializationRoots {
public:
+#define RESET_ROOT_LIST(V) \
+ V(symbol_table, Array, HashTables::New<CanonicalStringSet>(4)) \
+ V(canonical_types, Array, HashTables::New<CanonicalTypeSet>(4)) \
+ V(canonical_function_types, Array, \
+ HashTables::New<CanonicalFunctionTypeSet>(4)) \
+ V(canonical_type_arguments, Array, \
+ HashTables::New<CanonicalTypeArgumentsSet>(4)) \
+ V(canonical_type_parameters, Array, \
+ HashTables::New<CanonicalTypeParameterSet>(4)) \
+ ONLY_IN_PRODUCT(ONLY_IN_AOT( \
+ V(closure_functions, GrowableObjectArray, GrowableObjectArray::null())))
+
ProgramSerializationRoots(ZoneGrowableArray<Object*>* base_objects,
ObjectStore* object_store,
Snapshot::Kind snapshot_kind)
: base_objects_(base_objects),
object_store_(object_store),
- dispatch_table_entries_(Array::Handle()),
- saved_symbol_table_(Array::Handle()),
- saved_canonical_types_(Array::Handle()),
- saved_canonical_function_types_(Array::Handle()),
- saved_canonical_type_arguments_(Array::Handle()),
- saved_canonical_type_parameters_(Array::Handle()) {
- saved_symbol_table_ = object_store->symbol_table();
- object_store->set_symbol_table(
- Array::Handle(HashTables::New<CanonicalStringSet>(4)));
- saved_canonical_types_ = object_store->canonical_types();
- object_store->set_canonical_types(
- Array::Handle(HashTables::New<CanonicalTypeSet>(4)));
- saved_canonical_function_types_ = object_store->canonical_function_types();
- object_store->set_canonical_function_types(
- Array::Handle(HashTables::New<CanonicalFunctionTypeSet>(4)));
- saved_canonical_type_arguments_ = object_store->canonical_type_arguments();
- object_store->set_canonical_type_arguments(
- Array::Handle(HashTables::New<CanonicalTypeArgumentsSet>(4)));
- saved_canonical_type_parameters_ =
- object_store->canonical_type_parameters();
- object_store->set_canonical_type_parameters(
- Array::Handle(HashTables::New<CanonicalTypeParameterSet>(4)));
+ snapshot_kind_(snapshot_kind) {
+#define ONLY_IN_AOT(code) \
+ if (snapshot_kind_ == Snapshot::kFullAOT) { \
+ code \
+ }
+#define SAVE_AND_RESET_ROOT(name, Type, init) \
+ do { \
+ saved_##name##_ = object_store->name(); \
+ object_store->set_##name(Type::Handle(init)); \
+ } while (0);
+
+ RESET_ROOT_LIST(SAVE_AND_RESET_ROOT)
+#undef SAVE_AND_RESET_ROOT
+#undef ONLY_IN_AOT
}
~ProgramSerializationRoots() {
- object_store_->set_symbol_table(saved_symbol_table_);
- object_store_->set_canonical_types(saved_canonical_types_);
- object_store_->set_canonical_function_types(
- saved_canonical_function_types_);
- object_store_->set_canonical_type_arguments(
- saved_canonical_type_arguments_);
- object_store_->set_canonical_type_parameters(
- saved_canonical_type_parameters_);
+#define ONLY_IN_AOT(code) \
+ if (snapshot_kind_ == Snapshot::kFullAOT) { \
+ code \
+ }
+#define RESTORE_ROOT(name, Type, init) \
+ object_store_->set_##name(saved_##name##_);
+ RESET_ROOT_LIST(RESTORE_ROOT)
+#undef RESTORE_ROOT
+#undef ONLY_IN_AOT
}
void AddBaseObjects(Serializer* s) {
@@ -5776,14 +5781,16 @@
}
private:
- ZoneGrowableArray<Object*>* base_objects_;
- ObjectStore* object_store_;
- Array& dispatch_table_entries_;
- Array& saved_symbol_table_;
- Array& saved_canonical_types_;
- Array& saved_canonical_function_types_;
- Array& saved_canonical_type_arguments_;
- Array& saved_canonical_type_parameters_;
+ ZoneGrowableArray<Object*>* const base_objects_;
+ ObjectStore* const object_store_;
+ const Snapshot::Kind snapshot_kind_;
+ Array& dispatch_table_entries_ = Array::Handle();
+
+#define ONLY_IN_AOT(code) code
+#define DECLARE_FIELD(name, Type, init) Type& saved_##name##_ = Type::Handle();
+ RESET_ROOT_LIST(DECLARE_FIELD)
+#undef DECLARE_FIELD
+#undef ONLY_IN_AOT
};
#endif // !DART_PRECOMPILED_RUNTIME
diff --git a/runtime/vm/closure_functions_cache.cc b/runtime/vm/closure_functions_cache.cc
index ff62680..49badbb 100644
--- a/runtime/vm/closure_functions_cache.cc
+++ b/runtime/vm/closure_functions_cache.cc
@@ -73,7 +73,9 @@
return Function::null();
}
-void ClosureFunctionsCache::AddClosureFunctionLocked(const Function& function) {
+void ClosureFunctionsCache::AddClosureFunctionLocked(
+ const Function& function,
+ bool allow_implicit_closure_functions /* = false */) {
ASSERT(!Compiler::IsBackgroundCompilation());
auto thread = Thread::Current();
@@ -86,7 +88,8 @@
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
ASSERT(!closures.IsNull());
- ASSERT(function.IsNonImplicitClosureFunction());
+ ASSERT(allow_implicit_closure_functions ||
+ function.IsNonImplicitClosureFunction());
closures.Add(function, Heap::kOld);
}
diff --git a/runtime/vm/closure_functions_cache.h b/runtime/vm/closure_functions_cache.h
index 019c12b..c52c2e6 100644
--- a/runtime/vm/closure_functions_cache.h
+++ b/runtime/vm/closure_functions_cache.h
@@ -49,7 +49,13 @@
static FunctionPtr LookupClosureFunctionLocked(const Function& parent,
TokenPosition token_pos);
- static void AddClosureFunctionLocked(const Function& function);
+ // Normally implicit closure functions are not added to this cache, however
+ // during AOT compilation we might add those implicit closure functions
+ // that have their original functions shaken to allow ProgramWalker to
+ // discover them.
+ static void AddClosureFunctionLocked(
+ const Function& function,
+ bool allow_implicit_closure_functions = false);
static intptr_t FindClosureIndex(const Function& needle);
static FunctionPtr ClosureFunctionFromIndex(intptr_t idx);
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 87d430f..9fefdca 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -141,12 +141,11 @@
// The object is a function and symbolic stack traces are enabled.
static constexpr const char* kSymbolicStackTraces =
"needed for symbolic stack traces";
- // The object is a function that is only used via its implicit closure
- // function, into which it was inlined.
- static constexpr const char* kInlinedIntoICF =
- "inlined into implicit closure function";
// The object is a parent function function of a non-inlined local function.
static constexpr const char* kLocalParent = "parent of a local function";
+ // The object is a main function of the root library.
+ static constexpr const char* kMainFunction =
+ "this is main function of the root library";
// The object has an entry point pragma that requires it be retained.
static constexpr const char* kEntryPointPragma = "entry point pragma";
// The function is a target of FFI callback.
@@ -724,13 +723,10 @@
void Precompiler::AddRoots() {
HANDLESCOPE(T);
- // Note that <rootlibrary>.main is not a root. The appropriate main will be
- // discovered through _getMainClosure.
-
AddSelector(Symbols::NoSuchMethod());
-
AddSelector(Symbols::Call()); // For speed, not correctness.
+ // Add main as an entry point.
const Library& lib = Library::Handle(IG->object_store()->root_library());
if (lib.IsNull()) {
const String& msg = String::Handle(
@@ -740,22 +736,26 @@
}
const String& name = String::Handle(String::New("main"));
- const Object& main_closure = Object::Handle(lib.GetFunctionClosure(name));
- if (main_closure.IsClosure()) {
- if (lib.LookupLocalFunction(name) == Function::null()) {
- // Check whether the function is in exported namespace of library, in
- // this case we have to retain the root library caches.
- if (lib.LookupFunctionAllowPrivate(name) != Function::null() ||
- lib.LookupReExport(name) != Object::null()) {
- retain_root_library_caches_ = true;
- }
+ Function& main = Function::Handle(lib.LookupFunctionAllowPrivate(name));
+ if (main.IsNull()) {
+ const Object& obj = Object::Handle(lib.LookupReExport(name));
+ if (obj.IsFunction()) {
+ main ^= obj.ptr();
}
- AddConstObject(Closure::Cast(main_closure));
- } else if (main_closure.IsError()) {
- const Error& error = Error::Cast(main_closure);
- String& msg =
- String::Handle(Z, String::NewFormatted("Cannot find main closure %s\n",
- error.ToErrorCString()));
+ }
+ if (!main.IsNull()) {
+ if (lib.LookupLocalFunction(name) == Function::null()) {
+ retain_root_library_caches_ = true;
+ }
+ AddRetainReason(main, RetainReasons::kMainFunction);
+ AddTypesOf(main);
+ // Create closure object from main.
+ main = main.ImplicitClosureFunction();
+ AddConstObject(Closure::Handle(main.ImplicitStaticClosure()));
+ } else {
+ String& msg = String::Handle(
+ Z, String::NewFormatted("Cannot find main in library %s\n",
+ lib.ToCString()));
Jump(Error::Handle(Z, ApiError::New(msg)));
UNREACHABLE();
}
@@ -1930,25 +1930,25 @@
for (intptr_t j = 0; j < functions.Length(); j++) {
SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
function ^= functions.At(j);
- bool retain = possibly_retained_functions_.ContainsKey(function);
- if (!retain && function.HasImplicitClosureFunction()) {
- // It can happen that all uses of an implicit closure inline their
- // target function, leaving the target function uncompiled. Keep
- // the target function anyway so we can enumerate it to bind its
- // static calls, etc.
- function2 = function.ImplicitClosureFunction();
- retain = function2.HasCode();
- if (retain) {
- AddRetainReason(function, RetainReasons::kInlinedIntoICF);
- }
- }
- if (retain) {
- function.DropUncompiledImplicitClosureFunction();
+ function.DropUncompiledImplicitClosureFunction();
+
+ const bool retained =
+ possibly_retained_functions_.ContainsKey(function);
+ if (retained) {
AddTypesOf(function);
- if (function.HasImplicitClosureFunction()) {
- function2 = function.ImplicitClosureFunction();
- if (possibly_retained_functions_.ContainsKey(function2)) {
- AddTypesOf(function2);
+ }
+ if (function.HasImplicitClosureFunction()) {
+ function2 = function.ImplicitClosureFunction();
+
+ if (possibly_retained_functions_.ContainsKey(function2)) {
+ AddTypesOf(function2);
+ // If function has @pragma('vm:entry-point', 'get') we need to keep
+ // the function itself around so that runtime could find it and
+ // get to the implicit closure through it.
+ if (!retained &&
+ functions_with_entry_point_pragmas_.ContainsKey(function2)) {
+ AddRetainReason(function, RetainReasons::kEntryPointPragma);
+ AddTypesOf(function);
}
}
}
@@ -2129,6 +2129,7 @@
Array& functions = Array::Handle(Z);
Function& function = Function::Handle(Z);
Function& target = Function::Handle(Z);
+ Function& implicit_closure = Function::Handle(Z);
Code& code = Code::Handle(Z);
Object& owner = Object::Handle(Z);
GrowableObjectArray& retained_functions = GrowableObjectArray::Handle(Z);
@@ -2207,6 +2208,17 @@
owner, Smi::Handle(Smi::New(owner.GetClassId())));
code.set_owner(owner);
}
+ if (function.HasImplicitClosureFunction()) {
+ // If we are going to drop the function which has a compiled
+ // implicit closure move the closure itself to the list of closures
+ // attached to the object store so that ProgramVisitor could find it.
+ // The list of closures is going to be dropped during PRODUCT snapshotting
+ // so there is no overhead in doing so.
+ implicit_closure = function.ImplicitClosureFunction();
+ RELEASE_ASSERT(functions_to_retain_.ContainsKey(implicit_closure));
+ ClosureFunctionsCache::AddClosureFunctionLocked(
+ implicit_closure, /*allow_implicit_closure_functions=*/true);
+ }
dropped_function_count_++;
if (FLAG_trace_precompiler) {
THR_Print("Dropping function %s\n",
@@ -2289,6 +2301,9 @@
}
return true; // Continue iteration.
});
+
+ // Note: in PRODUCT mode snapshotter will drop this field when serializing.
+ // This is done in ProgramSerializationRoots.
IG->object_store()->set_closure_functions(retained_functions);
}
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index df13a3a..f7b0d45 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -44,6 +44,9 @@
writer.OpenObject("desc");
AttributesSerializer(&writer).WriteDescriptors();
writer.CloseObject();
+ writer.OpenObject("flags");
+ writer.PrintPropertyBool("nnbd", IsolateGroup::Current()->null_safety());
+ writer.CloseObject();
writer.CloseObject();
THR_Print("%s\n", writer.ToCString());
}
diff --git a/runtime/vm/compiler/compiler_pass.cc b/runtime/vm/compiler/compiler_pass.cc
index 4bbce2c..8e34d21 100644
--- a/runtime/vm/compiler/compiler_pass.cc
+++ b/runtime/vm/compiler/compiler_pass.cc
@@ -357,6 +357,7 @@
INVOKE_PASS(WidenSmiToInt32);
INVOKE_PASS(SelectRepresentations);
INVOKE_PASS(CSE);
+ INVOKE_PASS(Canonicalize);
INVOKE_PASS(LICM);
INVOKE_PASS(TryOptimizePatterns);
INVOKE_PASS(DSE);
@@ -387,6 +388,7 @@
INVOKE_PASS(AllocationSinking_DetachMaterializations);
INVOKE_PASS(EliminateWriteBarriers);
INVOKE_PASS(FinalizeGraph);
+ INVOKE_PASS(Canonicalize);
INVOKE_PASS(AllocateRegisters);
INVOKE_PASS(ReorderBlocks);
return pass_state->flow_graph();
@@ -573,7 +575,6 @@
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/globals.h b/runtime/vm/globals.h
index 2e63f76..46cd706 100644
--- a/runtime/vm/globals.h
+++ b/runtime/vm/globals.h
@@ -73,8 +73,10 @@
#if defined(PRODUCT)
#define NOT_IN_PRODUCT(code)
+#define ONLY_IN_PRODUCT(code) code
#else // defined(PRODUCT)
#define NOT_IN_PRODUCT(code) code
+#define ONLY_IN_PRODUCT(code)
#endif // defined(PRODUCT)
#if defined(DART_PRECOMPILED_RUNTIME) && defined(DART_PRECOMPILER)
@@ -87,13 +89,9 @@
#if defined(DART_PRECOMPILED_RUNTIME)
#define NOT_IN_PRECOMPILED(code)
-#else
-#define NOT_IN_PRECOMPILED(code) code
-#endif // defined(DART_PRECOMPILED_RUNTIME)
-
-#if defined(DART_PRECOMPILED_RUNTIME)
#define ONLY_IN_PRECOMPILED(code) code
#else
+#define NOT_IN_PRECOMPILED(code) code
#define ONLY_IN_PRECOMPILED(code)
#endif // defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index fd8822c..256edc0 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -7483,7 +7483,7 @@
FunctionPtr Function::implicit_closure_function() const {
if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
- IsFieldInitializer() || IsFfiTrampoline()) {
+ IsFieldInitializer() || IsFfiTrampoline() || IsMethodExtractor()) {
return Function::null();
}
const Object& obj = Object::Handle(data());
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 33bede3..9fc1c99 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -103,8 +103,8 @@
EMIT_FIELD_INIT,
EMIT_FIELD_INIT)
#undef EMIT_FIELD_INIT
- unused_field_(0) // Just to prevent a trailing comma.
-{
+ // Just to prevent a trailing comma.
+ unused_field_(0) {
for (ObjectPtr* current = from(); current <= to(); current++) {
*current = Object::null();
}
diff --git a/tools/VERSION b/tools/VERSION
index 8e1e43b..26d24bd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 20
+PRERELEASE 21
PRERELEASE_PATCH 0
\ No newline at end of file