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