| // 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)); | |
| class2.addRedirectingFactory(new RedirectingFactory(null, | |
| name: new Name('_boz', library1), | |
| function: new FunctionNode(null), | |
| 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 (RedirectingFactory redirectingFactory in cls.redirectingFactories) { | |
| add( | |
| ReferenceNameKind.Function, | |
| new ReferenceNameObject( | |
| ReferenceName.fromNamedNode(redirectingFactory), | |
| redirectingFactory)); | |
| } | |
| } | |
| 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); | |
| } |