Version 2.15.0-28.0.dev
Merge commit '16e4709922a0abf92240e89ebe573623a01c83aa' into 'dev'
diff --git a/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart b/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart
index 5ffe53f..40f44a5 100644
--- a/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart
+++ b/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart
@@ -5,7 +5,7 @@
import 'dart:isolate';
import 'regexp_benchmark.dart';
-import '../../EventLoopLatencyJson/dart/latency.dart';
+import 'latency.dart';
main() async {
final exitPort = ReceivePort();
diff --git a/benchmarks/EventLoopLatencyRegexp/dart/latency.dart b/benchmarks/EventLoopLatencyRegexp/dart/latency.dart
new file mode 100644
index 0000000..5da9f4e
--- /dev/null
+++ b/benchmarks/EventLoopLatencyRegexp/dart/latency.dart
@@ -0,0 +1,135 @@
+// Copyright (c) 2020, 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 'dart:async';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// Measures event loop responsiveness.
+///
+/// Schedules new timer events, [tickDuration] in the future, and measures how
+/// long it takes for these events to actually arrive.
+///
+/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
+Future<EventLoopLatencyStats> measureEventLoopLatency(
+ Duration tickDuration, int numberOfTicks) {
+ final completer = Completer<EventLoopLatencyStats>();
+
+ final tickDurationInUs = tickDuration.inMicroseconds;
+ final buffer = _TickLatencies(numberOfTicks);
+ final sw = Stopwatch()..start();
+ int lastTimestamp = 0;
+
+ void trigger() {
+ final int currentTimestamp = sw.elapsedMicroseconds;
+
+ // Every tick we missed to schedule we'll add with difference to when we
+ // would've scheduled it and when we became responsive again.
+ bool done = false;
+ while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
+ done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
+ lastTimestamp += tickDurationInUs;
+ }
+
+ if (!done) {
+ lastTimestamp = currentTimestamp;
+ Timer(tickDuration, trigger);
+ } else {
+ completer.complete(buffer.makeStats());
+ }
+ }
+
+ Timer(tickDuration, trigger);
+
+ return completer.future;
+}
+
+/// Result of the event loop latency measurement.
+class EventLoopLatencyStats {
+ /// Minimum latency between scheduling a tick and it's arrival (in ms).
+ final double minLatency;
+
+ /// Average latency between scheduling a tick and it's arrival (in ms).
+ final double avgLatency;
+
+ /// Maximum latency between scheduling a tick and it's arrival (in ms).
+ final double maxLatency;
+
+ /// The 50th percentile (median) (in ms).
+ final double percentile50th;
+
+ /// The 90th percentile (in ms).
+ final double percentile90th;
+
+ /// The 95th percentile (in ms).
+ final double percentile95th;
+
+ /// The 99th percentile (in ms).
+ final double percentile99th;
+
+ /// The maximum RSS of the process.
+ final int maxRss;
+
+ EventLoopLatencyStats(
+ this.minLatency,
+ this.avgLatency,
+ this.maxLatency,
+ this.percentile50th,
+ this.percentile90th,
+ this.percentile95th,
+ this.percentile99th,
+ this.maxRss);
+
+ void report(String name) {
+ print('$name.Min(RunTimeRaw): $minLatency ms.');
+ print('$name.Avg(RunTimeRaw): $avgLatency ms.');
+ print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
+ print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
+ print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
+ print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
+ print('$name.Max(RunTimeRaw): $maxLatency ms.');
+ print('$name.MaxRss(MemoryUse): $maxRss');
+ }
+}
+
+/// Accumulates tick latencies and makes statistics for it.
+class _TickLatencies {
+ final Uint64List _timestamps;
+ int _index = 0;
+
+ _TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
+
+ /// Returns `true` while the buffer has not been filled yet.
+ bool add(int latencyInUs) {
+ _timestamps[_index++] = latencyInUs;
+ return _index < _timestamps.length;
+ }
+
+ EventLoopLatencyStats makeStats() {
+ if (_index != _timestamps.length) {
+ throw 'Buffer has not been fully filled yet.';
+ }
+
+ _timestamps.sort();
+ final length = _timestamps.length;
+ final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
+ final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
+ final int max = _timestamps.fold(0, math.max);
+ final percentile50th = _timestamps[50 * length ~/ 100];
+ final percentile90th = _timestamps[90 * length ~/ 100];
+ final percentile95th = _timestamps[95 * length ~/ 100];
+ final percentile99th = _timestamps[99 * length ~/ 100];
+
+ return EventLoopLatencyStats(
+ min / 1000,
+ avg / 1000,
+ max / 1000,
+ percentile50th / 1000,
+ percentile90th / 1000,
+ percentile95th / 1000,
+ percentile99th / 1000,
+ ProcessInfo.maxRss);
+ }
+}
diff --git a/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart b/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
index dec8cb2..86ef5bc 100644
--- a/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
+++ b/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
@@ -7,7 +7,7 @@
import 'dart:isolate';
import 'json_benchmark.dart';
-import '../../EventLoopLatencyJson/dart2/latency.dart';
+import 'latency.dart';
main() async {
final exitPort = ReceivePort();
diff --git a/benchmarks/EventLoopLatencyRegexp/dart2/latency.dart b/benchmarks/EventLoopLatencyRegexp/dart2/latency.dart
new file mode 100644
index 0000000..44ecae6
--- /dev/null
+++ b/benchmarks/EventLoopLatencyRegexp/dart2/latency.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2020, 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.
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// Measures event loop responsiveness.
+///
+/// Schedules new timer events, [tickDuration] in the future, and measures how
+/// long it takes for these events to actually arrive.
+///
+/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
+Future<EventLoopLatencyStats> measureEventLoopLatency(
+ Duration tickDuration, int numberOfTicks) {
+ final completer = Completer<EventLoopLatencyStats>();
+
+ final tickDurationInUs = tickDuration.inMicroseconds;
+ final buffer = _TickLatencies(numberOfTicks);
+ final sw = Stopwatch()..start();
+ int lastTimestamp = 0;
+
+ void trigger() {
+ final int currentTimestamp = sw.elapsedMicroseconds;
+
+ // Every tick we missed to schedule we'll add with difference to when we
+ // would've scheduled it and when we became responsive again.
+ bool done = false;
+ while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
+ done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
+ lastTimestamp += tickDurationInUs;
+ }
+
+ if (!done) {
+ lastTimestamp = currentTimestamp;
+ Timer(tickDuration, trigger);
+ } else {
+ completer.complete(buffer.makeStats());
+ }
+ }
+
+ Timer(tickDuration, trigger);
+
+ return completer.future;
+}
+
+/// Result of the event loop latency measurement.
+class EventLoopLatencyStats {
+ /// Minimum latency between scheduling a tick and it's arrival (in ms).
+ final double minLatency;
+
+ /// Average latency between scheduling a tick and it's arrival (in ms).
+ final double avgLatency;
+
+ /// Maximum latency between scheduling a tick and it's arrival (in ms).
+ final double maxLatency;
+
+ /// The 50th percentile (median) (in ms).
+ final double percentile50th;
+
+ /// The 90th percentile (in ms).
+ final double percentile90th;
+
+ /// The 95th percentile (in ms).
+ final double percentile95th;
+
+ /// The 99th percentile (in ms).
+ final double percentile99th;
+
+ /// The maximum RSS of the process.
+ final int maxRss;
+
+ EventLoopLatencyStats(
+ this.minLatency,
+ this.avgLatency,
+ this.maxLatency,
+ this.percentile50th,
+ this.percentile90th,
+ this.percentile95th,
+ this.percentile99th,
+ this.maxRss);
+
+ void report(String name) {
+ print('$name.Min(RunTimeRaw): $minLatency ms.');
+ print('$name.Avg(RunTimeRaw): $avgLatency ms.');
+ print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
+ print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
+ print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
+ print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
+ print('$name.Max(RunTimeRaw): $maxLatency ms.');
+ print('$name.MaxRss(MemoryUse): $maxRss');
+ }
+}
+
+/// Accumulates tick latencies and makes statistics for it.
+class _TickLatencies {
+ final Uint64List _timestamps;
+ int _index = 0;
+
+ _TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
+
+ /// Returns `true` while the buffer has not been filled yet.
+ bool add(int latencyInUs) {
+ _timestamps[_index++] = latencyInUs;
+ return _index < _timestamps.length;
+ }
+
+ EventLoopLatencyStats makeStats() {
+ if (_index != _timestamps.length) {
+ throw 'Buffer has not been fully filled yet.';
+ }
+
+ _timestamps.sort();
+ final length = _timestamps.length;
+ final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
+ final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
+ final int max = _timestamps.fold(0, math.max);
+ final percentile50th = _timestamps[50 * length ~/ 100];
+ final percentile90th = _timestamps[90 * length ~/ 100];
+ final percentile95th = _timestamps[95 * length ~/ 100];
+ final percentile99th = _timestamps[99 * length ~/ 100];
+
+ return EventLoopLatencyStats(
+ min / 1000,
+ avg / 1000,
+ max / 1000,
+ percentile50th / 1000,
+ percentile90th / 1000,
+ percentile95th / 1000,
+ percentile99th / 1000,
+ ProcessInfo.maxRss);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index a9bc050..d203ef9 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -6,6 +6,7 @@
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:analysis_server/protocol/protocol_generated.dart'
hide AnalysisGetNavigationParams;
+import 'package:analysis_server/src/domains/analysis/macro_files.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
@@ -15,6 +16,7 @@
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
+import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
import 'package:collection/collection.dart';
@@ -54,8 +56,10 @@
final result = await server.getResolvedUnit(path);
final unit = result?.unit;
if (result?.state == ResultState.VALID && unit != null) {
- computeDartNavigation(
- server.resourceProvider, collector, unit, offset, 0);
+ computeDartNavigation(server.resourceProvider, collector, unit, offset, 0,
+ analyzerConverter: AnalyzerConverter(
+ locationProvider: MacroElementLocationProvider(
+ MacroFiles(server.resourceProvider))));
collector.createRegions();
}
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index f15c592..083158c 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -188,6 +188,71 @@
);
}
+ Future<void> test_macro_simpleIdentifier_getter() async {
+ final macroAnnotationsContents = '''
+library analyzer.macro.annotations;
+const observable = 0;
+''';
+
+ final mainContents = '''
+import 'macro_annotations.dart';
+
+class A {
+ @observable
+ int _foo = 0;
+}
+
+void f(A a) {
+ a.[[foo^]];
+}
+''';
+
+ final combinedContents = r'''
+import 'macro_annotations.dart';
+
+class A {
+ @observable
+ int _foo = 0;
+
+ int get [[foo]] => _foo;
+
+ set foo(int val) {
+ print('Setting foo to ${val}');
+ _foo = val;
+ }
+}
+
+void f(A a) {
+ a.[[foo^]];
+}
+''';
+
+ final macroAnnotationsFileUri =
+ Uri.file(join(projectFolderPath, 'lib', 'macro_annotations.dart'));
+ final pubspecFileUri = Uri.file(join(projectFolderPath, 'pubspec.yaml'));
+ final combinedFileUri = Uri.file(join(projectFolderPath, '.dart_tool',
+ 'analyzer', 'macro', 'lib', 'main.dart'));
+
+ await initialize(
+ textDocumentCapabilities:
+ withLocationLinkSupport(emptyTextDocumentClientCapabilities));
+ await openFile(pubspecFileUri, '');
+ await openFile(
+ macroAnnotationsFileUri, withoutMarkers(macroAnnotationsContents));
+ await openFile(mainFileUri, withoutMarkers(mainContents));
+ final res = await getDefinitionAsLocationLinks(
+ mainFileUri, positionFromMarker(mainContents));
+
+ expect(res, hasLength(1));
+ final loc = res.single;
+ expect(loc.originSelectionRange, equals(rangeFromMarkers(mainContents)));
+ expect(loc.targetUri, equals(combinedFileUri.toString()));
+
+ final getFooRange = rangesFromMarkers(combinedContents)[0];
+ expect(loc.targetRange, equals(getFooRange));
+ expect(loc.targetSelectionRange, equals(getFooRange));
+ }
+
Future<void> test_nonDartFile() async {
newFile(pubspecFilePath, content: simplePubspecContent);
await initialize();
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 0800570..cb38e6a 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
@@ -13,7 +13,9 @@
as generateDirectParserAstHelper;
import "parser_test_listener_creator.dart" as generateParserTestListener;
import "parser_test_parser_creator.dart" as generateParserTestParser;
+import '../tool/ast_model.dart';
import '../tool/generate_ast_equivalence.dart' as generateAstEquivalence;
+import '../tool/generate_ast_coverage.dart' as generateAstCoverage;
import 'utils/io_utils.dart' show computeRepoDirUri;
final Uri repoDir = computeRepoDirUri();
@@ -24,7 +26,9 @@
directParserAstHelper();
parserTestListener();
parserTestParser();
- await astEquivalence();
+ AstModel astModel = await deriveAstModel(repoDir);
+ await astEquivalence(astModel);
+ await astCoverage(astModel);
}
void parserTestParser() {
@@ -50,14 +54,22 @@
"dart pkg/front_end/tool/_fasta/direct_parser_ast_helper_creator.dart");
}
-Future<void> astEquivalence() async {
+Future<void> astEquivalence(AstModel astModel) async {
Uri generatedFile = generateAstEquivalence.computeEquivalenceUri(repoDir);
String generated =
- await generateAstEquivalence.generateAstEquivalence(repoDir);
+ await generateAstEquivalence.generateAstEquivalence(repoDir, astModel);
check(generated, generatedFile,
"dart pkg/front_end/tool/generate_ast_equivalence.dart");
}
+Future<void> astCoverage(AstModel astModel) async {
+ Uri generatedFile = generateAstCoverage.computeCoverageUri(repoDir);
+ String generated =
+ await generateAstCoverage.generateAstCoverage(repoDir, astModel);
+ check(generated, generatedFile,
+ "dart pkg/front_end/tool/generate_ast_coverage.dart");
+}
+
void experimentalFlags() {
{
Uri generatedFile =
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 201c0e11..1e568ba 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -262,6 +262,7 @@
coverage
cr
creator
+creators
criterion
cross
cruft
@@ -457,6 +458,7 @@
firsts
fishy
fishythefish
+fits
fixnum
fleshed
float32
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 14694ef..6ec4b57 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -548,6 +548,7 @@
insufficient
intdiv
interactive
+interchangeable
interested
internet
interpolate
diff --git a/pkg/front_end/tool/ast_model.dart b/pkg/front_end/tool/ast_model.dart
index 14f981f..74d8cf2 100644
--- a/pkg/front_end/tool/ast_model.dart
+++ b/pkg/front_end/tool/ast_model.dart
@@ -55,6 +55,20 @@
'PrimitiveConstant',
};
+/// Names of inner [Node] classes that are used as interfaces for (generally)
+/// interchangeable classes.
+///
+/// For instance, when [Expression] is used as the field type, any subtype of
+/// [Expression] can be used to populate the field.
+const Set<String> _interchangeableClasses = const {
+ 'Member',
+ 'Statement',
+ 'Expression',
+ 'Constant',
+ 'DartType',
+ 'Initializer',
+};
+
/// Names of subclasses of [NamedNode] that do _not_ have `visitXReference` or
/// `defaultXReference` methods.
const Set<String> _classesWithoutVisitReference = const {
@@ -96,8 +110,7 @@
'_proceduresView': null,
'_proceduresInternal': FieldRule(name: 'procedures'),
'_redirectingFactoriesView': null,
- '_redirectingFactoriesInternal':
- FieldRule(name: 'redirectingFactories'),
+ '_redirectingFactoriesInternal': FieldRule(name: 'redirectingFactories'),
'lazyBuilder': null,
'dirty': null,
},
@@ -264,6 +277,7 @@
final Class node;
AstClassKind _kind;
final String declarativeName;
+ final bool isInterchangeable;
AstClass superclass;
List<AstClass> interfaces = [];
@@ -273,8 +287,12 @@
List<AstField> fields = [];
AstClass(this.node,
- {this.superclass, AstClassKind kind, this.declarativeName})
- : _kind = kind {
+ {this.superclass,
+ AstClassKind kind,
+ this.declarativeName,
+ this.isInterchangeable})
+ : _kind = kind,
+ assert(isInterchangeable != null) {
if (superclass != null) {
superclass.subclasses.add(this);
}
@@ -520,10 +538,13 @@
_classesWithoutVisitReference.toSet();
Map<String, Map<String, FieldRule>> fieldRuleMap = {..._fieldRuleMap};
Map<String, FieldRule> nullFieldRules = {...?fieldRuleMap.remove(null)};
+ Set<String> interchangeableClasses = _interchangeableClasses.toSet();
for (Class cls in astLibrary.classes) {
declarativeClassesNames.remove(cls.name);
classesWithoutVisitMethods.remove(cls.name);
classesWithoutVisitReference.remove(cls.name);
+ interchangeableClasses.remove(cls.name);
+
Map<String, FieldRule> fieldRules = {...?fieldRuleMap.remove(cls.name)};
Set<String> renames = {};
Class parent = cls;
@@ -575,6 +596,10 @@
reportError('Unknown classes without visit reference methods: '
'${classesWithoutVisitReference}');
}
+ if (interchangeableClasses.isNotEmpty) {
+ reportError('Unknown interchangeable classes: '
+ '${interchangeableClasses}');
+ }
if (fieldRuleMap.isNotEmpty) {
reportError('Unknown classes with field rules: ${fieldRuleMap.keys}');
}
@@ -615,8 +640,10 @@
AstClass astClass = classMap[node];
if (astClass == null) {
+ bool isInterchangeable = _interchangeableClasses.contains(node.name);
if (node == classNode) {
- astClass = new AstClass(node, kind: AstClassKind.root);
+ astClass = new AstClass(node,
+ kind: AstClassKind.root, isInterchangeable: isInterchangeable);
} else if (classHierarchy.isSubtypeOf(node, classNode)) {
AstClass superclass = computeAstClass(node.superclass);
AstClassKind kind;
@@ -631,7 +658,8 @@
astClass = new AstClass(node,
superclass: superclass,
kind: kind,
- declarativeName: declarativeName);
+ declarativeName: declarativeName,
+ isInterchangeable: isInterchangeable);
for (Supertype supertype in node.implementedTypes) {
AstClass astSupertype = computeAstClass(supertype.classNode);
if (astSupertype != null) {
@@ -640,11 +668,15 @@
}
}
} else if (node.isEnum || _utilityClassesAsValues.contains(node.name)) {
- astClass = new AstClass(node, kind: AstClassKind.utilityAsValue);
+ astClass = new AstClass(node,
+ kind: AstClassKind.utilityAsValue,
+ isInterchangeable: isInterchangeable);
} else {
AstClass superclass = computeAstClass(node.superclass);
astClass = new AstClass(node,
- superclass: superclass, kind: AstClassKind.utilityAsStructure);
+ superclass: superclass,
+ kind: AstClassKind.utilityAsStructure,
+ isInterchangeable: isInterchangeable);
}
if (astClass != null) {
classMap[node] = astClass;
diff --git a/pkg/front_end/tool/generate_ast_coverage.dart b/pkg/front_end/tool/generate_ast_coverage.dart
new file mode 100644
index 0000000..423f2d6
--- /dev/null
+++ b/pkg/front_end/tool/generate_ast_coverage.dart
@@ -0,0 +1,121 @@
+// 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.
+
+// @dart = 2.9
+
+import 'dart:io';
+
+import 'ast_model.dart';
+import 'visitor_generator.dart';
+
+Uri computeCoverageUri(Uri repoDir) {
+ return repoDir.resolve('pkg/kernel/lib/src/coverage.dart');
+}
+
+main(List<String> args) async {
+ Uri output = args.isEmpty
+ ? computeCoverageUri(Uri.base)
+ : new File(args[0]).absolute.uri;
+ String result = await generateAstCoverage(Uri.base);
+ new File.fromUri(output).writeAsStringSync(result);
+}
+
+Future<String> generateAstCoverage(Uri repoDir, [AstModel astModel]) async {
+ astModel ??= await deriveAstModel(repoDir);
+ return generateVisitor(astModel, new CoverageVisitorStrategy());
+}
+
+class CoverageVisitorStrategy extends Visitor0Strategy {
+ Map<String, Set<String>> nestedClassNames = {};
+
+ @override
+ String get returnType => 'void';
+
+ @override
+ String get visitorName => 'CoverageVisitor';
+
+ @override
+ String get visitorComment => '''
+/// Recursive visitor that collects kinds for all visited nodes.
+///
+/// This can be used to verify that tests have the intended coverage.''';
+
+ @override
+ void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
+ AstClass superAstClass = astClass.superclass;
+ while (superAstClass != null && !superAstClass.isInterchangeable) {
+ superAstClass = superAstClass.superclass;
+ }
+ String innerName = superAstClass?.name ?? 'Node';
+ (nestedClassNames[innerName] ??= {}).add(astClass.name);
+ sb.writeln('''
+ visited.add(${innerName}Kind.${astClass.name});
+ node.visitChildren(this);''');
+ }
+
+ @override
+ void handleVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
+ 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;
+ (nestedClassNames[innerName] ??= {}).add(astClass.name);
+ sb.writeln('''
+ visited.add(${innerName}Kind.${astClass.name});
+ node.visitChildren(this);''');
+ }
+ }
+
+ @override
+ void generateHeader(AstModel astModel, StringBuffer sb) {
+ super.generateHeader(astModel, sb);
+ sb.writeln('''
+ Set<Object> visited = {};''');
+ }
+
+ @override
+ void generateFooter(AstModel astModel, StringBuffer sb) {
+ super.generateFooter(astModel, sb);
+ nestedClassNames.forEach((String innerName, Set<String> classNames) {
+ sb.writeln('''
+
+enum ${innerName}Kind {''');
+ for (String className in classNames.toList()..sort()) {
+ sb.writeln('''
+ $className,''');
+ }
+ sb.writeln('''
+}''');
+ });
+ sb.writeln('''
+
+/// Returns the set of node kinds that were not visited by [visitor].
+Set<Object> missingNodes($visitorName visitor) {
+ Set<Object> all = {''');
+ nestedClassNames.forEach((String innerName, Set<String> classNames) {
+ sb.writeln('''
+ ...${innerName}Kind.values,''');
+ });
+ sb.writeln('''
+ };
+ all.removeAll(visitor.visited);
+ return all;
+}''');
+ nestedClassNames.forEach((String innerName, Set<String> classNames) {
+ if (innerName == 'Node') return;
+ sb.writeln('''
+/// Returns the set of [${innerName}Kind]s that were not visited by [visitor].
+Set<${innerName}Kind> missing${innerName}s($visitorName visitor) {
+ Set<${innerName}Kind> all =
+ new Set<${innerName}Kind>.from(${innerName}Kind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}''');
+ });
+ }
+}
diff --git a/pkg/front_end/tool/generate_ast_equivalence.dart b/pkg/front_end/tool/generate_ast_equivalence.dart
index 87349d6..4d2aac6 100644
--- a/pkg/front_end/tool/generate_ast_equivalence.dart
+++ b/pkg/front_end/tool/generate_ast_equivalence.dart
@@ -21,8 +21,8 @@
new File.fromUri(output).writeAsStringSync(result);
}
-Future<String> generateAstEquivalence(Uri repoDir) async {
- AstModel astModel = await deriveAstModel(repoDir);
+Future<String> generateAstEquivalence(Uri repoDir, [AstModel astModel]) async {
+ astModel ??= await deriveAstModel(repoDir);
return generateVisitor(astModel, new EquivalenceVisitorStrategy());
}
@@ -110,7 +110,8 @@
'check${astClass.name}_${field.name}';
@override
- void handleDefaultVisit(AstClass astClass, StringBuffer sb) {
+ void handleDefaultVisit(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
return false;''');
}
@@ -387,7 +388,7 @@
}
@override
- void handleVisit(AstClass astClass, StringBuffer sb) {
+ void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
registerAstClassEquivalence(astClass);
sb.writeln('''
return strategy.${classCheckName(astClass)}(
@@ -395,13 +396,15 @@
}
@override
- void handleDefaultVisitReference(AstClass astClass, StringBuffer sb) {
+ void handleDefaultVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
return false;''');
}
@override
- void handleVisitReference(AstClass astClass, StringBuffer sb) {
+ void handleVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
return false;''');
}
@@ -504,12 +507,14 @@
return result;
}
- /// Returns `true` if [a] and [b] are equivalent, as defined by their
- /// corresponding canonical names. Inequivalence is _not_ registered.
+ /// Returns `true` if [a] and [b] are equivalent, either by existing
+ /// assumption or as defined by their corresponding canonical names.
+ /// Inequivalence is _not_ registered.
bool $matchNamedNodes(NamedNode? a, NamedNode? b) {
return identical(a, b) ||
a == null ||
b == null ||
+ checkAssumedReferences(a.reference, b.reference) ||
new ReferenceName.fromNamedNode(a) ==
new ReferenceName.fromNamedNode(b);
}
@@ -527,10 +532,12 @@
return $checkingState.$assumeReferences(a, b);
}
- /// Returns `true` if [a] and [b] are equivalent, as defined by their
- /// corresponding canonical names. Inequivalence is _not_ registered.
+ /// Returns `true` if [a] and [b] are equivalent, either by existing
+ /// assumption or as defined by their corresponding canonical names.
+ /// Inequivalence is _not_ registered.
bool $matchReferences(Reference? a, Reference? b) {
return identical(a, b) ||
+ checkAssumedReferences(a, b) ||
ReferenceName.fromReference(a) ==
ReferenceName.fromReference(b);
}
diff --git a/pkg/front_end/tool/visitor_generator.dart b/pkg/front_end/tool/visitor_generator.dart
index ff3bace..a965151 100644
--- a/pkg/front_end/tool/visitor_generator.dart
+++ b/pkg/front_end/tool/visitor_generator.dart
@@ -9,7 +9,12 @@
import 'ast_model.dart';
/// Generates a visitor library into [sb] based on [astModel] and [strategy].
-String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
+///
+/// If [format] is `false`, the generated output will _not_ be formatted using
+/// the Dart formatter. Use this during development to support incomplete
+/// generation.
+String generateVisitor(AstModel astModel, VisitorStrategy strategy,
+ {bool format: true}) {
StringBuffer sb = new StringBuffer();
strategy.generateHeader(astModel, sb);
@@ -18,7 +23,7 @@
case AstClassKind.root:
case AstClassKind.inner:
if (astClass.hasVisitMethod) {
- strategy.generateDefaultVisit(astClass, sb);
+ strategy.generateDefaultVisit(astModel, astClass, sb);
}
for (AstClass subclass in astClass.subclasses) {
addVisitNode(subclass);
@@ -28,7 +33,7 @@
case AstClassKind.named:
case AstClassKind.declarative:
if (astClass.hasVisitMethod) {
- strategy.generateVisit(astClass, sb);
+ strategy.generateVisit(astModel, astClass, sb);
}
break;
case AstClassKind.implementation:
@@ -44,7 +49,7 @@
case AstClassKind.root:
case AstClassKind.inner:
if (astClass.hasVisitReferenceMethod) {
- strategy.generateDefaultVisitReference(astClass, sb);
+ strategy.generateDefaultVisitReference(astModel, astClass, sb);
}
for (AstClass subclass in astClass.subclasses) {
addVisitReference(subclass);
@@ -54,7 +59,7 @@
case AstClassKind.named:
case AstClassKind.declarative:
if (astClass.hasVisitReferenceMethod) {
- strategy.generateVisitReference(astClass, sb);
+ strategy.generateVisitReference(astModel, astClass, sb);
}
break;
case AstClassKind.implementation:
@@ -71,7 +76,9 @@
strategy.generateFooter(astModel, sb);
String result = sb.toString();
- result = new DartFormatter().format(result);
+ if (format) {
+ result = new DartFormatter().format(result);
+ }
return result;
}
@@ -79,21 +86,27 @@
abstract class VisitorStrategy {
const VisitorStrategy();
+ /// Comment used as doc comment for the generated visitor class.
+ String get visitorComment => '';
+
/// Generates the header of the visitor library, including preamble, imports
/// and visitor class declaration start.
void generateHeader(AstModel astModel, StringBuffer sb);
/// Generates a `defaultX` visitor method for [astClass].
- void generateDefaultVisit(AstClass astClass, StringBuffer sb);
+ void generateDefaultVisit(
+ AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates a `visitX` visitor method for [astClass].
- void generateVisit(AstClass astClass, StringBuffer sb);
+ void generateVisit(AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates a `defaultXReference` visitor method for [astClass].
- void generateDefaultVisitReference(AstClass astClass, StringBuffer sb);
+ void generateDefaultVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates a `visitXReference` visitor method for [astClass].
- void generateVisitReference(AstClass astClass, StringBuffer sb);
+ void generateVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates the footer of the visitor library, including the visitor class
/// declaration end.
@@ -119,6 +132,7 @@
sb.writeln('''
import 'package:kernel/ast.dart';
+$visitorComment
class $visitorName$visitorTypeParameters implements Visitor<$returnType> {''');
}
@@ -128,56 +142,62 @@
}
@override
- void generateDefaultVisit(AstClass astClass, StringBuffer sb) {
+ void generateDefaultVisit(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}(
${astClass.name} node) {''');
- handleDefaultVisit(astClass, sb);
+ handleDefaultVisit(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `defaultX` visitor method of [astClass].
- void handleDefaultVisit(AstClass astClass, StringBuffer sb) {}
+ void handleDefaultVisit(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
- void generateVisit(AstClass astClass, StringBuffer sb) {
+ void generateVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}(
${astClass.name} node) {''');
- handleVisit(astClass, sb);
+ handleVisit(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `visitX` visitor method of [astClass].
- void handleVisit(AstClass astClass, StringBuffer sb) {}
+ void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
- void generateDefaultVisitReference(AstClass astClass, StringBuffer sb) {
- sb.writeln(''''
+ void generateDefaultVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
+ sb.writeln('''
@override
${returnType} default${astClass.name}Reference(
- '${astClass.name} node) {''');
- handleDefaultVisitReference(astClass, sb);
+ ${astClass.name} node) {''');
+ handleDefaultVisitReference(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `defaultXReference` visitor method of [astClass].
- void handleDefaultVisitReference(AstClass astClass, StringBuffer sb) {}
+ void handleDefaultVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
- void generateVisitReference(AstClass astClass, StringBuffer sb) {
+ void generateVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}Reference(
${astClass.name} node) {''');
- handleVisitReference(astClass, sb);
+ handleVisitReference(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `visitXReference` visitor method of [astClass].
- void handleVisitReference(AstClass astClass, StringBuffer sb) {}
+ void handleVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {}
}
/// Strategy for creating an empty `Visitor<void>` implementation.
@@ -220,6 +240,7 @@
sb.writeln('''
import 'package:kernel/ast.dart';
+$visitorComment
class $visitorName$visitorTypeParameters
implements Visitor1<$returnType, $argumentType> {''');
}
@@ -230,60 +251,66 @@
}
@override
- void generateDefaultVisit(AstClass astClass, StringBuffer sb) {
+ void generateDefaultVisit(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}(
${astClass.name} node, $argumentType $argumentName) {''');
- handleDefaultVisit(astClass, sb);
+ handleDefaultVisit(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `defaultX` visitor method of [astClass].
- void handleDefaultVisit(AstClass astClass, StringBuffer sb) {}
+ void handleDefaultVisit(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
- void generateVisit(AstClass astClass, StringBuffer sb) {
+ void generateVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}(
${astClass.name} node, $argumentType $argumentName) {''');
- handleVisit(astClass, sb);
+ handleVisit(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `visitX` visitor method of [astClass].
- void handleVisit(AstClass astClass, StringBuffer sb) {}
+ void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
- void generateDefaultVisitReference(AstClass astClass, StringBuffer sb) {
+ void generateDefaultVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}Reference(
${astClass.name} node, $argumentType $argumentName) {''');
- handleDefaultVisitReference(astClass, sb);
+ handleDefaultVisitReference(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `defaultXReference` visitor method of [astClass].
- void handleDefaultVisitReference(AstClass astClass, StringBuffer sb) {}
+ void handleDefaultVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
- void generateVisitReference(AstClass astClass, StringBuffer sb) {
+ void generateVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}Reference(
${astClass.name} node, $argumentType $argumentName) {''');
- handleVisitReference(astClass, sb);
+ handleVisitReference(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `visitXReference` visitor method of [astClass].
- void handleVisitReference(AstClass astClass, StringBuffer sb) {}
+ void handleVisitReference(
+ AstModel astModel, AstClass astClass, StringBuffer sb) {}
}
/// Strategy for creating an empty `Visitor1<void,Null>` implementation.
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 63cee4b..a736433 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -5328,6 +5328,7 @@
interfaceTarget.acceptReference(v);
name.accept(v);
arguments.accept(v);
+ functionType?.accept(v);
}
@override
@@ -5342,6 +5343,9 @@
arguments = v.transform(arguments);
arguments.parent = this;
}
+ if (functionType != null) {
+ functionType = v.visitDartType(functionType!) as FunctionType;
+ }
}
@override
@@ -5356,6 +5360,10 @@
arguments = v.transform(arguments);
arguments.parent = this;
}
+ if (functionType != null) {
+ functionType =
+ v.visitDartType(functionType!, cannotRemoveSentinel) as FunctionType;
+ }
}
@override
diff --git a/pkg/kernel/lib/src/coverage.dart b/pkg/kernel/lib/src/coverage.dart
new file mode 100644
index 0000000..cfcd11d
--- /dev/null
+++ b/pkg/kernel/lib/src/coverage.dart
@@ -0,0 +1,1179 @@
+import 'package:kernel/ast.dart';
+
+/// Recursive visitor that collects kinds for all visited nodes.
+///
+/// This can be used to verify that tests have the intended coverage.
+class CoverageVisitor implements Visitor<void> {
+ Set<Object> visited = {};
+ @override
+ void defaultNode(Node node) {}
+ @override
+ void defaultTreeNode(TreeNode node) {}
+ @override
+ void visitLibrary(Library node) {
+ visited.add(NodeKind.Library);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypedef(Typedef node) {
+ visited.add(NodeKind.Typedef);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitClass(Class node) {
+ visited.add(NodeKind.Class);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitExtension(Extension node) {
+ visited.add(NodeKind.Extension);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultMember(Member node) {}
+ @override
+ void visitField(Field node) {
+ visited.add(MemberKind.Field);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConstructor(Constructor node) {
+ visited.add(MemberKind.Constructor);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRedirectingFactory(RedirectingFactory node) {
+ visited.add(MemberKind.RedirectingFactory);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitProcedure(Procedure node) {
+ visited.add(MemberKind.Procedure);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLibraryDependency(LibraryDependency node) {
+ visited.add(NodeKind.LibraryDependency);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLibraryPart(LibraryPart node) {
+ visited.add(NodeKind.LibraryPart);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitCombinator(Combinator node) {
+ visited.add(NodeKind.Combinator);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultInitializer(Initializer node) {}
+ @override
+ void visitInvalidInitializer(InvalidInitializer node) {
+ visited.add(InitializerKind.InvalidInitializer);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFieldInitializer(FieldInitializer node) {
+ visited.add(InitializerKind.FieldInitializer);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSuperInitializer(SuperInitializer node) {
+ visited.add(InitializerKind.SuperInitializer);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRedirectingInitializer(RedirectingInitializer node) {
+ visited.add(InitializerKind.RedirectingInitializer);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLocalInitializer(LocalInitializer node) {
+ visited.add(InitializerKind.LocalInitializer);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitAssertInitializer(AssertInitializer node) {
+ visited.add(InitializerKind.AssertInitializer);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionNode(FunctionNode node) {
+ visited.add(NodeKind.FunctionNode);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultExpression(Expression node) {}
+ @override
+ void visitInvalidExpression(InvalidExpression node) {
+ visited.add(ExpressionKind.InvalidExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitVariableGet(VariableGet node) {
+ visited.add(ExpressionKind.VariableGet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitVariableSet(VariableSet node) {
+ visited.add(ExpressionKind.VariableSet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDynamicGet(DynamicGet node) {
+ visited.add(ExpressionKind.DynamicGet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceGet(InstanceGet node) {
+ visited.add(ExpressionKind.InstanceGet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionTearOff(FunctionTearOff node) {
+ visited.add(ExpressionKind.FunctionTearOff);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceTearOff(InstanceTearOff node) {
+ visited.add(ExpressionKind.InstanceTearOff);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDynamicSet(DynamicSet node) {
+ visited.add(ExpressionKind.DynamicSet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceSet(InstanceSet node) {
+ visited.add(ExpressionKind.InstanceSet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSuperPropertyGet(SuperPropertyGet node) {
+ visited.add(ExpressionKind.SuperPropertyGet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSuperPropertySet(SuperPropertySet node) {
+ visited.add(ExpressionKind.SuperPropertySet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStaticGet(StaticGet node) {
+ visited.add(ExpressionKind.StaticGet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStaticTearOff(StaticTearOff node) {
+ visited.add(ExpressionKind.StaticTearOff);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStaticSet(StaticSet node) {
+ visited.add(ExpressionKind.StaticSet);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDynamicInvocation(DynamicInvocation node) {
+ visited.add(ExpressionKind.DynamicInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceInvocation(InstanceInvocation node) {
+ visited.add(ExpressionKind.InstanceInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceGetterInvocation(InstanceGetterInvocation node) {
+ visited.add(ExpressionKind.InstanceGetterInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionInvocation(FunctionInvocation node) {
+ visited.add(ExpressionKind.FunctionInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+ visited.add(ExpressionKind.LocalFunctionInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSuperMethodInvocation(SuperMethodInvocation node) {
+ visited.add(ExpressionKind.SuperMethodInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStaticInvocation(StaticInvocation node) {
+ visited.add(ExpressionKind.StaticInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConstructorInvocation(ConstructorInvocation node) {
+ visited.add(ExpressionKind.ConstructorInvocation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitEqualsNull(EqualsNull node) {
+ visited.add(ExpressionKind.EqualsNull);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitEqualsCall(EqualsCall node) {
+ visited.add(ExpressionKind.EqualsCall);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstantiation(Instantiation node) {
+ visited.add(ExpressionKind.Instantiation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNot(Not node) {
+ visited.add(ExpressionKind.Not);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLogicalExpression(LogicalExpression node) {
+ visited.add(ExpressionKind.LogicalExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConditionalExpression(ConditionalExpression node) {
+ visited.add(ExpressionKind.ConditionalExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStringConcatenation(StringConcatenation node) {
+ visited.add(ExpressionKind.StringConcatenation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitListConcatenation(ListConcatenation node) {
+ visited.add(ExpressionKind.ListConcatenation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSetConcatenation(SetConcatenation node) {
+ visited.add(ExpressionKind.SetConcatenation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitMapConcatenation(MapConcatenation node) {
+ visited.add(ExpressionKind.MapConcatenation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceCreation(InstanceCreation node) {
+ visited.add(ExpressionKind.InstanceCreation);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFileUriExpression(FileUriExpression node) {
+ visited.add(ExpressionKind.FileUriExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIsExpression(IsExpression node) {
+ visited.add(ExpressionKind.IsExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitAsExpression(AsExpression node) {
+ visited.add(ExpressionKind.AsExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNullCheck(NullCheck node) {
+ visited.add(ExpressionKind.NullCheck);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultBasicLiteral(BasicLiteral node) {}
+ @override
+ void visitStringLiteral(StringLiteral node) {
+ visited.add(ExpressionKind.StringLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIntLiteral(IntLiteral node) {
+ visited.add(ExpressionKind.IntLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDoubleLiteral(DoubleLiteral node) {
+ visited.add(ExpressionKind.DoubleLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitBoolLiteral(BoolLiteral node) {
+ visited.add(ExpressionKind.BoolLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNullLiteral(NullLiteral node) {
+ visited.add(ExpressionKind.NullLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSymbolLiteral(SymbolLiteral node) {
+ visited.add(ExpressionKind.SymbolLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypeLiteral(TypeLiteral node) {
+ visited.add(ExpressionKind.TypeLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitThisExpression(ThisExpression node) {
+ visited.add(ExpressionKind.ThisExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRethrow(Rethrow node) {
+ visited.add(ExpressionKind.Rethrow);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitThrow(Throw node) {
+ visited.add(ExpressionKind.Throw);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitListLiteral(ListLiteral node) {
+ visited.add(ExpressionKind.ListLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSetLiteral(SetLiteral node) {
+ visited.add(ExpressionKind.SetLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitMapLiteral(MapLiteral node) {
+ visited.add(ExpressionKind.MapLiteral);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitAwaitExpression(AwaitExpression node) {
+ visited.add(ExpressionKind.AwaitExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionExpression(FunctionExpression node) {
+ visited.add(ExpressionKind.FunctionExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConstantExpression(ConstantExpression node) {
+ visited.add(ExpressionKind.ConstantExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLet(Let node) {
+ visited.add(ExpressionKind.Let);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitBlockExpression(BlockExpression node) {
+ visited.add(ExpressionKind.BlockExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLoadLibrary(LoadLibrary node) {
+ visited.add(ExpressionKind.LoadLibrary);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
+ visited.add(ExpressionKind.CheckLibraryIsLoaded);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConstructorTearOff(ConstructorTearOff node) {
+ visited.add(ExpressionKind.ConstructorTearOff);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
+ visited.add(ExpressionKind.RedirectingFactoryTearOff);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypedefTearOff(TypedefTearOff node) {
+ visited.add(ExpressionKind.TypedefTearOff);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitArguments(Arguments node) {
+ visited.add(NodeKind.Arguments);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNamedExpression(NamedExpression node) {
+ visited.add(NodeKind.NamedExpression);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitMapLiteralEntry(MapLiteralEntry node) {
+ visited.add(NodeKind.MapLiteralEntry);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultStatement(Statement node) {}
+ @override
+ void visitExpressionStatement(ExpressionStatement node) {
+ visited.add(StatementKind.ExpressionStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitBlock(Block node) {
+ visited.add(StatementKind.Block);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitAssertBlock(AssertBlock node) {
+ visited.add(StatementKind.AssertBlock);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitEmptyStatement(EmptyStatement node) {
+ visited.add(StatementKind.EmptyStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitAssertStatement(AssertStatement node) {
+ visited.add(StatementKind.AssertStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitLabeledStatement(LabeledStatement node) {
+ visited.add(StatementKind.LabeledStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitBreakStatement(BreakStatement node) {
+ visited.add(StatementKind.BreakStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitWhileStatement(WhileStatement node) {
+ visited.add(StatementKind.WhileStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDoStatement(DoStatement node) {
+ visited.add(StatementKind.DoStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitForStatement(ForStatement node) {
+ visited.add(StatementKind.ForStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitForInStatement(ForInStatement node) {
+ visited.add(StatementKind.ForInStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSwitchStatement(SwitchStatement node) {
+ visited.add(StatementKind.SwitchStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitContinueSwitchStatement(ContinueSwitchStatement node) {
+ visited.add(StatementKind.ContinueSwitchStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIfStatement(IfStatement node) {
+ visited.add(StatementKind.IfStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitReturnStatement(ReturnStatement node) {
+ visited.add(StatementKind.ReturnStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTryCatch(TryCatch node) {
+ visited.add(StatementKind.TryCatch);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTryFinally(TryFinally node) {
+ visited.add(StatementKind.TryFinally);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitYieldStatement(YieldStatement node) {
+ visited.add(StatementKind.YieldStatement);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitVariableDeclaration(VariableDeclaration node) {
+ visited.add(StatementKind.VariableDeclaration);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionDeclaration(FunctionDeclaration node) {
+ visited.add(StatementKind.FunctionDeclaration);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSwitchCase(SwitchCase node) {
+ visited.add(NodeKind.SwitchCase);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitCatch(Catch node) {
+ visited.add(NodeKind.Catch);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypeParameter(TypeParameter node) {
+ visited.add(NodeKind.TypeParameter);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitComponent(Component node) {
+ visited.add(NodeKind.Component);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitName(Name node) {
+ visited.add(NodeKind.Name);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultDartType(DartType node) {}
+ @override
+ void visitInvalidType(InvalidType node) {
+ visited.add(DartTypeKind.InvalidType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDynamicType(DynamicType node) {
+ visited.add(DartTypeKind.DynamicType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitVoidType(VoidType node) {
+ visited.add(DartTypeKind.VoidType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNeverType(NeverType node) {
+ visited.add(DartTypeKind.NeverType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNullType(NullType node) {
+ visited.add(DartTypeKind.NullType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInterfaceType(InterfaceType node) {
+ visited.add(DartTypeKind.InterfaceType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionType(FunctionType node) {
+ visited.add(DartTypeKind.FunctionType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypedefType(TypedefType node) {
+ visited.add(DartTypeKind.TypedefType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFutureOrType(FutureOrType node) {
+ visited.add(DartTypeKind.FutureOrType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitExtensionType(ExtensionType node) {
+ visited.add(DartTypeKind.ExtensionType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypeParameterType(TypeParameterType node) {
+ visited.add(DartTypeKind.TypeParameterType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitNamedType(NamedType node) {
+ visited.add(NodeKind.NamedType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSupertype(Supertype node) {
+ visited.add(NodeKind.Supertype);
+ node.visitChildren(this);
+ }
+
+ @override
+ void defaultConstant(Constant node) {}
+ @override
+ void visitNullConstant(NullConstant node) {
+ visited.add(ConstantKind.NullConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitBoolConstant(BoolConstant node) {
+ visited.add(ConstantKind.BoolConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIntConstant(IntConstant node) {
+ visited.add(ConstantKind.IntConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDoubleConstant(DoubleConstant node) {
+ visited.add(ConstantKind.DoubleConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStringConstant(StringConstant node) {
+ visited.add(ConstantKind.StringConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSymbolConstant(SymbolConstant node) {
+ visited.add(ConstantKind.SymbolConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitMapConstant(MapConstant node) {
+ visited.add(ConstantKind.MapConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitListConstant(ListConstant node) {
+ visited.add(ConstantKind.ListConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSetConstant(SetConstant node) {
+ visited.add(ConstantKind.SetConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceConstant(InstanceConstant node) {
+ visited.add(ConstantKind.InstanceConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstantiationConstant(InstantiationConstant node) {
+ visited.add(ConstantKind.InstantiationConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStaticTearOffConstant(StaticTearOffConstant node) {
+ visited.add(ConstantKind.StaticTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
+ visited.add(ConstantKind.ConstructorTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRedirectingFactoryTearOffConstant(
+ RedirectingFactoryTearOffConstant node) {
+ visited.add(ConstantKind.RedirectingFactoryTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
+ visited.add(ConstantKind.TypedefTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypeLiteralConstant(TypeLiteralConstant node) {
+ visited.add(ConstantKind.TypeLiteralConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitUnevaluatedConstant(UnevaluatedConstant node) {
+ visited.add(ConstantKind.UnevaluatedConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypedefReference(Typedef node) {}
+ @override
+ void visitClassReference(Class node) {}
+ @override
+ void visitExtensionReference(Extension node) {}
+ @override
+ void defaultMemberReference(Member node) {}
+ @override
+ void visitFieldReference(Field node) {}
+ @override
+ void visitConstructorReference(Constructor node) {}
+ @override
+ void visitRedirectingFactoryReference(RedirectingFactory node) {}
+ @override
+ void visitProcedureReference(Procedure node) {}
+ @override
+ void defaultConstantReference(Constant node) {}
+ @override
+ void visitNullConstantReference(NullConstant node) {
+ visited.add(ConstantKind.NullConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitBoolConstantReference(BoolConstant node) {
+ visited.add(ConstantKind.BoolConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIntConstantReference(IntConstant node) {
+ visited.add(ConstantKind.IntConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDoubleConstantReference(DoubleConstant node) {
+ visited.add(ConstantKind.DoubleConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStringConstantReference(StringConstant node) {
+ visited.add(ConstantKind.StringConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSymbolConstantReference(SymbolConstant node) {
+ visited.add(ConstantKind.SymbolConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitMapConstantReference(MapConstant node) {
+ visited.add(ConstantKind.MapConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitListConstantReference(ListConstant node) {
+ visited.add(ConstantKind.ListConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSetConstantReference(SetConstant node) {
+ visited.add(ConstantKind.SetConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceConstantReference(InstanceConstant node) {
+ visited.add(ConstantKind.InstanceConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstantiationConstantReference(InstantiationConstant node) {
+ visited.add(ConstantKind.InstantiationConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitStaticTearOffConstantReference(StaticTearOffConstant node) {
+ visited.add(ConstantKind.StaticTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConstructorTearOffConstantReference(
+ ConstructorTearOffConstant node) {
+ visited.add(ConstantKind.ConstructorTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRedirectingFactoryTearOffConstantReference(
+ RedirectingFactoryTearOffConstant node) {
+ visited.add(ConstantKind.RedirectingFactoryTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypedefTearOffConstantReference(TypedefTearOffConstant node) {
+ visited.add(ConstantKind.TypedefTearOffConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitTypeLiteralConstantReference(TypeLiteralConstant node) {
+ visited.add(ConstantKind.TypeLiteralConstant);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitUnevaluatedConstantReference(UnevaluatedConstant node) {
+ visited.add(ConstantKind.UnevaluatedConstant);
+ node.visitChildren(this);
+ }
+}
+
+enum NodeKind {
+ Arguments,
+ Catch,
+ Class,
+ Combinator,
+ Component,
+ Extension,
+ FunctionNode,
+ Library,
+ LibraryDependency,
+ LibraryPart,
+ MapLiteralEntry,
+ Name,
+ NamedExpression,
+ NamedType,
+ Supertype,
+ SwitchCase,
+ TypeParameter,
+ Typedef,
+}
+
+enum MemberKind {
+ Constructor,
+ Field,
+ Procedure,
+ RedirectingFactory,
+}
+
+enum InitializerKind {
+ AssertInitializer,
+ FieldInitializer,
+ InvalidInitializer,
+ LocalInitializer,
+ RedirectingInitializer,
+ SuperInitializer,
+}
+
+enum ExpressionKind {
+ AsExpression,
+ AwaitExpression,
+ BlockExpression,
+ BoolLiteral,
+ CheckLibraryIsLoaded,
+ ConditionalExpression,
+ ConstantExpression,
+ ConstructorInvocation,
+ ConstructorTearOff,
+ DoubleLiteral,
+ DynamicGet,
+ DynamicInvocation,
+ DynamicSet,
+ EqualsCall,
+ EqualsNull,
+ FileUriExpression,
+ FunctionExpression,
+ FunctionInvocation,
+ FunctionTearOff,
+ InstanceCreation,
+ InstanceGet,
+ InstanceGetterInvocation,
+ InstanceInvocation,
+ InstanceSet,
+ InstanceTearOff,
+ Instantiation,
+ IntLiteral,
+ InvalidExpression,
+ IsExpression,
+ Let,
+ ListConcatenation,
+ ListLiteral,
+ LoadLibrary,
+ LocalFunctionInvocation,
+ LogicalExpression,
+ MapConcatenation,
+ MapLiteral,
+ Not,
+ NullCheck,
+ NullLiteral,
+ RedirectingFactoryTearOff,
+ Rethrow,
+ SetConcatenation,
+ SetLiteral,
+ StaticGet,
+ StaticInvocation,
+ StaticSet,
+ StaticTearOff,
+ StringConcatenation,
+ StringLiteral,
+ SuperMethodInvocation,
+ SuperPropertyGet,
+ SuperPropertySet,
+ SymbolLiteral,
+ ThisExpression,
+ Throw,
+ TypeLiteral,
+ TypedefTearOff,
+ VariableGet,
+ VariableSet,
+}
+
+enum StatementKind {
+ AssertBlock,
+ AssertStatement,
+ Block,
+ BreakStatement,
+ ContinueSwitchStatement,
+ DoStatement,
+ EmptyStatement,
+ ExpressionStatement,
+ ForInStatement,
+ ForStatement,
+ FunctionDeclaration,
+ IfStatement,
+ LabeledStatement,
+ ReturnStatement,
+ SwitchStatement,
+ TryCatch,
+ TryFinally,
+ VariableDeclaration,
+ WhileStatement,
+ YieldStatement,
+}
+
+enum DartTypeKind {
+ DynamicType,
+ ExtensionType,
+ FunctionType,
+ FutureOrType,
+ InterfaceType,
+ InvalidType,
+ NeverType,
+ NullType,
+ TypeParameterType,
+ TypedefType,
+ VoidType,
+}
+
+enum ConstantKind {
+ BoolConstant,
+ ConstructorTearOffConstant,
+ DoubleConstant,
+ InstanceConstant,
+ InstantiationConstant,
+ IntConstant,
+ ListConstant,
+ MapConstant,
+ NullConstant,
+ RedirectingFactoryTearOffConstant,
+ SetConstant,
+ StaticTearOffConstant,
+ StringConstant,
+ SymbolConstant,
+ TypeLiteralConstant,
+ TypedefTearOffConstant,
+ UnevaluatedConstant,
+}
+
+/// Returns the set of node kinds that were not visited by [visitor].
+Set<Object> missingNodes(CoverageVisitor visitor) {
+ Set<Object> all = {
+ ...NodeKind.values,
+ ...MemberKind.values,
+ ...InitializerKind.values,
+ ...ExpressionKind.values,
+ ...StatementKind.values,
+ ...DartTypeKind.values,
+ ...ConstantKind.values,
+ };
+ all.removeAll(visitor.visited);
+ return all;
+}
+
+/// Returns the set of [MemberKind]s that were not visited by [visitor].
+Set<MemberKind> missingMembers(CoverageVisitor visitor) {
+ Set<MemberKind> all = new Set<MemberKind>.from(MemberKind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}
+
+/// Returns the set of [InitializerKind]s that were not visited by [visitor].
+Set<InitializerKind> missingInitializers(CoverageVisitor visitor) {
+ Set<InitializerKind> all =
+ new Set<InitializerKind>.from(InitializerKind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}
+
+/// Returns the set of [ExpressionKind]s that were not visited by [visitor].
+Set<ExpressionKind> missingExpressions(CoverageVisitor visitor) {
+ Set<ExpressionKind> all = new Set<ExpressionKind>.from(ExpressionKind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}
+
+/// Returns the set of [StatementKind]s that were not visited by [visitor].
+Set<StatementKind> missingStatements(CoverageVisitor visitor) {
+ Set<StatementKind> all = new Set<StatementKind>.from(StatementKind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}
+
+/// Returns the set of [DartTypeKind]s that were not visited by [visitor].
+Set<DartTypeKind> missingDartTypes(CoverageVisitor visitor) {
+ Set<DartTypeKind> all = new Set<DartTypeKind>.from(DartTypeKind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}
+
+/// Returns the set of [ConstantKind]s that were not visited by [visitor].
+Set<ConstantKind> missingConstants(CoverageVisitor visitor) {
+ Set<ConstantKind> all = new Set<ConstantKind>.from(ConstantKind.values);
+ all.removeAll(visitor.visited);
+ return all;
+}
diff --git a/pkg/kernel/lib/src/equivalence.dart b/pkg/kernel/lib/src/equivalence.dart
index 5a1e62c..9034fe6 100644
--- a/pkg/kernel/lib/src/equivalence.dart
+++ b/pkg/kernel/lib/src/equivalence.dart
@@ -950,12 +950,14 @@
return result;
}
- /// Returns `true` if [a] and [b] are equivalent, as defined by their
- /// corresponding canonical names. Inequivalence is _not_ registered.
+ /// Returns `true` if [a] and [b] are equivalent, either by existing
+ /// assumption or as defined by their corresponding canonical names.
+ /// Inequivalence is _not_ registered.
bool matchNamedNodes(NamedNode? a, NamedNode? b) {
return identical(a, b) ||
a == null ||
b == null ||
+ checkAssumedReferences(a.reference, b.reference) ||
new ReferenceName.fromNamedNode(a) ==
new ReferenceName.fromNamedNode(b);
}
@@ -973,10 +975,12 @@
return _checkingState.assumeReferences(a, b);
}
- /// Returns `true` if [a] and [b] are equivalent, as defined by their
- /// corresponding canonical names. Inequivalence is _not_ registered.
+ /// Returns `true` if [a] and [b] are equivalent, either by existing
+ /// assumption or as defined by their corresponding canonical names.
+ /// Inequivalence is _not_ registered.
bool matchReferences(Reference? a, Reference? b) {
return identical(a, b) ||
+ checkAssumedReferences(a, b) ||
ReferenceName.fromReference(a) == ReferenceName.fromReference(b);
}
diff --git a/pkg/kernel/lib/src/equivalence_helpers.dart b/pkg/kernel/lib/src/equivalence_helpers.dart
index a7ccb27..29f6e00 100644
--- a/pkg/kernel/lib/src/equivalence_helpers.dart
+++ b/pkg/kernel/lib/src/equivalence_helpers.dart
@@ -265,7 +265,7 @@
return new ReferenceName.internal(ReferenceNameKind.Member, node.name,
new ReferenceName.fromNamedNode(node.enclosingLibrary));
} else if (node is Member) {
- Class? enclosingClass = node.enclosingClass;
+ TreeNode? parent = node.parent;
Reference? libraryReference = node.name.libraryName;
String? uri;
if (libraryReference != null) {
@@ -276,18 +276,15 @@
uri = libraryReference.canonicalName?.name;
}
}
- if (enclosingClass != null) {
- return new ReferenceName.internal(
- ReferenceNameKind.Member,
- node.name.text,
- new ReferenceName.fromNamedNode(enclosingClass),
- uri);
+ if (parent is Class) {
+ return new ReferenceName.internal(ReferenceNameKind.Member,
+ node.name.text, new ReferenceName.fromNamedNode(parent), uri);
+ } else if (parent is Library) {
+ return new ReferenceName.internal(ReferenceNameKind.Member,
+ node.name.text, new ReferenceName.fromNamedNode(parent), uri);
} else {
return new ReferenceName.internal(
- ReferenceNameKind.Member,
- node.name.text,
- new ReferenceName.fromNamedNode(node.enclosingLibrary),
- uri);
+ ReferenceNameKind.Member, node.name.text, null, uri);
}
} else {
throw new ArgumentError(
diff --git a/pkg/kernel/lib/src/node_creator.dart b/pkg/kernel/lib/src/node_creator.dart
new file mode 100644
index 0000000..7100c5d
--- /dev/null
+++ b/pkg/kernel/lib/src/node_creator.dart
@@ -0,0 +1,1354 @@
+// 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 '../ast.dart';
+import 'coverage.dart';
+
+/// Helper class used to generate ASTs that contain all different nodes.
+class NodeCreator {
+ final Uri _uri;
+
+ /// The parent [Component] for all created libraries, classes, extensions,
+ /// typedefs and members.
+ final Component _component = new Component();
+
+ /// These fields contain maps of requested nodes different kinds that are
+ /// still pending. The mapped values are used to track how many nodes of the
+ /// specific kind have been created. When all variants of a kind have been
+ /// created, the entry is removed from the map.
+ final Map<ExpressionKind, int> _pendingExpressions;
+ final Map<StatementKind, int> _pendingStatements;
+ final Map<DartTypeKind, int> _pendingDartTypes;
+ final Map<ConstantKind, int> _pendingConstants;
+ final Map<InitializerKind, int> _pendingInitializers;
+ final Map<MemberKind, int> _pendingMembers;
+ final Map<NodeKind, int> _pendingNodes;
+
+ /// The set of all kinds of nodes created by this node creator.
+ final Set<Object> _createdKinds = {};
+
+ /// These fields contain list of nodes needed for the creation of other nodes.
+ ///
+ /// Needed nodes are nodes that need to exist prior to the node that required
+ /// it. For instance, to create an [InterfaceType] node, a [Class] node must
+ /// exist for the [InterfaceType] to reference.
+ ///
+ /// Needed nodes are added to the context of the created nodes. For instance,
+ /// a needed [Class] is added to the [_component] and a needed
+ /// [VariableDeclaration] is added to a enclosing [Block].
+ List<Library> _neededLibraries = [];
+ List<Class> _neededClasses = [];
+ List<Extension> _neededExtensions = [];
+ List<Typedef> _neededTypedefs = [];
+ List<TypeParameter> _neededTypeParameters = [];
+ List<Constructor> _neededConstructors = [];
+ List<Procedure> _neededRedirectingFactories = [];
+ List<Procedure> _neededProcedures = [];
+ List<Field> _neededFields = [];
+ List<LibraryDependency> _neededLibraryDependencies = [];
+ List<VariableDeclaration> _neededVariableDeclarations = [];
+ List<LabeledStatement> _neededLabeledStatements = [];
+ List<FunctionDeclaration> _neededFunctionDeclarations = [];
+ List<SwitchCase> _neededSwitchCases = [];
+
+ /// Creates a [NodeCreator] requested to create nodes of the specified kinds.
+ NodeCreator({
+ Iterable<ExpressionKind> expressions: ExpressionKind.values,
+ Iterable<StatementKind> statements: StatementKind.values,
+ Iterable<DartTypeKind> dartTypes: DartTypeKind.values,
+ Iterable<ConstantKind> constants: ConstantKind.values,
+ Iterable<InitializerKind> initializers: InitializerKind.values,
+ Iterable<MemberKind> members: MemberKind.values,
+ Iterable<NodeKind> nodes: NodeKind.values,
+ }) : _pendingExpressions = new Map<ExpressionKind, int>.fromIterables(
+ expressions, new List<int>.filled(expressions.length, 0)),
+ _pendingStatements = new Map<StatementKind, int>.fromIterables(
+ statements, new List<int>.filled(statements.length, 0)),
+ _pendingDartTypes = new Map<DartTypeKind, int>.fromIterables(
+ dartTypes, new List<int>.filled(dartTypes.length, 0)),
+ _pendingConstants = new Map<ConstantKind, int>.fromIterables(
+ constants, new List<int>.filled(constants.length, 0)),
+ _pendingInitializers = new Map<InitializerKind, int>.fromIterables(
+ initializers, new List<int>.filled(initializers.length, 0)),
+ _pendingMembers = new Map<MemberKind, int>.fromIterables(
+ members, new List<int>.filled(members.length, 0)),
+ _pendingNodes = new Map<NodeKind, int>.fromIterables(
+ nodes, new List<int>.filled(nodes.length, 0)),
+ _uri = Uri.parse('test:uri') {
+ _createdKinds.addAll(_pendingExpressions.keys);
+ _createdKinds.addAll(_pendingStatements.keys);
+ _createdKinds.addAll(_pendingDartTypes.keys);
+ _createdKinds.addAll(_pendingInitializers.keys);
+ _createdKinds.addAll(_pendingMembers.keys);
+ _createdKinds.addAll(_pendingNodes.keys);
+ }
+
+ /// The kinds created by this node creator.
+ Iterable<Object> get createdKinds => _createdKinds;
+
+ /// Wraps [statement] in nodes needed in the statement context.
+ ///
+ /// For instance, if a [LabeledStatement] was needed for the creation of
+ /// [statement], [statement] is wrapped inside the labeled statement.
+ Statement _ensureContext(Statement statement) {
+ if (_neededSwitchCases.isNotEmpty) {
+ statement = SwitchStatement(NullLiteral(), [
+ ..._neededSwitchCases,
+ SwitchCase([NullLiteral()], [TreeNode.noOffset], Block([statement]))
+ ]);
+ }
+ _neededSwitchCases.clear();
+ for (LabeledStatement labeledStatement in _neededLabeledStatements) {
+ labeledStatement.body = statement;
+ statement = labeledStatement;
+ }
+ _neededLabeledStatements.clear();
+ statement = Block([
+ ..._neededVariableDeclarations,
+ ..._neededFunctionDeclarations,
+ statement
+ ]);
+ _neededFunctionDeclarations.clear();
+ _neededVariableDeclarations.clear();
+ return statement;
+ }
+
+ /// Adds [statement] to [statements] including any nodes needed in the
+ /// context.
+ void _addStatement(List<Statement> statements, Statement statement) {
+ statements.add(_ensureContext(statement));
+ }
+
+ /// Adds [expression] to [statements] including any nodes needed in the
+ /// context.
+ void _addExpression(List<Statement> statements, Expression expression) {
+ _addStatement(statements, ExpressionStatement(expression));
+ }
+
+ /// Adds [type] to [statements] including any nodes needed in the context.
+ void _addDartType(List<Statement> statements, DartType type) {
+ _addExpression(statements, TypeLiteral(type));
+ }
+
+ /// Adds [constant] to [statements] including any nodes needed in the context.
+ void _addConstant(List<Statement> statements, Constant constant) {
+ _addExpression(statements, ConstantExpression(constant));
+ }
+
+ /// Generates a list of [Statement] containing all pending in-body nodes.
+ List<Statement> _generateBodies() {
+ List<Statement> statements = [];
+ while (_pendingStatements.isNotEmpty) {
+ _addStatement(statements, _createStatement());
+ }
+ while (_pendingExpressions.isNotEmpty) {
+ _addExpression(statements, _createExpression());
+ }
+ while (_pendingDartTypes.isNotEmpty) {
+ _addDartType(statements, _createDartType());
+ }
+ while (_pendingConstants.isNotEmpty) {
+ _addConstant(statements, _createConstant());
+ }
+ for (NodeKind kind in inBodyNodeKinds) {
+ while (_pendingNodes.containsKey(kind)) {
+ Node node = _createNodeFromKind(kind);
+ switch (kind) {
+ case NodeKind.Name:
+ _addExpression(
+ statements,
+ DynamicGet(DynamicAccessKind.Dynamic, _createExpression(),
+ node as Name));
+ break;
+ case NodeKind.Arguments:
+ _addExpression(
+ statements,
+ DynamicInvocation(DynamicAccessKind.Dynamic,
+ _createExpression(), _createName(), node as Arguments));
+ break;
+ case NodeKind.Catch:
+ _addStatement(
+ statements, TryCatch(_createStatement(), [node as Catch]));
+ break;
+ case NodeKind.FunctionNode:
+ _addExpression(
+ statements, FunctionExpression(node as FunctionNode));
+ break;
+ case NodeKind.MapLiteralEntry:
+ _addExpression(statements, MapLiteral([node as MapLiteralEntry]));
+ break;
+ case NodeKind.NamedExpression:
+ _addExpression(
+ statements,
+ DynamicInvocation(
+ DynamicAccessKind.Dynamic,
+ _createExpression(),
+ _createName(),
+ Arguments([], named: [node as NamedExpression])));
+ break;
+ case NodeKind.NamedType:
+ _addDartType(
+ statements,
+ FunctionType([], _createDartType(), Nullability.nonNullable,
+ namedParameters: [node as NamedType]));
+ break;
+ case NodeKind.SwitchCase:
+ _addStatement(statements,
+ SwitchStatement(_createExpression(), [node as SwitchCase]));
+ break;
+ case NodeKind.TypeParameter:
+ _addExpression(
+ statements,
+ FunctionExpression(FunctionNode(Block([]),
+ typeParameters: [node as TypeParameter])));
+ break;
+ default:
+ throw new UnimplementedError('Unhandled in body node $kind.');
+ }
+ }
+ }
+ return statements;
+ }
+
+ /// Generates [Statement]s containing occurrences of all requested nodes.
+ List<Statement> generateBodies() {
+ List<Statement> statements = _generateBodies();
+ Set<Object> unsupportedKinds = {};
+ if (_pendingInitializers.isNotEmpty) {
+ unsupportedKinds.addAll(_pendingInitializers.keys);
+ }
+ if (_pendingMembers.isNotEmpty) {
+ unsupportedKinds.addAll(_pendingMembers.keys);
+ }
+ if (_pendingNodes.isNotEmpty) {
+ assert(
+ _pendingNodes.keys.every((kind) => !inBodyNodeKinds.contains(kind)));
+ unsupportedKinds.addAll(_pendingNodes.keys);
+ }
+ if (unsupportedKinds.isNotEmpty) {
+ throw new UnsupportedError('Cannot create these node in a body context: '
+ '${unsupportedKinds.join(', ')}');
+ }
+ return statements;
+ }
+
+ /// Generates a [Component] containing occurrences of all requested and needed
+ /// nodes.
+ Component generateComponent() {
+ Class cls = _needClass();
+ for (Statement statement in _generateBodies()) {
+ cls.addProcedure(Procedure(
+ _createName(), ProcedureKind.Method, FunctionNode(statement),
+ fileUri: _uri));
+ }
+ while (_pendingInitializers.isNotEmpty) {
+ Initializer initializer = _createInitializer();
+ cls.addConstructor(Constructor(FunctionNode(null),
+ name: _createName(), fileUri: _uri, initializers: [initializer]));
+ }
+ while (_pendingMembers.isNotEmpty) {
+ Member member = _createMember();
+ if (member is Procedure) {
+ cls.addProcedure(member);
+ } else if (member is Field) {
+ cls.addField(member);
+ } else if (member is Constructor) {
+ cls.addConstructor(member);
+ } else if (member is RedirectingFactory) {
+ cls.addRedirectingFactory(member);
+ } else {
+ throw new UnsupportedError(
+ 'Unexpected member $member (${member.runtimeType})');
+ }
+ }
+ while (_pendingNodes.isNotEmpty) {
+ NodeKind kind = _pendingNodes.keys.first;
+ Node node = _createNodeFromKind(kind);
+ switch (kind) {
+ case NodeKind.Name:
+ case NodeKind.Arguments:
+ case NodeKind.Catch:
+ case NodeKind.FunctionNode:
+ case NodeKind.MapLiteralEntry:
+ case NodeKind.NamedExpression:
+ case NodeKind.NamedType:
+ case NodeKind.SwitchCase:
+ case NodeKind.TypeParameter:
+ throw new UnimplementedError('Expected in body node $kind.');
+ case NodeKind.Class:
+ _needLibrary().addClass(node as Class);
+ break;
+ case NodeKind.Combinator:
+ _needLibrary().addDependency(LibraryDependency.import(_needLibrary(),
+ combinators: [node as Combinator]));
+ break;
+ case NodeKind.Component:
+ assert(identical(node, _component),
+ "Cannot create multiple Component nodes.");
+ break;
+ case NodeKind.Extension:
+ _needLibrary().addExtension(node as Extension);
+ break;
+ case NodeKind.Library:
+ _component.libraries.add(node as Library);
+ break;
+ case NodeKind.LibraryDependency:
+ _needLibrary().addDependency(node as LibraryDependency);
+ break;
+ case NodeKind.LibraryPart:
+ _needLibrary().addPart(node as LibraryPart);
+ break;
+ case NodeKind.Supertype:
+ _needLibrary().addClass(
+ Class(name: 'foo', fileUri: _uri, supertype: node as Supertype));
+ break;
+ case NodeKind.Typedef:
+ _needLibrary().addTypedef(node as Typedef);
+ break;
+ }
+ }
+ return _component;
+ }
+
+ /// Returns a [Library] node that fits the requirements.
+ ///
+ /// If no such [Library] exists in [_neededLibraries], a new [Library] is
+ /// created and added to [_neededLibraries].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ Library _needLibrary() {
+ for (Library library in _neededLibraries) {
+ return library;
+ }
+ Library library = Library(_uri, fileUri: _uri);
+ _neededLibraries.add(library);
+ _component.libraries.add(library);
+ return library;
+ }
+
+ /// Returns a [LibraryDependency] node that fits the requirements.
+ ///
+ /// If no such [LibraryDependency] exists in [_neededLibraryDependencies], a
+ /// new [LibraryDependency] is created and added to
+ /// [_neededLibraryDependencies].
+ LibraryDependency _needLibraryDependency({bool deferred: false}) {
+ for (LibraryDependency libraryDependency in _neededLibraryDependencies) {
+ if (!deferred || libraryDependency.isDeferred) {
+ return libraryDependency;
+ }
+ }
+ LibraryDependency libraryDependency = deferred
+ ? LibraryDependency.deferredImport(_needLibrary(), 'foo')
+ : LibraryDependency.import(_needLibrary());
+ _neededLibraryDependencies.add(libraryDependency);
+ return libraryDependency;
+ }
+
+ /// Returns a [Class] node that fits the requirements.
+ ///
+ /// If no such [Class] exists in [_neededClasses], a new [Class] is
+ /// created and added to [_neededClasses].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ Class _needClass() {
+ for (Class cls in _neededClasses) {
+ return cls;
+ }
+ Class cls = Class(name: 'Foo', fileUri: _uri);
+ _neededClasses.add(cls);
+ _needLibrary().addClass(cls);
+ return cls;
+ }
+
+ /// Returns a [Extension] node that fits the requirements.
+ ///
+ /// If no such [Extension] exists in [_neededExtensions], a new [Extension] is
+ /// created and added to [_neededExtensions].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ Extension _needExtension() {
+ for (Extension extension in _neededExtensions) {
+ return extension;
+ }
+ Extension extension = Extension(name: 'foo', fileUri: _uri)
+ ..onType = DynamicType();
+ _neededExtensions.add(extension);
+ _needLibrary().addExtension(extension);
+ return extension;
+ }
+
+ /// Returns a [Typedef] node that fits the requirements.
+ ///
+ /// If no such [Typedef] exists in [_neededTypedefs], a new [Typedef] is
+ /// created and added to [_neededTypedefs].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ Typedef _needTypedef() {
+ for (Typedef typedef in _neededTypedefs) {
+ return typedef;
+ }
+ Typedef typedef = Typedef('foo', DynamicType(), fileUri: _uri);
+ _neededTypedefs.add(typedef);
+ _needLibrary().addTypedef(typedef);
+ return typedef;
+ }
+
+ /// Returns a [TypeParameter] node that fits the requirements.
+ ///
+ /// If no such [TypeParameter] exists in [_neededTypeParameters], a new
+ /// [TypeParameter] is created and added to [_neededTypeParameters].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ TypeParameter _needTypeParameter() {
+ for (TypeParameter typeParameter in _neededTypeParameters) {
+ return typeParameter;
+ }
+ TypeParameter typeParameter =
+ TypeParameter('foo', DynamicType(), DynamicType());
+ _neededTypeParameters.add(typeParameter);
+ // TODO(johnniwinther): Add the type parameter to a context; class, method
+ // or function type.
+ return typeParameter;
+ }
+
+ /// Returns a [Procedure] node that fits the requirements.
+ ///
+ /// If no such [Procedure] exists in [_neededProcedures], a new [Library] is
+ /// created and added to [_neededProcedures].
+ ///
+ /// [index] is used to create multiple distinct [Procedure] nodes even when
+ /// these have the same requirements.
+ Procedure _needProcedure({int? index, bool? isStatic}) {
+ for (Procedure procedure in _neededProcedures) {
+ if (isStatic == null || isStatic == procedure.isStatic) {
+ if (index == null || index == 0) {
+ return procedure;
+ } else {
+ index--;
+ }
+ }
+ }
+ isStatic ??= true;
+ Procedure procedure = Procedure(
+ Name('foo'), ProcedureKind.Method, FunctionNode(Block([])),
+ fileUri: _uri, isStatic: isStatic);
+ _neededProcedures.add(procedure);
+ if (isStatic) {
+ _needLibrary().addProcedure(procedure);
+ } else {
+ _needClass().addProcedure(procedure);
+ }
+ return procedure;
+ }
+
+ /// Returns a [Constructor] node that fits the requirements.
+ ///
+ /// If no such [Constructor] exists in [_neededConstructors], a new
+ /// [Constructor] is created and added to [_neededConstructors].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ Constructor _needConstructor() {
+ for (Constructor constructor in _neededConstructors) {
+ return constructor;
+ }
+ Constructor constructor =
+ Constructor(FunctionNode(null), name: Name('foo'), fileUri: _uri);
+ _needClass().addConstructor(constructor);
+ return constructor;
+ }
+
+ /// Returns a redirecting factory [Procedure] node that fits the requirements.
+ ///
+ /// If no such [Library] exists in [_neededRedirectingFactories], a new
+ /// [Procedure] is created and added to [_neededRedirectingFactories].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ Procedure _needRedirectingFactory() {
+ for (Procedure redirectingFactory in _neededRedirectingFactories) {
+ return redirectingFactory;
+ }
+ Procedure redirectingFactory = Procedure(
+ Name('foo'), ProcedureKind.Method, FunctionNode(null),
+ fileUri: _uri)
+ ..isRedirectingFactory = true;
+ _needClass().addProcedure(redirectingFactory);
+ return redirectingFactory;
+ }
+
+ /// Returns a [Field] node that fits the requirements.
+ ///
+ /// If no such [Field] exists in [_neededFields], a new [Field] is
+ /// created and added to [_neededFields].
+ ///
+ /// [index] is used to create multiple distinct [Field] nodes even when
+ /// these have the same requirements.
+ Field _needField({int? index, bool? isStatic, bool? hasSetter}) {
+ for (Field field in _neededFields) {
+ if (isStatic == null ||
+ isStatic == field.isStatic && hasSetter == null ||
+ hasSetter == field.hasSetter) {
+ if (index == null || index == 0) {
+ return field;
+ } else {
+ index--;
+ }
+ }
+ }
+ hasSetter ??= false;
+ isStatic ??= true;
+ Field field = hasSetter
+ ? new Field.immutable(Name('foo'), fileUri: _uri, isStatic: isStatic)
+ : new Field.mutable(Name('foo'), fileUri: _uri, isStatic: isStatic);
+ _neededFields.add(field);
+ if (isStatic) {
+ _needLibrary().addField(field);
+ } else {
+ _needClass().addField(field);
+ }
+ return field;
+ }
+
+ /// Returns a [VariableDeclaration] node that fits the requirements.
+ ///
+ /// If no such [VariableDeclaration] exists in [_neededVariableDeclarations],
+ /// a new [VariableDeclaration] is created and added to
+ /// [_neededVariableDeclarations].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ VariableDeclaration _needVariableDeclaration() {
+ for (VariableDeclaration variableDeclaration
+ in _neededVariableDeclarations) {
+ return variableDeclaration;
+ }
+ VariableDeclaration variableDeclaration = VariableDeclaration('foo');
+ _neededVariableDeclarations.add(variableDeclaration);
+ return variableDeclaration;
+ }
+
+ /// Returns a [LabeledStatement] node that fits the requirements.
+ ///
+ /// If no such [LabeledStatement] exists in [_neededLabeledStatements], a new
+ /// [LabeledStatement] is created and added to [_neededLabeledStatements].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ LabeledStatement _needLabeledStatement() {
+ for (LabeledStatement labeledStatement in _neededLabeledStatements) {
+ return labeledStatement;
+ }
+ LabeledStatement labeledStatement = LabeledStatement(null);
+ _neededLabeledStatements.add(labeledStatement);
+ return labeledStatement;
+ }
+
+ /// Returns a [SwitchCase] node that fits the requirements.
+ ///
+ /// If no such [SwitchCase] exists in [_neededSwitchCases], a new [SwitchCase]
+ /// is created and added to [_neededSwitchCases].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ SwitchCase _needSwitchCase() {
+ for (SwitchCase switchCase in _neededSwitchCases) {
+ return switchCase;
+ }
+ SwitchCase switchCase =
+ SwitchCase([NullLiteral()], [TreeNode.noOffset], EmptyStatement());
+ _neededSwitchCases.add(switchCase);
+ return switchCase;
+ }
+
+ /// Returns a [FunctionDeclaration] node that fits the requirements.
+ ///
+ /// If no such [FunctionDeclaration] exists in [_neededFunctionDeclarations],
+ /// a new [FunctionDeclaration] is created and added to
+ /// [_neededFunctionDeclarations].
+ // TODO(johnniwinther): Add requirements when/where needed.
+ FunctionDeclaration _needFunctionDeclaration() {
+ for (FunctionDeclaration functionDeclaration
+ in _neededFunctionDeclarations) {
+ return functionDeclaration;
+ }
+ FunctionDeclaration functionDeclaration = FunctionDeclaration(
+ VariableDeclaration('foo'), FunctionNode(Block([])));
+ _neededFunctionDeclarations.add(functionDeclaration);
+ return functionDeclaration;
+ }
+
+ /// Creates an [Expression] node.
+ ///
+ /// If there are any pending expressions, one of these is created.
+ Expression _createExpression() {
+ if (_pendingExpressions.isEmpty) {
+ return NullLiteral();
+ }
+ ExpressionKind kind = _pendingExpressions.keys.first;
+ return _createExpressionFromKind(kind);
+ }
+
+ /// Creates an [Expression] node of the specified [kind].
+ ///
+ /// If there are any pending expressions of this [kind], one of these is
+ /// created.
+ Expression _createExpressionFromKind(ExpressionKind kind) {
+ int? index = _pendingExpressions.remove(kind);
+ switch (kind) {
+ case ExpressionKind.AsExpression:
+ return AsExpression(_createExpression(), _createDartType());
+ case ExpressionKind.AwaitExpression:
+ return AwaitExpression(_createExpression());
+ case ExpressionKind.BlockExpression:
+ return BlockExpression(
+ _createStatementFromKind(StatementKind.Block) as Block,
+ _createExpression());
+ case ExpressionKind.BoolLiteral:
+ return BoolLiteral(true);
+ case ExpressionKind.CheckLibraryIsLoaded:
+ return CheckLibraryIsLoaded(_needLibraryDependency(deferred: true));
+ case ExpressionKind.ConditionalExpression:
+ return ConditionalExpression(_createExpression(), _createExpression(),
+ _createExpression(), _createDartType());
+ case ExpressionKind.ConstantExpression:
+ return ConstantExpression(_createConstant(), _createDartType());
+ case ExpressionKind.ConstructorInvocation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => ConstructorInvocation(_needConstructor(), _createArguments(),
+ isConst: false),
+ () => ConstructorInvocation(_needConstructor(), _createArguments(),
+ isConst: true),
+ ]);
+ case ExpressionKind.ConstructorTearOff:
+ return ConstructorTearOff(_needConstructor());
+ case ExpressionKind.DoubleLiteral:
+ return DoubleLiteral(42.5);
+ case ExpressionKind.DynamicGet:
+ return DynamicGet(
+ DynamicAccessKind.Dynamic, _createExpression(), _createName());
+ case ExpressionKind.DynamicInvocation:
+ return DynamicInvocation(DynamicAccessKind.Dynamic, _createExpression(),
+ _createName(), _createArguments());
+ case ExpressionKind.DynamicSet:
+ return DynamicSet(DynamicAccessKind.Dynamic, _createExpression(),
+ _createName(), _createExpression());
+ case ExpressionKind.EqualsCall:
+ return EqualsCall(_createExpression(), _createExpression(),
+ functionType: _createFunctionType(),
+ interfaceTarget: _needProcedure());
+ case ExpressionKind.EqualsNull:
+ return EqualsNull(_createExpression());
+ case ExpressionKind.FileUriExpression:
+ return FileUriExpression(_createExpression(), _uri);
+ case ExpressionKind.FunctionExpression:
+ return FunctionExpression(_createFunctionNode());
+ case ExpressionKind.FunctionInvocation:
+ return FunctionInvocation(FunctionAccessKind.FunctionType,
+ _createExpression(), _createArguments(),
+ functionType: _createFunctionType());
+ case ExpressionKind.FunctionTearOff:
+ return FunctionTearOff(_createExpression());
+ case ExpressionKind.InstanceCreation:
+ return InstanceCreation(_needClass().reference, [], {}, [], []);
+ case ExpressionKind.InstanceGet:
+ return InstanceGet(
+ InstanceAccessKind.Instance, _createExpression(), _createName(),
+ interfaceTarget: _needField(), resultType: _createDartType());
+ case ExpressionKind.InstanceGetterInvocation:
+ return InstanceGetterInvocation(InstanceAccessKind.Instance,
+ _createExpression(), _createName(), _createArguments(),
+ interfaceTarget: _needField(), functionType: _createFunctionType());
+ case ExpressionKind.InstanceInvocation:
+ return InstanceInvocation(InstanceAccessKind.Instance,
+ _createExpression(), _createName(), _createArguments(),
+ interfaceTarget: _needProcedure(),
+ functionType: _createFunctionType())
+ ..isBoundsSafe = true;
+ case ExpressionKind.InstanceSet:
+ return InstanceSet(InstanceAccessKind.Instance, _createExpression(),
+ _createName(), _createExpression(),
+ interfaceTarget: _needField());
+ case ExpressionKind.InstanceTearOff:
+ return InstanceTearOff(
+ InstanceAccessKind.Instance, _createExpression(), _createName(),
+ interfaceTarget: _needProcedure(), resultType: _createDartType());
+ case ExpressionKind.Instantiation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => Instantiation(_createExpression(), []),
+ () => Instantiation(_createExpression(), [_createDartType()]),
+ () => Instantiation(
+ _createExpression(), [_createDartType(), _createDartType()]),
+ ]);
+ case ExpressionKind.IntLiteral:
+ return IntLiteral(42);
+ case ExpressionKind.InvalidExpression:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => InvalidExpression(null),
+ () => InvalidExpression('foo'),
+ () => InvalidExpression('foo', _createExpression()),
+ ]);
+ case ExpressionKind.IsExpression:
+ return IsExpression(
+ _createExpression(),
+ _createDartType(),
+ );
+ case ExpressionKind.Let:
+ return Let(
+ _createStatementFromKind(StatementKind.VariableDeclaration)
+ as VariableDeclaration,
+ _createExpression());
+ case ExpressionKind.ListConcatenation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => ListConcatenation([], typeArgument: _createDartType()),
+ () => ListConcatenation([_createExpression()],
+ typeArgument: _createDartType()),
+ () => ListConcatenation([_createExpression(), _createExpression()],
+ typeArgument: _createDartType()),
+ ]);
+ case ExpressionKind.ListLiteral:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () =>
+ ListLiteral([], typeArgument: _createDartType(), isConst: false),
+ () => ListLiteral([_createExpression()],
+ typeArgument: _createDartType(), isConst: false),
+ () => ListLiteral([_createExpression(), _createExpression()],
+ typeArgument: _createDartType(), isConst: false),
+ () => ListLiteral([_createExpression(), _createExpression()],
+ typeArgument: _createDartType(), isConst: true),
+ ]);
+ case ExpressionKind.LoadLibrary:
+ return LoadLibrary(_needLibraryDependency(deferred: true));
+ case ExpressionKind.LocalFunctionInvocation:
+ return LocalFunctionInvocation(
+ _needFunctionDeclaration().variable, _createArguments(),
+ functionType: _createFunctionType());
+ case ExpressionKind.LogicalExpression:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => LogicalExpression(_createExpression(),
+ LogicalExpressionOperator.AND, _createExpression()),
+ () => LogicalExpression(_createExpression(),
+ LogicalExpressionOperator.OR, _createExpression()),
+ ]);
+ case ExpressionKind.MapConcatenation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => MapConcatenation([],
+ keyType: _createDartType(), valueType: _createDartType()),
+ () => MapConcatenation([_createExpression()],
+ keyType: _createDartType(), valueType: _createDartType()),
+ () => MapConcatenation([_createExpression(), _createExpression()],
+ keyType: _createDartType(), valueType: _createDartType()),
+ ]);
+ case ExpressionKind.MapLiteral:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => MapLiteral([],
+ keyType: _createDartType(),
+ valueType: _createDartType(),
+ isConst: false),
+ () => MapLiteral([_createMapLiteralEntry()],
+ keyType: _createDartType(),
+ valueType: _createDartType(),
+ isConst: false),
+ () => MapLiteral([
+ _createMapLiteralEntry(),
+ _createMapLiteralEntry(),
+ ],
+ keyType: _createDartType(),
+ valueType: _createDartType(),
+ isConst: false),
+ () => MapLiteral([
+ _createMapLiteralEntry(),
+ _createMapLiteralEntry(),
+ ],
+ keyType: _createDartType(),
+ valueType: _createDartType(),
+ isConst: true),
+ ]);
+ case ExpressionKind.Not:
+ return Not(_createExpression());
+ case ExpressionKind.NullCheck:
+ return NullCheck(_createExpression());
+ case ExpressionKind.NullLiteral:
+ return NullLiteral();
+ case ExpressionKind.RedirectingFactoryTearOff:
+ return RedirectingFactoryTearOff(_needRedirectingFactory());
+ case ExpressionKind.Rethrow:
+ return Rethrow();
+ case ExpressionKind.SetConcatenation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => SetConcatenation([], typeArgument: _createDartType()),
+ () => SetConcatenation([_createExpression()],
+ typeArgument: _createDartType()),
+ () => SetConcatenation([_createExpression(), _createExpression()],
+ typeArgument: _createDartType()),
+ ]);
+ case ExpressionKind.SetLiteral:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => SetLiteral([], typeArgument: _createDartType(), isConst: false),
+ () => SetLiteral([_createExpression()],
+ typeArgument: _createDartType(), isConst: false),
+ () => SetLiteral([_createExpression(), _createExpression()],
+ typeArgument: _createDartType(), isConst: false),
+ () => SetLiteral([_createExpression(), _createExpression()],
+ typeArgument: _createDartType(), isConst: true),
+ ]);
+ case ExpressionKind.StaticGet:
+ return StaticGet(_needField());
+ case ExpressionKind.StaticInvocation:
+ return StaticInvocation(_needProcedure(), _createArguments());
+ case ExpressionKind.StaticSet:
+ return StaticSet(_needField(), _createExpression());
+ case ExpressionKind.StaticTearOff:
+ return StaticTearOff(_needProcedure());
+ case ExpressionKind.StringConcatenation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => StringConcatenation([]),
+ () => StringConcatenation([_createExpression()]),
+ () => StringConcatenation([_createExpression(), _createExpression()]),
+ ]);
+ case ExpressionKind.StringLiteral:
+ return StringLiteral('foo');
+ case ExpressionKind.SuperMethodInvocation:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => SuperMethodInvocation(_createName(), _createArguments()),
+ () => SuperMethodInvocation(
+ _createName(), _createArguments(), _needProcedure()),
+ ]);
+ case ExpressionKind.SuperPropertyGet:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => SuperPropertyGet(_createName()),
+ () => SuperPropertyGet(_createName(), _needField()),
+ ]);
+ case ExpressionKind.SuperPropertySet:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => SuperPropertySet(_createName(), _createExpression(), null),
+ () => SuperPropertySet(
+ _createName(), _createExpression(), _needField()),
+ ]);
+ case ExpressionKind.SymbolLiteral:
+ return SymbolLiteral('foo');
+ case ExpressionKind.ThisExpression:
+ return ThisExpression();
+ case ExpressionKind.Throw:
+ return Throw(_createExpression());
+ case ExpressionKind.TypeLiteral:
+ return TypeLiteral(_createDartType());
+ case ExpressionKind.TypedefTearOff:
+ // TODO(johnniwinther): Add non-trivial cases.
+ return TypedefTearOff([], _createExpression(), []);
+ case ExpressionKind.VariableGet:
+ return _createOneOf(_pendingExpressions, kind, index, [
+ () => VariableGet(_needVariableDeclaration()),
+ () => VariableGet(_needVariableDeclaration(), _createDartType()),
+ ]);
+ case ExpressionKind.VariableSet:
+ return VariableSet(_needVariableDeclaration(), _createExpression());
+ }
+ }
+
+ /// Creates a [Statement] node.
+ ///
+ /// If there are any pending statements, one of these is created.
+ Statement _createStatement() {
+ if (_pendingStatements.isEmpty) {
+ return EmptyStatement();
+ }
+ StatementKind kind = _pendingStatements.keys.first;
+ return _createStatementFromKind(kind);
+ }
+
+ /// Creates a [Statement] of the specified [kind].
+ ///
+ /// If there are any pending statements of this [kind], one of these is
+ /// created.
+ Statement _createStatementFromKind(StatementKind kind) {
+ int? index = _pendingStatements.remove(kind);
+ switch (kind) {
+ case StatementKind.AssertBlock:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => AssertBlock([]),
+ () => AssertBlock([_createStatement()]),
+ () => AssertBlock([_createStatement(), _createStatement()]),
+ ]);
+ case StatementKind.AssertStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => AssertStatement(_createExpression(),
+ conditionStartOffset: TreeNode.noOffset,
+ conditionEndOffset: TreeNode.noOffset),
+ () => AssertStatement(_createExpression(),
+ message: _createExpression(),
+ conditionStartOffset: TreeNode.noOffset,
+ conditionEndOffset: TreeNode.noOffset),
+ ]);
+ case StatementKind.Block:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => Block([]),
+ () => Block([_createStatement()]),
+ () => Block([_createStatement(), _createStatement()]),
+ ]);
+ case StatementKind.BreakStatement:
+ return BreakStatement(_needLabeledStatement());
+ case StatementKind.ContinueSwitchStatement:
+ return ContinueSwitchStatement(_needSwitchCase());
+ case StatementKind.DoStatement:
+ return DoStatement(_createStatement(), _createExpression());
+ case StatementKind.EmptyStatement:
+ return EmptyStatement();
+ case StatementKind.ExpressionStatement:
+ return ExpressionStatement(_createExpression());
+ case StatementKind.ForInStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => ForInStatement(_createVariableDeclaration(),
+ _createExpression(), _createStatement(),
+ isAsync: false),
+ () => ForInStatement(_createVariableDeclaration(),
+ _createExpression(), _createStatement(),
+ isAsync: true),
+ ]);
+ case StatementKind.ForStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => ForStatement([], null, [], _createStatement()),
+ () => ForStatement([_createVariableDeclaration()],
+ _createExpression(), [_createExpression()], _createStatement()),
+ () => ForStatement(
+ [_createVariableDeclaration(), _createVariableDeclaration()],
+ _createExpression(),
+ [_createExpression(), _createExpression()],
+ _createStatement()),
+ ]);
+ case StatementKind.FunctionDeclaration:
+ return FunctionDeclaration(
+ VariableDeclaration(null), _createFunctionNode());
+ case StatementKind.IfStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => IfStatement(_createExpression(), _createStatement(), null),
+ () => IfStatement(
+ _createExpression(), _createStatement(), _createStatement()),
+ ]);
+ case StatementKind.LabeledStatement:
+ return LabeledStatement(_createStatement());
+ case StatementKind.ReturnStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => ReturnStatement(),
+ () => ReturnStatement(_createExpression()),
+ ]);
+ case StatementKind.SwitchStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => SwitchStatement(_createExpression(), []),
+ () => SwitchStatement(_createExpression(), [_createSwitchCase()]),
+ () => SwitchStatement(
+ _createExpression(), [_createSwitchCase(), _createSwitchCase()]),
+ ]);
+ case StatementKind.TryCatch:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => TryCatch(_createStatement(), []),
+ () => TryCatch(_createStatement(), [_createCatch()]),
+ () => TryCatch(_createStatement(), [_createCatch(), _createCatch()]),
+ ]);
+ case StatementKind.TryFinally:
+ return TryFinally(_createStatement(), _createStatement());
+ case StatementKind.VariableDeclaration:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => VariableDeclaration('foo'),
+ () => VariableDeclaration('foo', initializer: _createExpression()),
+ () => VariableDeclaration('foo', type: _createDartType()),
+ ]);
+ case StatementKind.WhileStatement:
+ return WhileStatement(_createExpression(), _createStatement());
+ case StatementKind.YieldStatement:
+ return _createOneOf(_pendingStatements, kind, index, [
+ () => YieldStatement(_createExpression(), isYieldStar: false),
+ () => YieldStatement(_createExpression(), isYieldStar: true),
+ ]);
+ }
+ }
+
+ /// Creates a [VariableDeclaration] node.
+ ///
+ /// If there are any pending [VariableDeclaration] nodes, one of these is
+ /// created.
+ VariableDeclaration _createVariableDeclaration() {
+ return _createStatementFromKind(StatementKind.VariableDeclaration)
+ as VariableDeclaration;
+ }
+
+ /// Creates a [DartType] node.
+ ///
+ /// If there are any pending types, one of these is created.
+ DartType _createDartType() {
+ if (_pendingDartTypes.isEmpty) {
+ return VoidType();
+ }
+ DartTypeKind kind = _pendingDartTypes.keys.first;
+ return _createDartTypeFromKind(kind);
+ }
+
+ /// Creates a [DartType] node of the specified [kind].
+ ///
+ /// If there are any pending types of this [kind], one of these is created.
+ DartType _createDartTypeFromKind(DartTypeKind kind) {
+ int? index = _pendingDartTypes.remove(kind);
+ switch (kind) {
+ case DartTypeKind.DynamicType:
+ return DynamicType();
+ case DartTypeKind.ExtensionType:
+ return ExtensionType(_needExtension(), Nullability.nonNullable);
+ case DartTypeKind.FunctionType:
+ return _createOneOf(_pendingDartTypes, kind, index, [
+ // TODO(johnniwinther): Create non-trivial cases.
+ () => FunctionType([], _createDartType(), Nullability.nonNullable),
+ ]);
+ case DartTypeKind.FutureOrType:
+ return FutureOrType(_createDartType(), Nullability.nonNullable);
+ case DartTypeKind.InterfaceType:
+ return _createOneOf(_pendingDartTypes, kind, index, [
+ // TODO(johnniwinther): Create non-trivial cases.
+ () => InterfaceType(_needClass(), Nullability.nonNullable, []),
+ ]);
+ case DartTypeKind.InvalidType:
+ return InvalidType();
+ case DartTypeKind.NeverType:
+ return NeverType.nonNullable();
+ case DartTypeKind.NullType:
+ return NullType();
+ case DartTypeKind.TypeParameterType:
+ return _createOneOf(_pendingDartTypes, kind, index, [
+ () =>
+ TypeParameterType(_needTypeParameter(), Nullability.nonNullable),
+ () => TypeParameterType(
+ _needTypeParameter(), Nullability.nonNullable, _createDartType()),
+ ]);
+ case DartTypeKind.TypedefType:
+ return _createOneOf(_pendingDartTypes, kind, index, [
+ // TODO(johnniwinther): Create non-trivial cases.
+ () => TypedefType(_needTypedef(), Nullability.nonNullable, []),
+ ]);
+ case DartTypeKind.VoidType:
+ return VoidType();
+ }
+ }
+
+ /// Creates a [FunctionType] node.
+ ///
+ /// If there are any pending [FunctionType] nodes, one of these is created.
+ FunctionType _createFunctionType() {
+ return _createDartTypeFromKind(DartTypeKind.FunctionType) as FunctionType;
+ }
+
+ /// Creates a [Constant] node.
+ ///
+ /// If there are any pending constants, one of these is created.
+ Constant _createConstant() {
+ if (_pendingConstants.isEmpty) {
+ return NullConstant();
+ }
+ ConstantKind kind = _pendingConstants.keys.first;
+ return _createConstantFromKind(kind);
+ }
+
+ /// Creates a [Constant] node of the specified [kind].
+ ///
+ /// If there are any pending constants of this [kind], one of these is
+ /// created.
+ Constant _createConstantFromKind(ConstantKind kind) {
+ int? index = _pendingConstants.remove(kind);
+ switch (kind) {
+ case ConstantKind.BoolConstant:
+ return BoolConstant(true);
+ case ConstantKind.ConstructorTearOffConstant:
+ return ConstructorTearOffConstant(_needConstructor());
+ case ConstantKind.DoubleConstant:
+ return DoubleConstant(42.5);
+ case ConstantKind.InstanceConstant:
+ return _createOneOf(_pendingConstants, kind, index, [
+ () => InstanceConstant(_needClass().reference, [], {}),
+ () => InstanceConstant(_needClass().reference, [
+ _createDartType()
+ ], {
+ _needField(isStatic: false, hasSetter: false).getterReference:
+ _createConstant()
+ }),
+ () => InstanceConstant(_needClass().reference, [
+ _createDartType()
+ ], {
+ _needField(index: 0, isStatic: false, hasSetter: false)
+ .getterReference: _createConstant(),
+ _needField(index: 1, isStatic: false, hasSetter: false)
+ .getterReference: _createConstant()
+ }),
+ ]);
+ case ConstantKind.InstantiationConstant:
+ return InstantiationConstant(_createConstant(), [_createDartType()]);
+ case ConstantKind.IntConstant:
+ return IntConstant(42);
+ case ConstantKind.ListConstant:
+ return _createOneOf(_pendingConstants, kind, index, [
+ () => ListConstant(_createDartType(), []),
+ () => ListConstant(_createDartType(), [_createConstant()]),
+ () => ListConstant(
+ _createDartType(), [_createConstant(), _createConstant()]),
+ ]);
+ case ConstantKind.MapConstant:
+ return _createOneOf(_pendingConstants, kind, index, [
+ () => MapConstant(_createDartType(), _createDartType(), []),
+ () => MapConstant(_createDartType(), _createDartType(),
+ [ConstantMapEntry(_createConstant(), _createConstant())]),
+ () => MapConstant(_createDartType(), _createDartType(), [
+ ConstantMapEntry(_createConstant(), _createConstant()),
+ ConstantMapEntry(_createConstant(), _createConstant())
+ ]),
+ ]);
+ case ConstantKind.NullConstant:
+ return NullConstant();
+ case ConstantKind.RedirectingFactoryTearOffConstant:
+ return RedirectingFactoryTearOffConstant(_needRedirectingFactory());
+ case ConstantKind.SetConstant:
+ return _createOneOf(_pendingConstants, kind, index, [
+ () => SetConstant(_createDartType(), []),
+ () => SetConstant(_createDartType(), [_createConstant()]),
+ () => SetConstant(
+ _createDartType(), [_createConstant(), _createConstant()]),
+ ]);
+ case ConstantKind.StaticTearOffConstant:
+ return StaticTearOffConstant(_needProcedure());
+ case ConstantKind.StringConstant:
+ return StringConstant('foo');
+ case ConstantKind.SymbolConstant:
+ return _createOneOf(_pendingConstants, kind, index, [
+ () => SymbolConstant('foo', null),
+ () => SymbolConstant('_foo', _needLibrary().reference),
+ ]);
+ case ConstantKind.TypeLiteralConstant:
+ return TypeLiteralConstant(_createDartType());
+ case ConstantKind.TypedefTearOffConstant:
+ // TODO(johnniwinther): Add non-trivial cases.
+ return TypedefTearOffConstant(
+ [],
+ _createConstantFromKind(ConstantKind.ConstructorTearOffConstant)
+ as TearOffConstant,
+ []);
+ case ConstantKind.UnevaluatedConstant:
+ return UnevaluatedConstant(_createExpression());
+ }
+ }
+
+ /// Creates an [Initializer] node.
+ ///
+ /// If there are any pending initializers, one of these is created.
+ Initializer _createInitializer() {
+ if (_pendingInitializers.isEmpty) {
+ return InvalidInitializer();
+ }
+ InitializerKind kind = _pendingInitializers.keys.first;
+ return _createInitializerFromKind(kind);
+ }
+
+ /// Creates an [Initializer] node of the specified [kind].
+ ///
+ /// If there are any pending initializers of this [kind], one of these is
+ /// created.
+ Initializer _createInitializerFromKind(InitializerKind kind) {
+ // ignore: unused_local_variable
+ int? index = _pendingInitializers.remove(kind);
+ switch (kind) {
+ case InitializerKind.AssertInitializer:
+ return AssertInitializer(
+ _createStatementFromKind(StatementKind.AssertStatement)
+ as AssertStatement);
+ case InitializerKind.FieldInitializer:
+ return FieldInitializer(
+ _needField(isStatic: false), _createExpression());
+ case InitializerKind.InvalidInitializer:
+ return InvalidInitializer();
+ case InitializerKind.LocalInitializer:
+ return LocalInitializer(_createVariableDeclaration());
+ case InitializerKind.RedirectingInitializer:
+ return RedirectingInitializer(_needConstructor(), _createArguments());
+ case InitializerKind.SuperInitializer:
+ return SuperInitializer(_needConstructor(), _createArguments());
+ }
+ }
+
+ /// Creates a [Member] node.
+ ///
+ /// If there are any pending members, one of these is created.
+ Member _createMember() {
+ if (_pendingMembers.isEmpty) {
+ return Field.immutable(_createName(), fileUri: _uri);
+ }
+ MemberKind kind = _pendingMembers.keys.first;
+ return _createMemberKind(kind);
+ }
+
+ /// Creates a [Member] node of the specified [kind].
+ ///
+ /// If there are any pending members of this [kind], one of these is created.
+ Member _createMemberKind(MemberKind kind) {
+ int? index = _pendingMembers.remove(kind);
+ switch (kind) {
+ case MemberKind.Constructor:
+ return Constructor(_createFunctionNode(),
+ name: _createName(), fileUri: _uri);
+ case MemberKind.Field:
+ return _createOneOf(_pendingMembers, kind, index, [
+ () => Field.mutable(_createName(), fileUri: _uri),
+ () => Field.immutable(_createName(), fileUri: _uri),
+ ]);
+ case MemberKind.Procedure:
+ return _createOneOf(_pendingMembers, kind, index, [
+ () => Procedure(
+ _createName(), ProcedureKind.Method, _createFunctionNode(),
+ fileUri: _uri),
+ () => Procedure(
+ _createName(), ProcedureKind.Operator, _createFunctionNode(),
+ fileUri: _uri),
+ () => Procedure(
+ _createName(), ProcedureKind.Getter, _createFunctionNode(),
+ fileUri: _uri),
+ () => Procedure(
+ _createName(), ProcedureKind.Setter, _createFunctionNode(),
+ fileUri: _uri),
+ ]);
+ case MemberKind.RedirectingFactory:
+ return RedirectingFactory(null,
+ name: _createName(),
+ function: _createFunctionNode(),
+ fileUri: _uri);
+ }
+ }
+
+ /// Creates an [Arguments] node.
+ ///
+ /// If there are any pending [Arguments] nodes, one of these is created.
+ Arguments _createArguments() {
+ return _createNodeFromKind(NodeKind.Arguments) as Arguments;
+ }
+
+ /// Creates a [Name] node.
+ ///
+ /// If there are any pending [Name] nodes, one of these is created.
+ Name _createName() {
+ return _createNodeFromKind(NodeKind.Name) as Name;
+ }
+
+ /// Creates a [FunctionNode] node.
+ ///
+ /// If there are any pending [FunctionNode] nodes, one of these is created.
+ FunctionNode _createFunctionNode() {
+ return _createNodeFromKind(NodeKind.FunctionNode) as FunctionNode;
+ }
+
+ /// Creates a [MapLiteralEntry] node.
+ ///
+ /// If there are any pending [MapLiteralEntry] nodes, one of these is created.
+ MapLiteralEntry _createMapLiteralEntry() {
+ return _createNodeFromKind(NodeKind.MapLiteralEntry) as MapLiteralEntry;
+ }
+
+ /// Creates a [SwitchCase] node.
+ ///
+ /// If there are any pending [SwitchCase] nodes, one of these is created.
+ SwitchCase _createSwitchCase() {
+ return _createNodeFromKind(NodeKind.SwitchCase) as SwitchCase;
+ }
+
+ /// Creates a [Catch] node.
+ ///
+ /// If there are any pending [Catch] nodes, one of these is created.
+ Catch _createCatch() {
+ return _createNodeFromKind(NodeKind.Catch) as Catch;
+ }
+
+ /// Creates a [Node] of the specified [kind].
+ ///
+ /// If there are any pending nodes of this [kind], one of these is created.
+ Node _createNodeFromKind(NodeKind kind) {
+ int? index = _pendingNodes.remove(kind);
+ switch (kind) {
+ case NodeKind.Arguments:
+ return _createOneOf(_pendingNodes, kind, index, [
+ // TODO(johnniwinther): Add non-trivial cases.
+ () => Arguments([]),
+ ]);
+ case NodeKind.Catch:
+ // TODO(johnniwinther): Add non-trivial cases.
+ return Catch(null, _createStatement());
+ case NodeKind.Class:
+ return Class(name: 'foo', fileUri: _uri);
+ case NodeKind.Combinator:
+ return _createOneOf(_pendingNodes, kind, index, [
+ () => Combinator.show([]),
+ () => Combinator.show(['foo']),
+ () => Combinator.show(['foo', 'bar']),
+ () => Combinator.hide([]),
+ () => Combinator.hide(['foo']),
+ () => Combinator.hide(['foo', 'bar']),
+ ]);
+ case NodeKind.Component:
+ return _component;
+ case NodeKind.Extension:
+ // TODO(johnniwinther): Add non-trivial cases.
+ return Extension(name: 'foo', fileUri: _uri)
+ ..onType = _createDartType();
+ case NodeKind.FunctionNode:
+ // TODO(johnniwinther): Add non-trivial cases.
+ return FunctionNode(_createStatement());
+ case NodeKind.Library:
+ return Library(_uri, fileUri: _uri);
+ case NodeKind.LibraryDependency:
+ return _createOneOf(_pendingNodes, kind, index, [
+ // TODO(johnniwinther): Add more cases.
+ () => LibraryDependency.import(_needLibrary()),
+ () => LibraryDependency.import(_needLibrary(), name: 'foo'),
+ () => LibraryDependency.export(_needLibrary()),
+ ]);
+ case NodeKind.LibraryPart:
+ // TODO(johnniwinther): Add non-trivial cases.
+ // TODO(johnniwinther): Do we need to use a valid part uri?
+ return LibraryPart([], 'foo');
+ case NodeKind.MapLiteralEntry:
+ return MapLiteralEntry(_createExpression(), _createExpression());
+ case NodeKind.Name:
+ return _createOneOf(_pendingNodes, kind, index, [
+ () => Name('foo'),
+ () => Name('_foo', _needLibrary()),
+ ]);
+ case NodeKind.NamedExpression:
+ return NamedExpression('foo', _createExpression());
+ case NodeKind.NamedType:
+ return NamedType('foo', _createDartType());
+ case NodeKind.Supertype:
+ return _createOneOf(_pendingNodes, kind, index, [
+ () => Supertype(_needClass(), []),
+ () => Supertype(_needClass(), [_createDartType()]),
+ () => Supertype(_needClass(), [_createDartType(), _createDartType()]),
+ ]);
+ case NodeKind.SwitchCase:
+ // TODO(johnniwinther): Add non-trivial cases.
+ return SwitchCase(
+ [NullLiteral()], [TreeNode.noOffset], _createStatement());
+ case NodeKind.TypeParameter:
+ return TypeParameter('foo', _createDartType(), _createDartType());
+ case NodeKind.Typedef:
+ return Typedef('foo', _createDartType(), fileUri: _uri);
+ }
+ }
+
+ /// Helper that creates a node of type [V] using the list of [creators].
+ ///
+ /// The [index] indicates how many nodes of the [kind] that have currently
+ /// been created. If there are more [creators] left after having created the
+ /// [index]th node, [pending] is updated with the next pending index. If all
+ /// pending nodes of the given [kind] have been created, [index] is `null` and
+ /// the first [creators] function is used.
+ V _createOneOf<K, V>(
+ Map<K, int> pending, K kind, int? index, List<V Function()> creators) {
+ if (index == null) {
+ // All pending nodes have been created so we just create the first.
+ return creators[0]();
+ }
+ if (index + 1 < creators.length) {
+ pending[kind] = index + 1;
+ }
+ return creators[index]();
+ }
+}
+
+/// [NodeKind]s for nodes that can occur inside member bodies.
+const Set<NodeKind> inBodyNodeKinds = {
+ NodeKind.Arguments,
+ NodeKind.Catch,
+ NodeKind.FunctionNode,
+ NodeKind.MapLiteralEntry,
+ NodeKind.Name,
+ NodeKind.NamedExpression,
+ NodeKind.NamedType,
+ NodeKind.SwitchCase,
+ NodeKind.TypeParameter,
+};
diff --git a/pkg/kernel/lib/src/text_util.dart b/pkg/kernel/lib/src/text_util.dart
index 5871066..ac95aac 100644
--- a/pkg/kernel/lib/src/text_util.dart
+++ b/pkg/kernel/lib/src/text_util.dart
@@ -33,10 +33,9 @@
String qualifiedClassNameToString(Class node,
{bool includeLibraryName: false}) {
- if (includeLibraryName) {
- return libraryNameToString(node.enclosingLibrary) +
- '::' +
- classNameToString(node);
+ TreeNode? parent = node.parent;
+ if (parent is Library && includeLibraryName) {
+ return libraryNameToString(parent) + '::' + classNameToString(node);
} else {
return classNameToString(node);
}
@@ -90,10 +89,9 @@
String qualifiedExtensionNameToString(Extension node,
{bool includeLibraryName: false}) {
- if (includeLibraryName) {
- return libraryNameToString(node.enclosingLibrary) +
- '::' +
- extensionNameToString(node);
+ TreeNode? parent = node.parent;
+ if (parent is Library && includeLibraryName) {
+ return libraryNameToString(parent) + '::' + extensionNameToString(node);
} else {
return extensionNameToString(node);
}
@@ -127,10 +125,9 @@
String qualifiedTypedefNameToString(Typedef node,
{bool includeLibraryName: false}) {
- if (includeLibraryName) {
- return libraryNameToString(node.enclosingLibrary) +
- '::' +
- typedefNameToString(node);
+ TreeNode? parent = node.parent;
+ if (parent is Library && includeLibraryName) {
+ return libraryNameToString(parent) + '::' + typedefNameToString(node);
} else {
return typedefNameToString(node);
}
@@ -164,15 +161,14 @@
String qualifiedMemberNameToString(Member node,
{bool includeLibraryName: false}) {
- if (node.enclosingClass != null) {
- return qualifiedClassNameToString(node.enclosingClass!,
+ TreeNode? parent = node.parent;
+ if (parent is Class) {
+ return qualifiedClassNameToString(parent,
includeLibraryName: includeLibraryName) +
'.' +
memberNameToString(node);
- } else if (includeLibraryName) {
- return libraryNameToString(node.enclosingLibrary) +
- '::' +
- memberNameToString(node);
+ } else if (parent is Library && includeLibraryName) {
+ return libraryNameToString(parent) + '::' + memberNameToString(node);
} else {
return memberNameToString(node);
}
diff --git a/pkg/kernel/test/clone_test.dart b/pkg/kernel/test/clone_test.dart
new file mode 100644
index 0000000..194e4b5
--- /dev/null
+++ b/pkg/kernel/test/clone_test.dart
@@ -0,0 +1,170 @@
+// 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:expect/expect.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/clone.dart';
+import 'package:kernel/src/coverage.dart';
+import 'package:kernel/src/equivalence.dart';
+import 'package:kernel/src/node_creator.dart';
+
+main() {
+ testBodyCloning();
+ testMemberCloning();
+}
+
+void testBodyCloning() {
+ // TODO(johnniwinther): Add a test for cloning in context.
+ NodeCreator creator =
+ new NodeCreator(initializers: [], members: [], nodes: inBodyNodeKinds);
+ List<TreeNode> nodes = creator.generateBodies();
+
+ CoverageVisitor coverageVisitor = new CoverageVisitor();
+ for (TreeNode node in nodes) {
+ node.accept(coverageVisitor);
+ CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
+ TreeNode clone = cloner.clone(node);
+ EquivalenceResult result = checkEquivalence(node, clone);
+ if (!result.isEquivalent) {
+ print(result);
+ }
+ Expect.isTrue(result.isEquivalent, "$node");
+ }
+ Expect.isEmpty(
+ creator.createdKinds.toSet()..removeAll(coverageVisitor.visited),
+ 'Nodes not covered in testing.');
+}
+
+void testMemberCloning() {
+ NodeCreator creator = new NodeCreator(nodes: inBodyNodeKinds);
+ Component component = creator.generateComponent();
+
+ CoverageVisitor coverageVisitor = new CoverageVisitor();
+
+ void testMembers<M extends Member>(
+ Iterable<M> members,
+ M Function(CloneVisitorWithMembers, M) cloneFunction,
+ String Function(M) toStringFunction) {
+ for (M member in members) {
+ member.accept(coverageVisitor);
+ CloneVisitorWithMembers cloner = new CloneVisitorWithMembers();
+ M clone = cloneFunction(cloner, member);
+ EquivalenceResult result = checkEquivalence(member, clone,
+ strategy: const MemberEquivalenceStrategy());
+ if (!result.isEquivalent) {
+ print(result);
+ }
+ Expect.isTrue(result.isEquivalent, toStringFunction(member));
+ }
+ }
+
+ void testProcedures(Iterable<Procedure> procedures) {
+ testMembers<Procedure>(
+ procedures,
+ (cloner, procedure) => cloner.cloneProcedure(procedure, null),
+ (procedure) => "${procedure.runtimeType}(${procedure.name}):"
+ "${procedure.function.body}");
+ }
+
+ void testFields(Iterable<Field> fields) {
+ testMembers<Field>(
+ fields,
+ (cloner, field) => cloner.cloneField(field, null, null),
+ (field) => "${field.runtimeType}(${field.name}):"
+ "${field.initializer}");
+ }
+
+ void testConstructors(Iterable<Constructor> constructors) {
+ testMembers<Constructor>(
+ constructors,
+ (cloner, constructor) => cloner.cloneConstructor(constructor, null),
+ (constructor) => "${constructor.runtimeType}(${constructor.name}):"
+ "${constructor.initializers}:"
+ "${constructor.function.body}");
+ }
+
+ void testRedirectingFactories(
+ Iterable<RedirectingFactory> redirectingFactory) {
+ testMembers<RedirectingFactory>(
+ redirectingFactory,
+ (cloner, redirectingFactory) =>
+ cloner.cloneRedirectingFactory(redirectingFactory, null),
+ (redirectingFactory) =>
+ "${redirectingFactory.runtimeType}(${redirectingFactory.name}):"
+ "${redirectingFactory.function.body}");
+ }
+
+ for (Library library in component.libraries) {
+ testProcedures(library.procedures);
+ testFields(library.fields);
+ for (Class cls in library.classes) {
+ testProcedures(cls.procedures);
+ testFields(cls.fields);
+ testConstructors(cls.constructors);
+ testRedirectingFactories(cls.redirectingFactories);
+ }
+ }
+ Expect.isEmpty(
+ creator.createdKinds.toSet()..removeAll(coverageVisitor.visited),
+ 'Nodes not covered in testing.');
+}
+
+class MemberEquivalenceStrategy extends EquivalenceStrategy {
+ const MemberEquivalenceStrategy();
+
+ void assumeClonedReferences(EquivalenceVisitor visitor, Member member1,
+ Reference? reference1, Member member2, Reference? reference2) {
+ if (reference1 != null && reference2 != null) {
+ ReferenceName referenceName1 = ReferenceName.fromNamedNode(member1);
+ ReferenceName referenceName2 = ReferenceName.fromNamedNode(member2);
+ if (referenceName1.memberName == referenceName2.memberName &&
+ referenceName1.memberUri == referenceName2.memberUri &&
+ referenceName2.declarationName == null ||
+ referenceName2.libraryUri == null) {
+ visitor.assumeReferences(reference1, reference2);
+ }
+ }
+ }
+
+ @override
+ bool checkProcedure(
+ EquivalenceVisitor visitor, Procedure? node, Object? other) {
+ if (node is Procedure && other is Procedure) {
+ assumeClonedReferences(
+ visitor, node, node.reference, other, other.reference);
+ }
+ return super.checkProcedure(visitor, node, other);
+ }
+
+ @override
+ bool checkConstructor(
+ EquivalenceVisitor visitor, Constructor? node, Object? other) {
+ if (node is Constructor && other is Constructor) {
+ assumeClonedReferences(
+ visitor, node, node.reference, other, other.reference);
+ }
+ return super.checkConstructor(visitor, node, other);
+ }
+
+ @override
+ bool checkRedirectingFactory(
+ EquivalenceVisitor visitor, RedirectingFactory? node, Object? other) {
+ if (node is RedirectingFactory && other is RedirectingFactory) {
+ assumeClonedReferences(
+ visitor, node, node.reference, other, other.reference);
+ }
+ return super.checkRedirectingFactory(visitor, node, other);
+ }
+
+ @override
+ bool checkField(EquivalenceVisitor visitor, Field? node, Object? other) {
+ if (node is Field && other is Field) {
+ assumeClonedReferences(
+ visitor, node, node.getterReference, other, other.getterReference);
+ assumeClonedReferences(
+ visitor, node, node.setterReference, other, other.setterReference);
+ }
+ return super.checkField(visitor, node, other);
+ }
+}
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 8b2adff9..9037441 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -727,7 +727,7 @@
return result.ptr();
}
-ObjectPtr DartLibraryCalls::HandleMessage(const Object& handler,
+ObjectPtr DartLibraryCalls::HandleMessage(Dart_Port port_id,
const Instance& message) {
auto thread = Thread::Current();
auto zone = thread->zone();
@@ -743,7 +743,7 @@
args = Array::New(kNumArguments);
isolate->isolate_object_store()->set_dart_args_2(args);
}
- args.SetAt(0, handler);
+ args.SetAt(0, Integer::Handle(zone, Integer::New(port_id)));
args.SetAt(1, message);
#if !defined(PRODUCT)
if (isolate->debugger()->IsStepping()) {
@@ -753,10 +753,9 @@
isolate->debugger()->SetResumeAction(Debugger::kStepInto);
}
#endif
- const Object& result =
+ const Object& handler =
Object::Handle(zone, DartEntry::InvokeFunction(function, args));
- ASSERT(result.IsNull() || result.IsError());
- return result.ptr();
+ return handler.ptr();
}
ObjectPtr DartLibraryCalls::DrainMicrotaskQueue() {
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index c69d5c8..6ec9067 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -291,12 +291,13 @@
// Returns the handler if one has been registered for this port id.
static ObjectPtr LookupHandler(Dart_Port port_id);
+ // Returns handler on success, an ErrorPtr on failure, null if can't find
+ // handler for this port id.
+ static ObjectPtr HandleMessage(Dart_Port port_id, const Instance& message);
+
// Returns a list of open ReceivePorts.
static ObjectPtr LookupOpenPorts();
- // Returns null on success, an ErrorPtr on failure.
- static ObjectPtr HandleMessage(const Object& handler,
- const Instance& dart_message);
// Returns null on success, an ErrorPtr on failure.
static ObjectPtr DrainMicrotaskQueue();
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index b1967f0..31b26f8 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1307,26 +1307,6 @@
tbes.CopyArgument(0, "isolateName", I->name());
#endif
- // If the message is in band we lookup the handler to dispatch to. If the
- // receive port was closed, we drop the message without deserializing it.
- // Illegal port is a special case for artificially enqueued isolate library
- // messages which are handled in C++ code below.
- Object& msg_handler = Object::Handle(zone);
- if (!message->IsOOB() && (message->dest_port() != Message::kIllegalPort)) {
- msg_handler = DartLibraryCalls::LookupHandler(message->dest_port());
- if (msg_handler.IsError()) {
- return ProcessUnhandledException(Error::Cast(msg_handler));
- }
- if (msg_handler.IsNull()) {
- // If the port has been closed then the message will be dropped at this
- // point. Make sure to post to the delivery failure port in that case.
- if (message->RedirectToDeliveryFailurePort()) {
- PortMap::PostMessage(std::move(message));
- }
- return kOK;
- }
- }
-
// Parse the message.
Object& msg_obj = Object::Handle(zone, ReadMessage(thread, message.get()));
if (msg_obj.IsError()) {
@@ -1414,12 +1394,18 @@
tbes.CopyArgument(1, "mode", "basic");
}
#endif
- const Object& result =
- Object::Handle(zone, DartLibraryCalls::HandleMessage(msg_handler, msg));
- if (result.IsError()) {
- status = ProcessUnhandledException(Error::Cast(result));
+ const Object& msg_handler = Object::Handle(
+ zone, DartLibraryCalls::HandleMessage(message->dest_port(), msg));
+ if (msg_handler.IsError()) {
+ status = ProcessUnhandledException(Error::Cast(msg_handler));
+ } else if (msg_handler.IsNull()) {
+ // If the port has been closed then the message will be dropped at this
+ // point. Make sure to post to the delivery failure port in that case.
+ if (message->RedirectToDeliveryFailurePort()) {
+ PortMap::PostMessage(std::move(message));
+ }
} else {
- ASSERT(result.IsNull());
+ // The handler closure which was used to successfully handle the message.
}
}
return status;
diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart
index 9703179..6d70c2f 100644
--- a/sdk/lib/_internal/vm/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart
@@ -175,14 +175,19 @@
return _portMap.values.map((e) => e['port']).toList();
}
- // Called from the VM to dispatch to the handler.
+ // Called from the VM to retrieve the handler and handle a message.
@pragma("vm:entry-point", "call")
- static void _handleMessage(Function handler, var message) {
+ static _handleMessage(int id, var message) {
+ final handler = _portMap[id]?['handler'];
+ if (handler == null) {
+ return null;
+ }
// TODO(floitsch): this relies on the fact that any exception aborts the
// VM. Once we have non-fatal global exceptions we need to catch errors
// so that we can run the immediate callbacks.
handler(message);
_runPendingImmediateCallback();
+ return handler;
}
// Call into the VM to close the VM maintained mappings.
diff --git a/tools/VERSION b/tools/VERSION
index 2034e26..79b34c1 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 27
+PRERELEASE 28
PRERELEASE_PATCH 0
\ No newline at end of file