| // 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; | |
| new(this.referenceName, this.object); | |
| } |