| // Copyright (c) 2025, 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 'dart:io'; |
| |
| import 'package:_fe_analyzer_shared/src/util/options.dart'; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/src/equivalence.dart'; |
| import 'package:kernel/src/tool/command_line_util.dart'; |
| |
| void main(List<String> args) { |
| ParsedOptions parsedOptions = ParsedOptions.parse(args, optionSpecification); |
| |
| CommandLineHelper.requireVariableArgumentCount([2], parsedOptions.arguments, |
| () { |
| print(''' |
| Usage: |
| |
| dart check_equivalence.dart [options] <dill1> <dill2> |
| '''); |
| }); |
| if (parsedOptions.arguments.length != 2) { |
| exit(1); |
| } |
| |
| bool unordered = Options.unordered.read(parsedOptions); |
| bool unorderedLibraries = |
| unordered || Options.unorderedLibraries.read(parsedOptions); |
| bool unorderedLibraryDependencies = |
| unordered || Options.unorderedLibraries.read(parsedOptions); |
| bool unorderedAdditionalExports = |
| unordered || Options.unorderedLibraries.read(parsedOptions); |
| bool unorderedParts = |
| unordered || Options.unorderedLibraries.read(parsedOptions); |
| bool unorderedTypedefs = |
| unordered || Options.unorderedLibraries.read(parsedOptions); |
| bool unorderedClasses = |
| unordered || Options.unorderedLibraries.read(parsedOptions); |
| bool unorderedMembers = |
| unordered || Options.unorderedMembers.read(parsedOptions); |
| bool unorderedFields = |
| unorderedMembers || Options.unorderedFields.read(parsedOptions); |
| bool unorderedProcedures = |
| unorderedMembers || Options.unorderedProcedures.read(parsedOptions); |
| bool unorderedConstructors = |
| unorderedMembers || Options.unorderedConstructors.read(parsedOptions); |
| bool unorderedAnnotations = |
| unordered || Options.unorderedAnnotations.read(parsedOptions); |
| |
| Component dill1 = CommandLineHelper.tryLoadDill(parsedOptions.arguments[0]); |
| Component dill2 = CommandLineHelper.tryLoadDill(parsedOptions.arguments[1]); |
| EquivalenceResult result = checkEquivalence(dill1, dill2, |
| strategy: new Strategy( |
| unorderedLibraries: unorderedLibraries, |
| unorderedLibraryDependencies: unorderedLibraryDependencies, |
| unorderedAdditionalExports: unorderedAdditionalExports, |
| unorderedParts: unorderedParts, |
| unorderedTypedefs: unorderedTypedefs, |
| unorderedClasses: unorderedClasses, |
| unorderedFields: unorderedFields, |
| unorderedProcedures: unorderedProcedures, |
| unorderedConstructors: unorderedConstructors, |
| unorderedAnnotations: unorderedAnnotations)); |
| if (result.isEquivalent) { |
| print('The dills are equivalent.'); |
| } else { |
| print('Inequivalence found:'); |
| print(result); |
| } |
| } |
| |
| EquivalenceResult checkNodeEquivalence( |
| Node node1, |
| Node node2, { |
| bool unorderedLibraries = false, |
| bool unorderedLibraryDependencies = false, |
| bool unorderedAdditionalExports = false, |
| bool unorderedParts = false, |
| bool unorderedTypedefs = false, |
| bool unorderedClasses = false, |
| bool unorderedFields = false, |
| bool unorderedProcedures = false, |
| bool unorderedConstructors = false, |
| bool unorderedAnnotations = false, |
| }) { |
| return checkEquivalence(node1, node2, |
| strategy: new Strategy( |
| unorderedLibraries: unorderedLibraries, |
| unorderedLibraryDependencies: unorderedLibraryDependencies, |
| unorderedAdditionalExports: unorderedAdditionalExports, |
| unorderedParts: unorderedParts, |
| unorderedTypedefs: unorderedTypedefs, |
| unorderedClasses: unorderedClasses, |
| unorderedFields: unorderedFields, |
| unorderedProcedures: unorderedProcedures, |
| unorderedConstructors: unorderedConstructors, |
| unorderedAnnotations: unorderedAnnotations)); |
| } |
| |
| class Strategy extends EquivalenceStrategy { |
| final bool unorderedLibraries; |
| final bool unorderedLibraryDependencies; |
| final bool unorderedAdditionalExports; |
| final bool unorderedParts; |
| final bool unorderedTypedefs; |
| final bool unorderedClasses; |
| final bool unorderedFields; |
| final bool unorderedProcedures; |
| final bool unorderedConstructors; |
| final bool unorderedAnnotations; |
| |
| Strategy( |
| {required this.unorderedLibraries, |
| required this.unorderedLibraryDependencies, |
| required this.unorderedAdditionalExports, |
| required this.unorderedParts, |
| required this.unorderedTypedefs, |
| required this.unorderedClasses, |
| required this.unorderedFields, |
| required this.unorderedProcedures, |
| required this.unorderedConstructors, |
| required this.unorderedAnnotations}); |
| |
| @override |
| bool checkComponent_libraries( |
| EquivalenceVisitor visitor, Component node, Component other) { |
| if (unorderedLibraries) { |
| return visitor.checkSets(node.libraries.toSet(), other.libraries.toSet(), |
| visitor.matchNamedNodes, visitor.checkNodes, 'libraries'); |
| } else { |
| return visitor.checkLists( |
| node.libraries, other.libraries, visitor.checkNodes, 'libraries'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_dependencies( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedLibraryDependencies) { |
| return visitor |
| .checkSets(node.dependencies.toSet(), other.dependencies.toSet(), |
| (LibraryDependency dependency1, LibraryDependency dependency2) { |
| return visitor.matchReferences(dependency1.importedLibraryReference, |
| dependency2.importedLibraryReference) && |
| dependency1.flags == dependency2.flags && |
| dependency1.name == dependency2.name && |
| dependency1.fileOffset == dependency2.fileOffset; |
| }, visitor.checkNodes, 'dependencies'); |
| } else { |
| return visitor.checkLists(node.dependencies, other.dependencies, |
| visitor.checkNodes, 'dependencies'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_parts( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedParts) { |
| return visitor.checkSets(node.parts.toSet(), other.parts.toSet(), |
| (LibraryPart part1, LibraryPart part2) { |
| return part1.partUri == part2.partUri; |
| }, visitor.checkNodes, 'parts'); |
| } else { |
| return visitor.checkLists( |
| node.parts, other.parts, visitor.checkNodes, 'parts'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_typedefs( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedTypedefs) { |
| return visitor.checkSets(node.typedefs.toSet(), other.typedefs.toSet(), |
| visitor.matchNamedNodes, visitor.checkNodes, 'typedefs'); |
| } else { |
| return visitor.checkLists( |
| node.typedefs, other.typedefs, visitor.checkNodes, 'typedefs'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_additionalExports( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedAdditionalExports) { |
| return visitor.checkSets( |
| node.additionalExports.toSet(), |
| other.additionalExports.toSet(), |
| visitor.matchReferences, |
| visitor.checkReferences, |
| 'additionalExports'); |
| } else { |
| return visitor.checkLists(node.additionalExports, other.additionalExports, |
| visitor.checkReferences, 'additionalExports'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_classes( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedClasses) { |
| return visitor.checkSets(node.classes.toSet(), other.classes.toSet(), |
| visitor.matchNamedNodes, visitor.checkNodes, 'classes'); |
| } else { |
| return visitor.checkLists( |
| node.classes, other.classes, visitor.checkNodes, 'classes'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_fields( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedFields) { |
| return visitor.checkSets(node.fields.toSet(), other.fields.toSet(), |
| visitor.matchNamedNodes, visitor.checkNodes, 'fields'); |
| } else { |
| return visitor.checkLists( |
| node.fields, other.fields, visitor.checkNodes, 'fields'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_procedures( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedProcedures) { |
| return visitor.checkSets( |
| node.procedures.toSet(), |
| other.procedures.toSet(), |
| visitor.matchNamedNodes, |
| visitor.checkNodes, |
| 'procedures'); |
| } else { |
| return visitor.checkLists( |
| node.procedures, other.procedures, visitor.checkNodes, 'procedures'); |
| } |
| } |
| |
| @override |
| bool checkLibrary_annotations( |
| EquivalenceVisitor visitor, Library node, Library other) { |
| if (unorderedAnnotations) { |
| return visitor.checkSets( |
| node.annotations.toSet(), |
| other.annotations.toSet(), |
| _matchAnnotations, |
| visitor.checkNodes, |
| 'annotations'); |
| } else { |
| return visitor.checkLists(node.annotations, other.annotations, |
| visitor.checkNodes, 'annotations'); |
| } |
| } |
| |
| @override |
| bool checkClass_fields(EquivalenceVisitor visitor, Class node, Class other) { |
| if (unorderedFields) { |
| return visitor.checkSets(node.fields.toSet(), other.fields.toSet(), |
| visitor.matchNamedNodes, visitor.checkNodes, 'fields'); |
| } else { |
| return visitor.checkLists( |
| node.fields, other.fields, visitor.checkNodes, 'fields'); |
| } |
| } |
| |
| @override |
| bool checkClass_procedures( |
| EquivalenceVisitor visitor, Class node, Class other) { |
| if (unorderedProcedures) { |
| return visitor.checkSets( |
| node.procedures.toSet(), |
| other.procedures.toSet(), |
| visitor.matchNamedNodes, |
| visitor.checkNodes, |
| 'procedures'); |
| } else { |
| return visitor.checkLists( |
| node.procedures, other.procedures, visitor.checkNodes, 'procedures'); |
| } |
| } |
| |
| @override |
| bool checkClass_constructors( |
| EquivalenceVisitor visitor, Class node, Class other) { |
| if (unorderedConstructors) { |
| return visitor.checkSets( |
| node.constructors.toSet(), |
| other.constructors.toSet(), |
| visitor.matchNamedNodes, |
| visitor.checkNodes, |
| 'constructors'); |
| } else { |
| return visitor.checkLists(node.constructors, other.constructors, |
| visitor.checkNodes, 'constructors'); |
| } |
| } |
| |
| @override |
| bool checkClass_annotations( |
| EquivalenceVisitor visitor, Class node, Class other) { |
| if (unorderedAnnotations) { |
| return visitor.checkSets( |
| node.annotations.toSet(), |
| other.annotations.toSet(), |
| _matchAnnotations, |
| visitor.checkNodes, |
| 'annotations'); |
| } else { |
| return visitor.checkLists(node.annotations, other.annotations, |
| visitor.checkNodes, 'annotations'); |
| } |
| } |
| |
| @override |
| bool checkExtension_annotations( |
| EquivalenceVisitor visitor, Extension node, Extension other) { |
| if (unorderedAnnotations) { |
| return visitor.checkSets( |
| node.annotations.toSet(), |
| other.annotations.toSet(), |
| _matchAnnotations, |
| visitor.checkNodes, |
| 'annotations'); |
| } else { |
| return visitor.checkLists(node.annotations, other.annotations, |
| visitor.checkNodes, 'annotations'); |
| } |
| } |
| |
| @override |
| bool checkMember_annotations( |
| EquivalenceVisitor visitor, Member node, Member other) { |
| if (unorderedAnnotations) { |
| return visitor.checkSets( |
| node.annotations.toSet(), |
| other.annotations.toSet(), |
| _matchAnnotations, |
| visitor.checkNodes, |
| 'annotations'); |
| } else { |
| return visitor.checkLists(node.annotations, other.annotations, |
| visitor.checkNodes, 'annotations'); |
| } |
| } |
| |
| bool _matchAnnotations(Expression expression1, Expression expression2) { |
| return expression1.runtimeType == expression2.runtimeType && |
| expression1.fileOffset == expression2.fileOffset; |
| } |
| } |
| |
| class Flags { |
| static const String unordered = '--unordered'; |
| static const String unorderedLibraries = '--unordered-libraries'; |
| static const String unorderedLibraryDependencies = |
| '--unordered-library-dependencies'; |
| static const String unorderedParts = '--unordered-parts'; |
| static const String unorderedAdditionalExports = |
| '--unordered-additional-exports'; |
| static const String unorderedTypedefs = '--unordered-typedefs'; |
| static const String unorderedClasses = '--unordered-classes'; |
| static const String unorderedMembers = '--unordered-members'; |
| static const String unorderedFields = '--unordered-fields'; |
| static const String unorderedProcedures = '--unordered-procedures'; |
| static const String unorderedConstructors = '--unordered-constructors'; |
| static const String unorderedAnnotations = '--unordered-annotations'; |
| } |
| |
| class Options { |
| static const Option<bool> unordered = |
| const Option(Flags.unordered, const BoolValue(false)); |
| static const Option<bool> unorderedLibraries = |
| const Option(Flags.unorderedLibraries, const BoolValue(false)); |
| static const Option<bool> unorderedLibraryDependencies = |
| const Option(Flags.unorderedLibraryDependencies, const BoolValue(false)); |
| static const Option<bool> unorderedParts = |
| const Option(Flags.unorderedParts, const BoolValue(false)); |
| static const Option<bool> unorderedAdditionalExports = |
| const Option(Flags.unorderedAdditionalExports, const BoolValue(false)); |
| static const Option<bool> unorderedTypedefs = |
| const Option(Flags.unorderedTypedefs, const BoolValue(false)); |
| static const Option<bool> unorderedClasses = |
| const Option(Flags.unorderedClasses, const BoolValue(false)); |
| static const Option<bool> unorderedMembers = |
| const Option(Flags.unorderedMembers, const BoolValue(false)); |
| static const Option<bool> unorderedFields = |
| const Option(Flags.unorderedFields, const BoolValue(false)); |
| static const Option<bool> unorderedProcedures = |
| const Option(Flags.unorderedProcedures, const BoolValue(false)); |
| static const Option<bool> unorderedConstructors = |
| const Option(Flags.unorderedConstructors, const BoolValue(false)); |
| static const Option<bool> unorderedAnnotations = |
| const Option(Flags.unorderedAnnotations, const BoolValue(false)); |
| } |
| |
| const List<Option> optionSpecification = [ |
| Options.unordered, |
| Options.unorderedLibraries, |
| Options.unorderedLibraryDependencies, |
| Options.unorderedParts, |
| Options.unorderedAdditionalExports, |
| Options.unorderedTypedefs, |
| Options.unorderedClasses, |
| Options.unorderedMembers, |
| Options.unorderedFields, |
| Options.unorderedProcedures, |
| Options.unorderedConstructors, |
| Options.unorderedAnnotations, |
| ]; |