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