blob: 4a85c2f5d8a8cc70937285f608eb6e4de8d8fd20 [file] [edit]
// 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/src/equivalence.dart';
void testReferenceNames(
Map<ReferenceNameKind, List<ReferenceNameObject>> map1,
Map<ReferenceNameKind, List<ReferenceNameObject>> map2,
) {
Expect.setEquals(map1.keys, map2.keys);
map1.forEach((ReferenceNameKind kind1, List<ReferenceNameObject> list1) {
map1.forEach((ReferenceNameKind kind2, List<ReferenceNameObject> list2) {
for (int index1 = 0; index1 < list1.length; index1++) {
for (int index2 = 0; index2 < list2.length; index2++) {
ReferenceName name1 = list1[index1].referenceName;
Object object1 = list1[index1].object;
ReferenceName name2 = list2[index2].referenceName;
Object object2 = list2[index2].object;
if (kind1 == kind2 && index1 == index2) {
Expect.equals(
name1,
name2,
"Expected $name1 for ${object1} (${object1.runtimeType}) and "
"$name2 for $object2 (${object2.runtimeType}) to be equal.",
);
} else {
Expect.notEquals(
name1,
name2,
"Expected $name1 for ${object1} (${object1.runtimeType}) and "
"$name2 for $object2 (${object2.runtimeType}) to be unequal.",
);
}
}
}
});
});
}
void main() {
Component component1 = createComponent();
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames1 =
computeReferenceNamesFromComponent(component1);
Component component2 = createComponent();
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames2 =
computeReferenceNamesFromComponent(component2);
Component component3 = createComponent();
component3.computeCanonicalNames();
CanonicalName root3 = component3.root;
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames3 =
computeReferenceNamesFromCanonicalName(root3);
Component component4 = createComponent();
component4.computeCanonicalNames();
CanonicalName root4 = component3.root;
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames4 =
computeReferenceNamesFromCanonicalName(root4);
testReferenceNames(referenceNames1, referenceNames2);
testReferenceNames(referenceNames1, referenceNames3);
testReferenceNames(referenceNames3, referenceNames4);
}
Component createComponent() {
Component component = new Component();
Library library1 = new Library(Uri.parse('test:library1'), fileUri: dummyUri);
component.libraries.add(library1);
Library library2 = new Library(Uri.parse('test:library2'), fileUri: dummyUri);
component.libraries.add(library2);
library1.addProcedure(
new Procedure(
new Name('foo'),
ProcedureKind.Method,
new FunctionNode(null),
fileUri: dummyUri,
),
);
library1.addProcedure(
new Procedure(
new Name('bar'),
ProcedureKind.Operator,
new FunctionNode(null),
fileUri: dummyUri,
),
);
library1.addProcedure(
new Procedure(
new Name('baz'),
ProcedureKind.Factory,
new FunctionNode(null),
fileUri: dummyUri,
),
);
library1.addProcedure(
new Procedure(
new Name('boz'),
ProcedureKind.Getter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
// The setter should be distinct from the getter even when they have the same
// name.
library1.addProcedure(
new Procedure(
new Name('boz'),
ProcedureKind.Setter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
library1.addProcedure(
new Procedure(
new Name('_boz', library2),
ProcedureKind.Getter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
// The setter should be distinct from the getter even when they have the same
// name.
library1.addProcedure(
new Procedure(
new Name('_boz', library2),
ProcedureKind.Setter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
library1.addField(
new Field.immutable(new Name('_foo', library1), fileUri: dummyUri),
);
library1.addField(
new Field.mutable(new Name('_bar', library2), fileUri: dummyUri),
);
Class class1 = new Class(name: 'Foo', fileUri: dummyUri);
library2.addClass(class1);
Class class2 = new Class(name: 'Bar', fileUri: dummyUri);
library2.addClass(class2);
class2.addConstructor(
new Constructor(
new FunctionNode(null),
name: new Name(''),
fileUri: dummyUri,
),
);
class2.addConstructor(
new Constructor(
new FunctionNode(null),
name: new Name('_', library1),
fileUri: dummyUri,
),
);
class2.addProcedure(
new Procedure(
new Name('foo'),
ProcedureKind.Method,
new FunctionNode(null),
fileUri: dummyUri,
),
);
class2.addProcedure(
new Procedure(
new Name('bar'),
ProcedureKind.Operator,
new FunctionNode(null),
fileUri: dummyUri,
),
);
class2.addProcedure(
new Procedure(
new Name('baz'),
ProcedureKind.Factory,
new FunctionNode(null),
fileUri: dummyUri,
),
);
class2.addProcedure(
new Procedure(
new Name('boz'),
ProcedureKind.Getter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
// The setter should be distinct from the getter even when they have the same
// name.
class2.addProcedure(
new Procedure(
new Name('boz'),
ProcedureKind.Setter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
class2.addProcedure(
new Procedure(
new Name('_boz', library2),
ProcedureKind.Getter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
// The setter should be distinct from the getter even when they have the same
// name.
class2.addProcedure(
new Procedure(
new Name('_boz', library2),
ProcedureKind.Setter,
new FunctionNode(null),
fileUri: dummyUri,
),
);
class2.addField(
new Field.immutable(new Name('_foo', library1), fileUri: dummyUri),
);
class2.addField(
new Field.mutable(new Name('_bar', library2), fileUri: dummyUri),
);
library1.addExtension(new Extension(name: 'Baz', fileUri: dummyUri));
library1.addTypedef(new Typedef('Boz', dummyDartType, fileUri: dummyUri));
return component;
}
void sortReferenceNames(Map<ReferenceNameKind, List<ReferenceNameObject>> map) {
map.forEach((key, value) {
value.sort(
(n1, n2) => n1.referenceName.name!.compareTo(n2.referenceName.name!),
);
});
}
Map<ReferenceNameKind, List<ReferenceNameObject>>
computeReferenceNamesFromComponent(Component component) {
Map<ReferenceNameKind, List<ReferenceNameObject>> map = {};
void add(ReferenceNameKind kind, ReferenceNameObject object) {
(map[kind] ??= []).add(object);
}
for (Library library in component.libraries) {
add(
ReferenceNameKind.Library,
new ReferenceNameObject(ReferenceName.fromNamedNode(library), library),
);
for (Typedef typedef in library.typedefs) {
add(
ReferenceNameKind.Typedef,
new ReferenceNameObject(ReferenceName.fromNamedNode(typedef), typedef),
);
}
for (Field field in library.fields) {
add(
ReferenceNameKind.Field,
new ReferenceNameObject(
ReferenceName.fromNamedNode(field, ReferenceNameKind.Field),
field,
),
);
add(
ReferenceNameKind.Getter,
new ReferenceNameObject(
ReferenceName.fromNamedNode(field, ReferenceNameKind.Getter),
field,
),
);
if (field.hasSetter) {
add(
ReferenceNameKind.Setter,
new ReferenceNameObject(
ReferenceName.fromNamedNode(field, ReferenceNameKind.Setter),
field,
),
);
}
}
for (Procedure procedure in library.procedures) {
ReferenceNameKind kind;
if (procedure.isGetter) {
kind = ReferenceNameKind.Getter;
} else if (procedure.isSetter) {
kind = ReferenceNameKind.Setter;
} else {
kind = ReferenceNameKind.Function;
}
add(
kind,
new ReferenceNameObject(
ReferenceName.fromNamedNode(procedure),
procedure,
),
);
}
for (Class cls in library.classes) {
add(
ReferenceNameKind.Declaration,
new ReferenceNameObject(ReferenceName.fromNamedNode(cls), cls),
);
for (Constructor constructor in cls.constructors) {
add(
ReferenceNameKind.Function,
new ReferenceNameObject(
ReferenceName.fromNamedNode(constructor),
constructor,
),
);
}
for (Procedure procedure in cls.procedures) {
ReferenceNameKind kind;
if (procedure.isGetter) {
kind = ReferenceNameKind.Getter;
} else if (procedure.isSetter) {
kind = ReferenceNameKind.Setter;
} else {
kind = ReferenceNameKind.Function;
}
add(
kind,
new ReferenceNameObject(
ReferenceName.fromNamedNode(procedure),
procedure,
),
);
}
for (Field field in cls.fields) {
add(
ReferenceNameKind.Field,
new ReferenceNameObject(ReferenceName.fromNamedNode(field), field),
);
add(
ReferenceNameKind.Getter,
new ReferenceNameObject(
ReferenceName.fromNamedNode(field, ReferenceNameKind.Getter),
field,
),
);
if (field.hasSetter) {
add(
ReferenceNameKind.Setter,
new ReferenceNameObject(
ReferenceName.fromNamedNode(field, ReferenceNameKind.Setter),
field,
),
);
}
}
}
for (Extension extension in library.extensions) {
add(
ReferenceNameKind.Declaration,
new ReferenceNameObject(
ReferenceName.fromNamedNode(extension),
extension,
),
);
}
}
sortReferenceNames(map);
return map;
}
Map<ReferenceNameKind, List<ReferenceNameObject>>
computeReferenceNamesFromCanonicalName(CanonicalName root) {
Map<ReferenceNameKind, List<ReferenceNameObject>> map = {};
void visit(CanonicalName canonicalName, ReferenceNameKind kind) {
void addObject() {
(map[kind] ??= []).add(
new ReferenceNameObject(
ReferenceName.fromCanonicalName(canonicalName),
canonicalName,
),
);
}
switch (kind) {
case ReferenceNameKind.Unknown:
for (CanonicalName child in canonicalName.children) {
visit(child, ReferenceNameKind.Library);
}
break;
case ReferenceNameKind.Library:
addObject();
for (CanonicalName child in canonicalName.children) {
ReferenceNameKind childKind = ReferenceNameKind.Declaration;
if (CanonicalName.isSymbolicName(child.name)) {
childKind = ReferenceName.kindFromSymbolicName(child.name);
}
visit(child, childKind);
}
break;
case ReferenceNameKind.Declaration:
addObject();
for (CanonicalName child in canonicalName.children) {
visit(child, ReferenceName.kindFromSymbolicName(child.name));
}
break;
case ReferenceNameKind.Typedef:
case ReferenceNameKind.Function:
case ReferenceNameKind.Field:
case ReferenceNameKind.Getter:
case ReferenceNameKind.Setter:
if (canonicalName.childrenOrNull != null) {
// Private name
for (CanonicalName child in canonicalName.children) {
visit(child, kind);
}
} else {
addObject();
}
break;
}
}
visit(root, ReferenceNameKind.Unknown);
sortReferenceNames(map);
return map;
}
class ReferenceNameObject {
final ReferenceName referenceName;
final Object object;
ReferenceNameObject(this.referenceName, this.object);
}