[cfe] Add NodeCreator and clone test

Change-Id: I3b0f9c716a25eed05e83dbd653f50a478242a7a5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210240
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2021-08-18 15:21:03 +00:00 committed by commit-bot@chromium.org
parent 02599e58ae
commit c13746df2b
14 changed files with 3000 additions and 90 deletions

View file

@ -13,7 +13,9 @@ import "../tool/_fasta/direct_parser_ast_helper_creator.dart"
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 @@ main() async {
directParserAstHelper();
parserTestListener();
parserTestParser();
await astEquivalence();
AstModel astModel = await deriveAstModel(repoDir);
await astEquivalence(astModel);
await astCoverage(astModel);
}
void parserTestParser() {
@ -50,14 +54,22 @@ void directParserAstHelper() {
"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 =

View file

@ -262,6 +262,7 @@ covariances
coverage
cr
creator
creators
criterion
cross
cruft
@ -457,6 +458,7 @@ finv
firsts
fishy
fishythefish
fits
fixnum
fleshed
float32

View file

@ -548,6 +548,7 @@ instrument
insufficient
intdiv
interactive
interchangeable
interested
internet
interpolate

View file

@ -55,6 +55,20 @@ const Set<String> _classesWithoutVisitMethods = const {
'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 @@ const Map<String /*?*/, Map<String, FieldRule /*?*/ >> _fieldRuleMap = {
'_proceduresView': null,
'_proceduresInternal': FieldRule(name: 'procedures'),
'_redirectingFactoriesView': null,
'_redirectingFactoriesInternal':
FieldRule(name: 'redirectingFactories'),
'_redirectingFactoriesInternal': FieldRule(name: 'redirectingFactories'),
'lazyBuilder': null,
'dirty': null,
},
@ -264,6 +277,7 @@ class AstClass {
final Class node;
AstClassKind _kind;
final String declarativeName;
final bool isInterchangeable;
AstClass superclass;
List<AstClass> interfaces = [];
@ -273,8 +287,12 @@ class AstClass {
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 @@ Future<AstModel> deriveAstModel(Uri repoDir, {bool printDump: false}) async {
_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 @@ Future<AstModel> deriveAstModel(Uri repoDir, {bool printDump: false}) async {
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 @@ Future<AstModel> deriveAstModel(Uri repoDir, {bool printDump: false}) async {
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 @@ Future<AstModel> deriveAstModel(Uri repoDir, {bool printDump: false}) async {
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 @@ Future<AstModel> deriveAstModel(Uri repoDir, {bool printDump: false}) async {
}
}
} 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;

View file

@ -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;
}''');
});
}
}

View file

@ -21,8 +21,8 @@ main(List<String> args) async {
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 @@ class EquivalenceVisitorStrategy extends Visitor1Strategy {
'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 @@ class EquivalenceVisitorStrategy extends Visitor1Strategy {
}
@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 @@ class EquivalenceVisitorStrategy extends Visitor1Strategy {
}
@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 @@ class $visitorName$visitorTypeParameters
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 @@ class $visitorName$visitorTypeParameters
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);
}

View file

@ -9,7 +9,12 @@ import 'package:dart_style/dart_style.dart' show DartFormatter;
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 @@ String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
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 @@ String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
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 @@ String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
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 @@ String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
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 @@ String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
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 @@ String generateVisitor(AstModel astModel, VisitorStrategy strategy) {
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 @@ abstract class Visitor0Strategy extends VisitorStrategy {
sb.writeln('''
import 'package:kernel/ast.dart';
$visitorComment
class $visitorName$visitorTypeParameters implements Visitor<$returnType> {''');
}
@ -128,56 +142,62 @@ class $visitorName$visitorTypeParameters implements Visitor<$returnType> {''');
}
@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 @@ abstract class Visitor1Strategy extends VisitorStrategy {
sb.writeln('''
import 'package:kernel/ast.dart';
$visitorComment
class $visitorName$visitorTypeParameters
implements Visitor1<$returnType, $argumentType> {''');
}
@ -230,60 +251,66 @@ class $visitorName$visitorTypeParameters
}
@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.

View file

@ -5328,6 +5328,7 @@ class InstanceGetterInvocation extends InstanceInvocationExpression {
interfaceTarget.acceptReference(v);
name.accept(v);
arguments.accept(v);
functionType?.accept(v);
}
@override
@ -5342,6 +5343,9 @@ class InstanceGetterInvocation extends InstanceInvocationExpression {
arguments = v.transform(arguments);
arguments.parent = this;
}
if (functionType != null) {
functionType = v.visitDartType(functionType!) as FunctionType;
}
}
@override
@ -5356,6 +5360,10 @@ class InstanceGetterInvocation extends InstanceInvocationExpression {
arguments = v.transform(arguments);
arguments.parent = this;
}
if (functionType != null) {
functionType =
v.visitDartType(functionType!, cannotRemoveSentinel) as FunctionType;
}
}
@override

File diff suppressed because it is too large Load diff

View file

@ -950,12 +950,14 @@ class EquivalenceVisitor implements Visitor1<bool, Node> {
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 @@ class EquivalenceVisitor implements Visitor1<bool, Node> {
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);
}

View file

@ -265,7 +265,7 @@ class ReferenceName {
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 @@ class ReferenceName {
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(

File diff suppressed because it is too large Load diff

View file

@ -33,10 +33,9 @@ String libraryNameToString(Library? node) {
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 classNameToString(Class? node) {
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 extensionNameToString(Extension? node) {
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 typedefNameToString(Typedef? node) {
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);
}

View file

@ -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);
}
}