blob: de1ded1792f15913c41cb143c83f98816996e6a6 [file]
// 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;
new({
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,
];