Refactor serialization test files.

BUG=
R=sigmund@google.com

Review URL: https://codereview.chromium.org/1870133002 .
This commit is contained in:
Johnni Winther 2016-04-11 10:39:20 +02:00
parent 4080c1b410
commit 1f6c0dd14b
6 changed files with 584 additions and 530 deletions

View file

@ -14,192 +14,7 @@ import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/filenames.dart';
import 'memory_compiler.dart';
import 'serialization_helper.dart';
const List<Test> TESTS = const <Test>[
const Test(const {
'main.dart': 'main() {}'
}),
const Test(const {
'main.dart': 'main() => print("Hello World");'
}),
const Test(const {
'main.dart': 'main() => print("Hello World", 0);'
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
main() {
String text = "Hello World";
print('$text');
}'''
}),
const Test(const {
'main.dart': r'''
main() {
String text = "Hello World";
print('$text', text);
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
main(List<String> arguments) {
print(arguments);
}'''
}),
const Test(const {
'main.dart': r'''
main(List<String> arguments) {
for (int i = 0; i < arguments.length; i++) {
print(arguments[i]);
}
}'''
}),
const Test(const {
'main.dart': r'''
main(List<String> arguments) {
for (String argument in arguments) {
print(argument);
}
}'''
}),
const Test(const {
'main.dart': r'''
class Class {}
main() {
print(new Class());
}'''
}),
const Test(const {
'main.dart': r'''
class Class implements Function {}
main() {
print(new Class());
}'''
},
expectedWarningCount: 1),
const Test(const {
'main.dart': r'''
class Class implements Function {
call() {}
}
main() {
print(new Class()());
}'''
}),
const Test(const {
'main.dart': r'''
class Class implements Comparable<Class> {
int compareTo(Class other) => 0;
}
main() {
print(new Class());
}'''
}),
const Test(const {
'main.dart': r'''
class Class implements Comparable<Class, Class> {
int compareTo(other) => 0;
}
main() {
print(new Class());
}'''
},
expectedWarningCount: 1),
const Test(const {
'main.dart': r'''
class Class implements Comparable<Class> {
int compareTo(String other) => 0;
}
main() {
print(new Class().compareTo(null));
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
class Class implements Comparable {
bool compareTo(a, b) => true;
}
main() {
print(new Class().compareTo(null, null));
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
import 'dart:math';
class MyRandom implements Random {
int nextInt(int max) {
return max.length;
}
bool nextBool() => true;
double nextDouble() => 0.0;
}
main() {
new MyRandom().nextInt(0);
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 0),
const Test(const {
'main.dart': r'''
import 'dart:math';
class MyRandom implements Random {
int nextInt(int max) {
return max.length;
}
bool nextBool() => true;
double nextDouble() => 0.0;
}
main() {
new MyRandom();
}'''
}),
const Test(const {
'main.dart': r'''
import 'dart:math';
class MyRandom implements Random {
int nextInt(int max) {
return max.length;
}
bool nextBool() => true;
double nextDouble() => 0.0;
}
main() {
// Invocation of `MyRandom.nextInt` is only detected knowing the actual
// implementation class for `List` and the world impact of its `shuffle`
// method.
[].shuffle(new MyRandom());
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 0),
];
import 'serialization_test_data.dart';
main(List<String> arguments) {
asyncTest(() async {
@ -217,20 +32,6 @@ main(List<String> arguments) {
});
}
class Test {
final Map sourceFiles;
final int expectedErrorCount;
final int expectedWarningCount;
final int expectedHintCount;
final int expectedInfoCount;
const Test(this.sourceFiles, {
this.expectedErrorCount: 0,
this.expectedWarningCount: 0,
this.expectedHintCount: 0,
this.expectedInfoCount: 0});
}
Future analyze(String serializedData, Uri entryPoint, Test test) async {
DiagnosticCollector diagnosticCollector = new DiagnosticCollector();
await runCompiler(

View file

@ -14,7 +14,7 @@ import 'package:compiler/src/filenames.dart';
import 'package:compiler/src/serialization/equivalence.dart';
import 'memory_compiler.dart';
import 'serialization_helper.dart';
import 'serialization_test.dart';
import 'serialization_test_helper.dart';
main(List<String> arguments) {
asyncTest(() async {
@ -47,65 +47,5 @@ Future check(
deserialize(compilerDeserialized, serializedData);
await compilerDeserialized.run(entryPoint);
checkResolutionImpacts(compilerNormal, compilerDeserialized, verbose: true);
checkAllImpacts(compilerNormal, compilerDeserialized, verbose: true);
}
/// Check equivalence of [impact1] and [impact2].
void checkImpacts(Element element1, Element element2,
ResolutionImpact impact1, ResolutionImpact impact2,
{bool verbose: false}) {
if (impact1 == null || impact2 == null) return;
if (verbose) {
print('Checking impacts for $element1 vs $element2');
}
testResolutionImpactEquivalence(impact1, impact2, const CheckStrategy());
}
/// Check equivalence between all resolution impacts common to [compiler1] and
/// [compiler2].
void checkResolutionImpacts(
Compiler compiler1,
Compiler compiler2,
{bool verbose: false}) {
void checkMembers(Element member1, Element member2) {
if (member1.isClass && member2.isClass) {
ClassElement class1 = member1;
ClassElement class2 = member2;
class1.forEachLocalMember((m1) {
checkMembers(m1, class2.lookupLocalMember(m1.name));
});
return;
}
if (!compiler1.resolution.hasResolutionImpact(member1)) {
return;
}
if (member2 == null) {
return;
}
if (areElementsEquivalent(member1, member2)) {
checkImpacts(
member1, member2,
compiler1.resolution.getResolutionImpact(member1),
compiler2.serialization.deserializer.getResolutionImpact(member2),
verbose: verbose);
}
}
for (LibraryElement library1 in compiler1.libraryLoader.libraries) {
LibraryElement library2 =
compiler2.libraryLoader.lookupLibrary(library1.canonicalUri);
if (library2 != null) {
library1.forEachLocalMember((Element member1) {
checkMembers(member1, library2.localLookup(member1.name));
});
}
}
}

View file

@ -27,9 +27,8 @@ import 'package:compiler/src/universe/class_set.dart';
import 'package:compiler/src/universe/use.dart';
import 'memory_compiler.dart';
import 'serialization_helper.dart';
import 'serialization_analysis_test.dart';
import 'serialization_impact_test.dart';
import 'serialization_test.dart';
import 'serialization_test_data.dart';
import 'serialization_test_helper.dart';
main(List<String> arguments) {
String filename;
@ -109,7 +108,7 @@ Future check(
await compilerDeserialized.run(entryPoint);
compilerDeserialized.world.populate();
checkResolutionImpacts(
checkAllImpacts(
compilerNormal, compilerDeserialized,
verbose: verbose);

View file

@ -20,6 +20,7 @@ import 'package:compiler/src/serialization/element_serialization.dart';
import 'package:compiler/src/serialization/equivalence.dart';
import 'package:compiler/src/serialization/json_serializer.dart';
import 'package:compiler/src/serialization/serialization.dart';
import 'serialization_test_helper.dart';
main(List<String> arguments) {
// Ensure that we can print out constant expressions.
@ -132,262 +133,6 @@ checkElementProperties(
const ElementPropertyEquivalence().visit(element1, element2);
}
/// Check the equivalence of the two lists of elements, [list1] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
checkElementLists(Object object1, Object object2, String property,
Iterable<Element> list1, Iterable<Element> list2) {
checkListEquivalence(object1, object2, property,
list1, list2, checkElementProperties);
}
/// Check equivalence of the two lists, [list1] and [list2], using
/// [checkEquivalence] to check the pair-wise equivalence.
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkListEquivalence(
Object object1, Object object2, String property,
Iterable list1, Iterable list2,
void checkEquivalence(o1, o2, property, a, b)) {
for (int i = 0; i < list1.length && i < list2.length; i++) {
checkEquivalence(
object1, object2, property,
list1.elementAt(i), list2.elementAt(i));
}
for (int i = list1.length; i < list2.length; i++) {
throw
'Missing equivalent for element '
'#$i ${list2.elementAt(i)} in `${property}` on $object2.\n'
'`${property}` on $object1:\n ${list1.join('\n ')}\n'
'`${property}` on $object2:\n ${list2.join('\n ')}';
}
for (int i = list2.length; i < list1.length; i++) {
throw
'Missing equivalent for element '
'#$i ${list1.elementAt(i)} in `${property}` on $object1.\n'
'`${property}` on $object1:\n ${list1.join('\n ')}\n'
'`${property}` on $object2:\n ${list2.join('\n ')}';
}
return true;
}
/// Computes the set difference between [set1] and [set2] using
/// [elementEquivalence] to determine element equivalence.
///
/// Elements both in [set1] and [set2] are added to [common], elements in [set1]
/// but not in [set2] are added to [unfound], and the set of elements in [set2]
/// but not in [set1] are returned.
Set computeSetDifference(
Iterable set1,
Iterable set2,
List common,
List unfound,
[bool sameElement(a, b) = equality]) {
// TODO(johnniwinther): Avoid the quadratic cost here. Some ideas:
// - convert each set to a list and sort it first, then compare by walking
// both lists in parallel
// - map each element to a canonical object, create a map containing those
// mappings, use the mapped sets to compare (then operations like
// set.difference would work)
Set remaining = set2.toSet();
for (var element1 in set1) {
bool found = false;
for (var element2 in remaining) {
if (sameElement(element1, element2)) {
found = true;
remaining.remove(element2);
break;
}
}
if (found) {
common.add(element1);
} else {
unfound.add(element1);
}
}
return remaining;
}
/// Check equivalence of the two iterables, [set1] and [set1], as sets using
/// [elementEquivalence] to compute the pair-wise equivalence.
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkSetEquivalence(
var object1,
var object2,
String property,
Iterable set1,
Iterable set2,
bool sameElement(a, b)) {
List common = [];
List unfound = [];
Set remaining =
computeSetDifference(set1, set2, common, unfound, sameElement);
if (unfound.isNotEmpty || remaining.isNotEmpty) {
String message =
"Set mismatch for `$property` on $object1 vs $object2: \n"
"Common:\n ${common.join('\n ')}\n"
"Unfound:\n ${unfound.join('\n ')}\n"
"Extra: \n ${remaining.join('\n ')}";
throw message;
}
return true;
}
/// Checks the equivalence of the identity (but not properties) of [element1]
/// and [element2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkElementIdentities(
Object object1, Object object2, String property,
Element element1, Element element2) {
if (identical(element1, element2)) return true;
if (element1 == null || element2 == null) {
return check(object1, object2, property, element1, element2);
} else {
return const ElementIdentityEquivalence(const CheckStrategy())
.visit(element1, element2);
}
}
/// Checks the pair-wise equivalence of the identity (but not properties) of the
/// elements in [list] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkElementListIdentities(
Object object1, Object object2, String property,
Iterable<Element> list1, Iterable<Element> list2) {
return checkListEquivalence(
object1, object2, property,
list1, list2, checkElementIdentities);
}
/// Checks the equivalence of [type1] and [type2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkTypes(
Object object1, Object object2, String property,
DartType type1, DartType type2) {
if (identical(type1, type2)) return true;
if (type1 == null || type2 == null) {
return check(object1, object2, property, type1, type2);
} else {
return const TypeEquivalence(const CheckStrategy()).visit(type1, type2);
}
}
/// Checks the pair-wise equivalence of the types in [list1] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkTypeLists(
Object object1, Object object2, String property,
List<DartType> list1, List<DartType> list2) {
return checkListEquivalence(
object1, object2, property, list1, list2, checkTypes);
}
/// Checks the equivalence of [exp1] and [exp2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkConstants(
Object object1, Object object2, String property,
ConstantExpression exp1, ConstantExpression exp2) {
if (identical(exp1, exp2)) return true;
if (exp1 == null || exp2 == null) {
return check(object1, object2, property, exp1, exp2);
} else {
return const ConstantEquivalence(const CheckStrategy()).visit(exp1, exp2);
}
}
/// Checks the pair-wise equivalence of the contants in [list1] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkConstantLists(
Object object1, Object object2, String property,
List<ConstantExpression> list1,
List<ConstantExpression> list2) {
return checkListEquivalence(
object1, object2, property,
list1, list2, checkConstants);
}
/// Strategy for checking equivalence.
///
/// Use this strategy to fail early with contextual information in the event of
/// inequivalence.
class CheckStrategy implements TestStrategy {
const CheckStrategy();
@override
bool test(var object1, var object2, String property, var value1, var value2) {
return check(object1, object2, property, value1, value2);
}
@override
bool testLists(
Object object1, Object object2, String property,
List list1, List list2,
[bool elementEquivalence(a, b) = equality]) {
return checkListEquivalence(
object1, object2, property, list1, list2,
(o1, o2, p, v1, v2) {
if (!elementEquivalence(v1, v2)) {
throw "$o1.$p = '${v1}' <> "
"$o2.$p = '${v2}'";
}
return true;
});
}
@override
bool testSets(
var object1, var object2, String property,
Iterable set1, Iterable set2,
[bool elementEquivalence(a, b) = equality]) {
return checkSetEquivalence(
object1, object2,property, set1, set2, elementEquivalence);
}
@override
bool testElements(
Object object1, Object object2, String property,
Element element1, Element element2) {
return checkElementIdentities(
object1, object2, property, element1, element2);
}
@override
bool testTypes(
Object object1, Object object2, String property,
DartType type1, DartType type2) {
return checkTypes(object1, object2, property, type1, type2);
}
@override
bool testConstants(
Object object1, Object object2, String property,
ConstantExpression exp1, ConstantExpression exp2) {
return checkConstants(object1, object2, property, exp1, exp2);
}
@override
bool testTypeLists(
Object object1, Object object2, String property,
List<DartType> list1, List<DartType> list2) {
return checkTypeLists(object1, object2, property, list1, list2);
}
@override
bool testConstantLists(
Object object1, Object object2, String property,
List<ConstantExpression> list1,
List<ConstantExpression> list2) {
return checkConstantLists(object1, object2, property, list1, list2);
}
}
/// Checks the equivalence of [constructor1] and [constructor2].
void constantConstructorEquivalence(ConstantConstructor constructor1,
ConstantConstructor constructor2) {
@ -477,14 +222,13 @@ class ConstantConstructorEquivalence
}
}
/// Check that the values [property] of [object1] and [object2], [value1] and
/// [value2] respectively, are equal and throw otherwise.
bool check(var object1, var object2, String property, var value1, value2) {
if (value1 != value2) {
throw "$object1.$property = '${value1}' <> "
"$object2.$property = '${value2}'";
}
return true;
/// Check the equivalence of the two lists of elements, [list1] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
checkElementLists(Object object1, Object object2, String property,
Iterable<Element> list1, Iterable<Element> list2) {
checkListEquivalence(object1, object2, property,
list1, list2, checkElementProperties);
}
/// Visitor that checks for equivalence of [Element] properties.

View file

@ -0,0 +1,205 @@
// Copyright (c) 2016, 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.
library dart2js.serialization_test_data;
const List<Test> TESTS = const <Test>[
const Test(const {
'main.dart': 'main() {}'
}),
const Test(const {
'main.dart': 'main() => print("Hello World");'
}),
const Test(const {
'main.dart': 'main() => print("Hello World", 0);'
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
main() {
String text = "Hello World";
print('$text');
}'''
}),
const Test(const {
'main.dart': r'''
main() {
String text = "Hello World";
print('$text', text);
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
main(List<String> arguments) {
print(arguments);
}'''
}),
const Test(const {
'main.dart': r'''
main(List<String> arguments) {
for (int i = 0; i < arguments.length; i++) {
print(arguments[i]);
}
}'''
}),
const Test(const {
'main.dart': r'''
main(List<String> arguments) {
for (String argument in arguments) {
print(argument);
}
}'''
}),
const Test(const {
'main.dart': r'''
class Class {}
main() {
print(new Class());
}'''
}),
const Test(const {
'main.dart': r'''
class Class implements Function {}
main() {
print(new Class());
}'''
},
expectedWarningCount: 1),
const Test(const {
'main.dart': r'''
class Class implements Function {
call() {}
}
main() {
print(new Class()());
}'''
}),
const Test(const {
'main.dart': r'''
class Class implements Comparable<Class> {
int compareTo(Class other) => 0;
}
main() {
print(new Class());
}'''
}),
const Test(const {
'main.dart': r'''
class Class implements Comparable<Class, Class> {
int compareTo(other) => 0;
}
main() {
print(new Class());
}'''
},
expectedWarningCount: 1),
const Test(const {
'main.dart': r'''
class Class implements Comparable<Class> {
int compareTo(String other) => 0;
}
main() {
print(new Class().compareTo(null));
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
class Class implements Comparable {
bool compareTo(a, b) => true;
}
main() {
print(new Class().compareTo(null, null));
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 1),
const Test(const {
'main.dart': r'''
import 'dart:math';
class MyRandom implements Random {
int nextInt(int max) {
return max.length;
}
bool nextBool() => true;
double nextDouble() => 0.0;
}
main() {
new MyRandom().nextInt(0);
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 0),
const Test(const {
'main.dart': r'''
import 'dart:math';
class MyRandom implements Random {
int nextInt(int max) {
return max.length;
}
bool nextBool() => true;
double nextDouble() => 0.0;
}
main() {
new MyRandom();
}'''
}),
const Test(const {
'main.dart': r'''
import 'dart:math';
class MyRandom implements Random {
int nextInt(int max) {
return max.length;
}
bool nextBool() => true;
double nextDouble() => 0.0;
}
main() {
// Invocation of `MyRandom.nextInt` is only detected knowing the actual
// implementation class for `List` and the world impact of its `shuffle`
// method.
[].shuffle(new MyRandom());
}'''
},
expectedWarningCount: 1,
expectedInfoCount: 0),
];
class Test {
final Map sourceFiles;
final int expectedErrorCount;
final int expectedWarningCount;
final int expectedHintCount;
final int expectedInfoCount;
const Test(this.sourceFiles, {
this.expectedErrorCount: 0,
this.expectedWarningCount: 0,
this.expectedHintCount: 0,
this.expectedInfoCount: 0});
}

View file

@ -0,0 +1,365 @@
// Copyright (c) 2016, 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.
library dart2js.serialization_test_helper;
import 'dart:io';
import 'memory_compiler.dart';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/common/resolution.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/constants/constructors.dart';
import 'package:compiler/src/constants/expressions.dart';
import 'package:compiler/src/dart_types.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/diagnostics/invariant.dart';
import 'package:compiler/src/elements/elements.dart';
import 'package:compiler/src/elements/visitor.dart';
import 'package:compiler/src/ordered_typeset.dart';
import 'package:compiler/src/serialization/element_serialization.dart';
import 'package:compiler/src/serialization/equivalence.dart';
import 'package:compiler/src/serialization/json_serializer.dart';
import 'package:compiler/src/serialization/serialization.dart';
/// Strategy for checking equivalence.
///
/// Use this strategy to fail early with contextual information in the event of
/// inequivalence.
class CheckStrategy implements TestStrategy {
const CheckStrategy();
@override
bool test(var object1, var object2, String property, var value1, var value2,
[bool equivalence(a, b) = equality]) {
return check(object1, object2, property, value1, value2, equivalence);
}
@override
bool testLists(
Object object1, Object object2, String property,
List list1, List list2,
[bool elementEquivalence(a, b) = equality]) {
return checkListEquivalence(
object1, object2, property, list1, list2,
(o1, o2, p, v1, v2) {
if (!elementEquivalence(v1, v2)) {
throw "$o1.$p = '${v1}' <> "
"$o2.$p = '${v2}'";
}
return true;
});
}
@override
bool testSets(
var object1, var object2, String property,
Iterable set1, Iterable set2,
[bool elementEquivalence(a, b) = equality]) {
return checkSetEquivalence(
object1, object2,property, set1, set2, elementEquivalence);
}
@override
bool testElements(
Object object1, Object object2, String property,
Element element1, Element element2) {
return checkElementIdentities(
object1, object2, property, element1, element2);
}
@override
bool testTypes(
Object object1, Object object2, String property,
DartType type1, DartType type2) {
return checkTypes(object1, object2, property, type1, type2);
}
@override
bool testConstants(
Object object1, Object object2, String property,
ConstantExpression exp1, ConstantExpression exp2) {
return checkConstants(object1, object2, property, exp1, exp2);
}
@override
bool testTypeLists(
Object object1, Object object2, String property,
List<DartType> list1, List<DartType> list2) {
return checkTypeLists(object1, object2, property, list1, list2);
}
@override
bool testConstantLists(
Object object1, Object object2, String property,
List<ConstantExpression> list1,
List<ConstantExpression> list2) {
return checkConstantLists(object1, object2, property, list1, list2);
}
}
/// Check that the values [property] of [object1] and [object2], [value1] and
/// [value2] respectively, are equal and throw otherwise.
bool check(var object1, var object2, String property, var value1, var value2,
[bool equivalence(a, b) = equality]) {
if (!equivalence(value1, value2)) {
throw "property='$property' "
"object1=$object1 (${object1.runtimeType}), value='${value1}' <> "
"object2=$object2 (${object2.runtimeType}), value='${value2}'";
}
return true;
}
/// Check equivalence of the two lists, [list1] and [list2], using
/// [checkEquivalence] to check the pair-wise equivalence.
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkListEquivalence(
Object object1, Object object2, String property,
Iterable list1, Iterable list2,
void checkEquivalence(o1, o2, property, a, b)) {
for (int i = 0; i < list1.length && i < list2.length; i++) {
checkEquivalence(
object1, object2, property,
list1.elementAt(i), list2.elementAt(i));
}
for (int i = list1.length; i < list2.length; i++) {
throw
'Missing equivalent for element '
'#$i ${list2.elementAt(i)} in `${property}` on $object2.\n'
'`${property}` on $object1:\n ${list1.join('\n ')}\n'
'`${property}` on $object2:\n ${list2.join('\n ')}';
}
for (int i = list2.length; i < list1.length; i++) {
throw
'Missing equivalent for element '
'#$i ${list1.elementAt(i)} in `${property}` on $object1.\n'
'`${property}` on $object1:\n ${list1.join('\n ')}\n'
'`${property}` on $object2:\n ${list2.join('\n ')}';
}
return true;
}
/// Computes the set difference between [set1] and [set2] using
/// [elementEquivalence] to determine element equivalence.
///
/// Elements both in [set1] and [set2] are added to [common], elements in [set1]
/// but not in [set2] are added to [unfound], and the set of elements in [set2]
/// but not in [set1] are returned.
Set computeSetDifference(
Iterable set1,
Iterable set2,
List common,
List unfound,
[bool sameElement(a, b) = equality]) {
// TODO(johnniwinther): Avoid the quadratic cost here. Some ideas:
// - convert each set to a list and sort it first, then compare by walking
// both lists in parallel
// - map each element to a canonical object, create a map containing those
// mappings, use the mapped sets to compare (then operations like
// set.difference would work)
Set remaining = set2.toSet();
for (var element1 in set1) {
bool found = false;
for (var element2 in remaining) {
if (sameElement(element1, element2)) {
found = true;
remaining.remove(element2);
break;
}
}
if (found) {
common.add(element1);
} else {
unfound.add(element1);
}
}
return remaining;
}
/// Check equivalence of the two iterables, [set1] and [set1], as sets using
/// [elementEquivalence] to compute the pair-wise equivalence.
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkSetEquivalence(
var object1,
var object2,
String property,
Iterable set1,
Iterable set2,
bool sameElement(a, b)) {
List common = [];
List unfound = [];
Set remaining =
computeSetDifference(set1, set2, common, unfound, sameElement);
if (unfound.isNotEmpty || remaining.isNotEmpty) {
String message =
"Set mismatch for `$property` on $object1 vs $object2: \n"
"Common:\n ${common.join('\n ')}\n"
"Unfound:\n ${unfound.join('\n ')}\n"
"Extra: \n ${remaining.join('\n ')}";
throw message;
}
return true;
}
/// Checks the equivalence of the identity (but not properties) of [element1]
/// and [element2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkElementIdentities(
Object object1, Object object2, String property,
Element element1, Element element2) {
if (identical(element1, element2)) return true;
if (element1 == null || element2 == null) {
return check(object1, object2, property, element1, element2);
} else {
return const ElementIdentityEquivalence(const CheckStrategy())
.visit(element1, element2);
}
}
/// Checks the pair-wise equivalence of the identity (but not properties) of the
/// elements in [list] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkElementListIdentities(
Object object1, Object object2, String property,
Iterable<Element> list1, Iterable<Element> list2) {
return checkListEquivalence(
object1, object2, property,
list1, list2, checkElementIdentities);
}
/// Checks the equivalence of [type1] and [type2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkTypes(
Object object1, Object object2, String property,
DartType type1, DartType type2) {
if (identical(type1, type2)) return true;
if (type1 == null || type2 == null) {
return check(object1, object2, property, type1, type2);
} else {
return const TypeEquivalence(const CheckStrategy()).visit(type1, type2);
}
}
/// Checks the pair-wise equivalence of the types in [list1] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkTypeLists(
Object object1, Object object2, String property,
List<DartType> list1, List<DartType> list2) {
return checkListEquivalence(
object1, object2, property, list1, list2, checkTypes);
}
/// Checks the equivalence of [exp1] and [exp2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkConstants(
Object object1, Object object2, String property,
ConstantExpression exp1, ConstantExpression exp2) {
if (identical(exp1, exp2)) return true;
if (exp1 == null || exp2 == null) {
return check(object1, object2, property, exp1, exp2);
} else {
return const ConstantEquivalence(const CheckStrategy()).visit(exp1, exp2);
}
}
/// Checks the pair-wise equivalence of the contants in [list1] and [list2].
///
/// Uses [object1], [object2] and [property] to provide context for failures.
bool checkConstantLists(
Object object1, Object object2, String property,
List<ConstantExpression> list1,
List<ConstantExpression> list2) {
return checkListEquivalence(
object1, object2, property,
list1, list2, checkConstants);
}
/// Check member property equivalence between all members common to [compiler1]
/// and [compiler2].
void checkLoadedLibraryMembers(
Compiler compiler1,
Compiler compiler2,
bool hasProperty(Element member1),
void checkMemberProperties(Compiler compiler1, Element member1,
Compiler compiler2, Element member2,
{bool verbose}),
{bool verbose: false}) {
void checkMembers(Element member1, Element member2) {
if (member1.isClass && member2.isClass) {
ClassElement class1 = member1;
ClassElement class2 = member2;
class1.forEachLocalMember((m1) {
checkMembers(m1, class2.lookupLocalMember(m1.name));
});
return;
}
if (!hasProperty(member1)) {
return;
}
if (member2 == null) {
return;
}
if (areElementsEquivalent(member1, member2)) {
checkMemberProperties(
compiler1, member1,
compiler2, member2,
verbose: verbose);
}
}
for (LibraryElement library1 in compiler1.libraryLoader.libraries) {
LibraryElement library2 =
compiler2.libraryLoader.lookupLibrary(library1.canonicalUri);
if (library2 != null) {
library1.forEachLocalMember((Element member1) {
checkMembers(member1, library2.localLookup(member1.name));
});
}
}
}
/// Check equivalence of all resolution impacts.
void checkAllImpacts(
Compiler compiler1,
Compiler compiler2,
{bool verbose: false}) {
checkLoadedLibraryMembers(
compiler1,
compiler2,
(Element member1) {
return compiler1.resolution.hasResolutionImpact(member1);
},
checkImpacts,
verbose: true);
}
/// Check equivalence of resolution impact for [member1] and [member2].
void checkImpacts(Compiler compiler1, Element member1,
Compiler compiler2, Element member2,
{bool verbose: false}) {
ResolutionImpact impact1 = compiler1.resolution.getResolutionImpact(member1);
ResolutionImpact impact2 =
compiler2.serialization.deserializer.getResolutionImpact(member2);
if (impact1 == null || impact2 == null) return;
if (verbose) {
print('Checking impacts for $member1 vs $member2');
}
testResolutionImpactEquivalence(impact1, impact2, const CheckStrategy());
}