Test closed world model after deserialization.
We still need better tracking of native/foreign elements. R=sigmund@google.com Review URL: https://codereview.chromium.org/1856953003 .
This commit is contained in:
@@ -188,7 +188,9 @@ abstract class Resolution {
|
||||
DiagnosticReporter get reporter;
|
||||
CoreTypes get coreTypes;
|
||||
|
||||
bool retainCaches;
|
||||
/// If set to `true` resolution caches will not be cleared. Use this only for
|
||||
/// testing.
|
||||
bool retainCachesForTesting;
|
||||
|
||||
void resolveTypedef(TypedefElement typdef);
|
||||
void resolveClass(ClassElement cls);
|
||||
|
||||
@@ -1959,7 +1959,7 @@ class _CompilerResolution implements Resolution {
|
||||
final Map<Element, ResolutionImpact> _resolutionImpactCache =
|
||||
<Element, ResolutionImpact>{};
|
||||
final Map<Element, WorldImpact> _worldImpactCache = <Element, WorldImpact>{};
|
||||
bool retainCaches = false;
|
||||
bool retainCachesForTesting = false;
|
||||
|
||||
_CompilerResolution(this.compiler);
|
||||
|
||||
@@ -2031,7 +2031,7 @@ class _CompilerResolution implements Resolution {
|
||||
assert(invariant(element, !element.isSynthesized || tree == null));
|
||||
ResolutionImpact resolutionImpact =
|
||||
compiler.resolver.resolve(element);
|
||||
if (compiler.serialization.supportSerialization || retainCaches) {
|
||||
if (compiler.serialization.supportSerialization || retainCachesForTesting) {
|
||||
// [ResolutionImpact] is currently only used by serialization. The
|
||||
// enqueuer uses the [WorldImpact] which is always cached.
|
||||
// TODO(johnniwinther): Align these use cases better; maybe only
|
||||
@@ -2054,7 +2054,7 @@ class _CompilerResolution implements Resolution {
|
||||
|
||||
@override
|
||||
void uncacheWorldImpact(Element element) {
|
||||
if (retainCaches) return;
|
||||
if (retainCachesForTesting) return;
|
||||
if (compiler.serialization.isDeserialized(element)) return;
|
||||
assert(invariant(element, _worldImpactCache[element] != null,
|
||||
message: "WorldImpact not computed for $element."));
|
||||
@@ -2064,7 +2064,7 @@ class _CompilerResolution implements Resolution {
|
||||
|
||||
@override
|
||||
void emptyCache() {
|
||||
if (retainCaches) return;
|
||||
if (retainCachesForTesting) return;
|
||||
for (Element element in _worldImpactCache.keys) {
|
||||
_worldImpactCache[element] = const WorldImpact();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ library elements.common;
|
||||
import '../common/names.dart' show
|
||||
Names,
|
||||
Uris;
|
||||
import '../core_types.dart' show
|
||||
CoreClasses;
|
||||
import '../dart_types.dart' show
|
||||
DartType,
|
||||
InterfaceType,
|
||||
@@ -429,6 +431,12 @@ abstract class ClassElementCommon implements ClassElement {
|
||||
allSupertypesAndSelf.asInstanceOf(intrface) != null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool implementsFunction(CoreClasses coreClasses) {
|
||||
return asInstanceOf(coreClasses.functionClass) != null ||
|
||||
callType != null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSubclassOf(ClassElement cls) {
|
||||
// Use [declaration] for both [this] and [cls], because
|
||||
|
||||
@@ -13,8 +13,6 @@ import '../compiler.dart' show
|
||||
import '../constants/constant_constructors.dart';
|
||||
import '../constants/constructors.dart';
|
||||
import '../constants/expressions.dart';
|
||||
import '../core_types.dart' show
|
||||
CoreClasses;
|
||||
import '../dart_types.dart';
|
||||
import '../diagnostics/messages.dart' show
|
||||
MessageTemplate;
|
||||
@@ -2701,11 +2699,6 @@ abstract class BaseClassElementX extends ElementX
|
||||
backendMembers.forEach(f);
|
||||
}
|
||||
|
||||
bool implementsFunction(CoreClasses coreClasses) {
|
||||
return asInstanceOf(coreClasses.functionClass) != null ||
|
||||
callType != null;
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Remove these when issue 18630 is fixed.
|
||||
ClassElement get patch => super.patch;
|
||||
ClassElement get origin => super.origin;
|
||||
|
||||
@@ -308,10 +308,13 @@ class ClassSerializer implements ElementSerializer {
|
||||
mixins = mixins.reversed.toList();
|
||||
InterfaceType supertype = element.thisType.asInstanceOf(superclass);
|
||||
|
||||
|
||||
encoder.setType(Key.SUPERTYPE, supertype);
|
||||
encoder.setTypes(Key.MIXINS, mixins);
|
||||
encoder.setTypes(Key.INTERFACES, element.interfaces.toList());
|
||||
FunctionType callType = element.declaration.callType;
|
||||
if (callType != null) {
|
||||
encoder.setType(Key.CALL_TYPE, element.callType);
|
||||
}
|
||||
|
||||
if (element.isMixinApplication) {
|
||||
MixinApplicationElement mixinElement = element;
|
||||
|
||||
@@ -39,7 +39,6 @@ bool areSetsEquivalent(
|
||||
Iterable set1,
|
||||
Iterable set2,
|
||||
[bool elementEquivalence(a, b) = equality]) {
|
||||
|
||||
Set remaining = set2.toSet();
|
||||
for (var element1 in set1) {
|
||||
bool found = false;
|
||||
|
||||
@@ -10,6 +10,7 @@ class Key {
|
||||
static const Key ARGUMENTS = const Key('arguments');
|
||||
static const Key BOUND = const Key('bound');
|
||||
static const Key CALL_STRUCTURE = const Key('callStructure');
|
||||
static const Key CALL_TYPE = const Key('callType');
|
||||
static const Key CANONICAL_URI = const Key('canonicalUri');
|
||||
static const Key CLASS = const Key('class');
|
||||
static const Key COMPILATION_UNIT = const Key('compilation-unit');
|
||||
|
||||
@@ -747,7 +747,6 @@ abstract class FunctionTypedElementMixin
|
||||
}
|
||||
|
||||
abstract class ClassElementMixin implements ElementZ, ClassElement {
|
||||
|
||||
InterfaceType _createType(List<DartType> typeArguments) {
|
||||
return new InterfaceType(this, typeArguments);
|
||||
}
|
||||
@@ -778,11 +777,6 @@ abstract class ClassElementMixin implements ElementZ, ClassElement {
|
||||
@override
|
||||
bool get hasLocalScopeMembers => _unsupported('hasLocalScopeMembers');
|
||||
|
||||
@override
|
||||
bool implementsFunction(CoreClasses coreClasses) {
|
||||
return _unsupported('implementsFunction');
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isEnumClass => false;
|
||||
|
||||
@@ -827,6 +821,7 @@ class ClassElementZ extends DeserializedElementZ
|
||||
DartType _supertype;
|
||||
OrderedTypeSet _allSupertypesAndSelf;
|
||||
Link<DartType> _interfaces;
|
||||
FunctionType _callType;
|
||||
|
||||
ClassElementZ(ObjectDecoder decoder)
|
||||
: super(decoder);
|
||||
@@ -860,6 +855,7 @@ class ClassElementZ extends DeserializedElementZ
|
||||
_allSupertypesAndSelf =
|
||||
new OrderedTypeSetBuilder(this)
|
||||
.createOrderedTypeSet(_supertype, _interfaces);
|
||||
_callType = _decoder.getType(Key.CALL_TYPE, isOptional: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -901,6 +897,12 @@ class ClassElementZ extends DeserializedElementZ
|
||||
|
||||
@override
|
||||
bool get isUnnamedMixinApplication => false;
|
||||
|
||||
@override
|
||||
FunctionType get callType {
|
||||
_ensureSuperHierarchy();
|
||||
return _callType;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MixinApplicationElementMixin
|
||||
|
||||
@@ -181,6 +181,8 @@ class ClassHierarchyNode {
|
||||
_directSubclasses = _directSubclasses.prepend(subclass);
|
||||
}
|
||||
|
||||
Iterable<ClassHierarchyNode> get directSubclasses => _directSubclasses;
|
||||
|
||||
/// Returns `true` if [other] is contained in the subtree of this node.
|
||||
///
|
||||
/// This means that [other] is a subclass of [cls].
|
||||
|
||||
@@ -16,6 +16,10 @@ 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");'
|
||||
}),
|
||||
|
||||
@@ -37,25 +37,28 @@ Future check(
|
||||
Compiler compilerNormal = compilerFor(
|
||||
memorySourceFiles: sourceFiles,
|
||||
options: [Flags.analyzeOnly]);
|
||||
compilerNormal.resolution.retainCaches = true;
|
||||
compilerNormal.resolution.retainCachesForTesting = true;
|
||||
await compilerNormal.run(entryPoint);
|
||||
|
||||
Compiler compilerDeserialized = compilerFor(
|
||||
memorySourceFiles: sourceFiles,
|
||||
options: [Flags.analyzeOnly]);
|
||||
compilerDeserialized.resolution.retainCaches = true;
|
||||
compilerDeserialized.resolution.retainCachesForTesting = true;
|
||||
deserialize(compilerDeserialized, serializedData);
|
||||
await compilerDeserialized.run(entryPoint);
|
||||
|
||||
checkResolutionImpacts(compilerNormal, compilerDeserialized);
|
||||
checkResolutionImpacts(compilerNormal, compilerDeserialized, verbose: true);
|
||||
}
|
||||
|
||||
/// Check equivalence of [impact1] and [impact2].
|
||||
void checkImpacts(Element element1, Element element2,
|
||||
ResolutionImpact impact1, ResolutionImpact impact2) {
|
||||
ResolutionImpact impact1, ResolutionImpact impact2,
|
||||
{bool verbose: false}) {
|
||||
if (impact1 == null || impact2 == null) return;
|
||||
|
||||
print('Checking impacts for $element1 vs $element2');
|
||||
if (verbose) {
|
||||
print('Checking impacts for $element1 vs $element2');
|
||||
}
|
||||
|
||||
testResolutionImpactEquivalence(impact1, impact2, const CheckStrategy());
|
||||
}
|
||||
@@ -63,7 +66,10 @@ void checkImpacts(Element element1, Element element2,
|
||||
|
||||
/// Check equivalence between all resolution impacts common to [compiler1] and
|
||||
/// [compiler2].
|
||||
void checkResolutionImpacts(Compiler compiler1, Compiler compiler2) {
|
||||
void checkResolutionImpacts(
|
||||
Compiler compiler1,
|
||||
Compiler compiler2,
|
||||
{bool verbose: false}) {
|
||||
|
||||
void checkMembers(Element member1, Element member2) {
|
||||
if (member1.isClass && member2.isClass) {
|
||||
@@ -87,7 +93,8 @@ void checkResolutionImpacts(Compiler compiler1, Compiler compiler2) {
|
||||
checkImpacts(
|
||||
member1, member2,
|
||||
compiler1.resolution.getResolutionImpact(member1),
|
||||
compiler2.serialization.deserializer.getResolutionImpact(member2));
|
||||
compiler2.serialization.deserializer.getResolutionImpact(member2),
|
||||
verbose: verbose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2015, 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_model_test;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:compiler/src/commandline_options.dart';
|
||||
import 'package:compiler/src/common/backend_api.dart';
|
||||
import 'package:compiler/src/common/names.dart';
|
||||
import 'package:compiler/src/common/resolution.dart';
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/dart_types.dart';
|
||||
import 'package:compiler/src/elements/elements.dart';
|
||||
import 'package:compiler/src/filenames.dart';
|
||||
import 'package:compiler/src/serialization/element_serialization.dart';
|
||||
import 'package:compiler/src/serialization/impact_serialization.dart';
|
||||
import 'package:compiler/src/serialization/json_serializer.dart';
|
||||
import 'package:compiler/src/serialization/serialization.dart';
|
||||
import 'package:compiler/src/serialization/equivalence.dart';
|
||||
import 'package:compiler/src/serialization/task.dart';
|
||||
import 'package:compiler/src/universe/world_impact.dart';
|
||||
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';
|
||||
|
||||
main(List<String> arguments) {
|
||||
String filename;
|
||||
for (String arg in arguments) {
|
||||
if (!arg.startsWith('-')) {
|
||||
filename = arg;
|
||||
}
|
||||
}
|
||||
bool verbose = arguments.contains('-v');
|
||||
|
||||
asyncTest(() async {
|
||||
print('------------------------------------------------------------------');
|
||||
print('serialize dart:core');
|
||||
print('------------------------------------------------------------------');
|
||||
String serializedData;
|
||||
File file = new File('out.data');
|
||||
if (arguments.contains('-l')) {
|
||||
if (file.existsSync()) {
|
||||
print('Loading data from $file');
|
||||
serializedData = file.readAsStringSync();
|
||||
}
|
||||
}
|
||||
if (serializedData == null) {
|
||||
serializedData = await serializeDartCore();
|
||||
if (arguments.contains('-s')) {
|
||||
print('Saving data to $file');
|
||||
file.writeAsStringSync(serializedData);
|
||||
}
|
||||
}
|
||||
if (filename != null) {
|
||||
Uri entryPoint = Uri.base.resolve(nativeToUriPath(filename));
|
||||
await check(serializedData, entryPoint);
|
||||
} else {
|
||||
Uri entryPoint = Uri.parse('memory:main.dart');
|
||||
for (Test test in TESTS) {
|
||||
if (test.sourceFiles['main.dart']
|
||||
.contains('main(List<String> arguments)')) {
|
||||
// TODO(johnniwinther): Check this test.
|
||||
continue;
|
||||
}
|
||||
print('==============================================================');
|
||||
print(test.sourceFiles);
|
||||
await check(
|
||||
serializedData,
|
||||
entryPoint,
|
||||
sourceFiles: test.sourceFiles,
|
||||
verbose: verbose);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future check(
|
||||
String serializedData,
|
||||
Uri entryPoint,
|
||||
{Map<String, String> sourceFiles: const <String, String>{},
|
||||
bool verbose: false}) async {
|
||||
|
||||
print('------------------------------------------------------------------');
|
||||
print('compile normal');
|
||||
print('------------------------------------------------------------------');
|
||||
Compiler compilerNormal = compilerFor(
|
||||
memorySourceFiles: sourceFiles,
|
||||
options: [Flags.analyzeOnly]);
|
||||
compilerNormal.resolution.retainCachesForTesting = true;
|
||||
await compilerNormal.run(entryPoint);
|
||||
compilerNormal.world.populate();
|
||||
|
||||
print('------------------------------------------------------------------');
|
||||
print('compile deserialized');
|
||||
print('------------------------------------------------------------------');
|
||||
Compiler compilerDeserialized = compilerFor(
|
||||
memorySourceFiles: sourceFiles,
|
||||
options: [Flags.analyzeOnly]);
|
||||
compilerDeserialized.resolution.retainCachesForTesting = true;
|
||||
deserialize(compilerDeserialized, serializedData);
|
||||
await compilerDeserialized.run(entryPoint);
|
||||
compilerDeserialized.world.populate();
|
||||
|
||||
checkResolutionImpacts(
|
||||
compilerNormal, compilerDeserialized,
|
||||
verbose: verbose);
|
||||
|
||||
checkSets(
|
||||
compilerNormal.resolverWorld.directlyInstantiatedClasses,
|
||||
compilerDeserialized.resolverWorld.directlyInstantiatedClasses,
|
||||
"Directly instantiated classes mismatch",
|
||||
areElementsEquivalent,
|
||||
verbose: verbose);
|
||||
|
||||
checkSets(
|
||||
compilerNormal.resolverWorld.instantiatedTypes,
|
||||
compilerDeserialized.resolverWorld.instantiatedTypes,
|
||||
"Instantiated types mismatch",
|
||||
areTypesEquivalent,
|
||||
// TODO(johnniwinther): Ensure that all instantiated types are tracked.
|
||||
failOnUnfound: false,
|
||||
verbose: verbose);
|
||||
|
||||
checkSets(
|
||||
compilerNormal.resolverWorld.isChecks,
|
||||
compilerDeserialized.resolverWorld.isChecks,
|
||||
"Is-check mismatch",
|
||||
areTypesEquivalent,
|
||||
verbose: verbose);
|
||||
|
||||
checkSets(
|
||||
compilerNormal.enqueuer.resolution.processedElements,
|
||||
compilerDeserialized.enqueuer.resolution.processedElements,
|
||||
"Processed element mismatch",
|
||||
areElementsEquivalent,
|
||||
verbose: verbose);
|
||||
|
||||
checkClassHierarchyNodes(
|
||||
compilerNormal.world.getClassHierarchyNode(
|
||||
compilerNormal.coreClasses.objectClass),
|
||||
compilerDeserialized.world.getClassHierarchyNode(
|
||||
compilerDeserialized.coreClasses.objectClass),
|
||||
verbose: verbose);
|
||||
}
|
||||
|
||||
void checkClassHierarchyNodes(
|
||||
ClassHierarchyNode a, ClassHierarchyNode b,
|
||||
{bool verbose: false}) {
|
||||
if (verbose) {
|
||||
print('Checking $a vs $b');
|
||||
}
|
||||
Expect.isTrue(
|
||||
areElementsEquivalent(a.cls, b.cls),
|
||||
"Element identity mismatch for ${a.cls} vs ${b.cls}.");
|
||||
Expect.equals(
|
||||
a.isDirectlyInstantiated,
|
||||
b.isDirectlyInstantiated,
|
||||
"Value mismatch for 'isDirectlyInstantiated' for ${a.cls} vs ${b.cls}.");
|
||||
Expect.equals(
|
||||
a.isIndirectlyInstantiated,
|
||||
b.isIndirectlyInstantiated,
|
||||
"Value mismatch for 'isIndirectlyInstantiated' "
|
||||
"for ${a.cls} vs ${b.cls}.");
|
||||
// TODO(johnniwinther): Enforce a canonical and stable order on direct
|
||||
// subclasses.
|
||||
for (ClassHierarchyNode child in a.directSubclasses) {
|
||||
bool found = false;
|
||||
for (ClassHierarchyNode other in b.directSubclasses) {
|
||||
if (areElementsEquivalent(child.cls, other.cls)) {
|
||||
checkClassHierarchyNodes(child, other,
|
||||
verbose: verbose);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Expect.isFalse(
|
||||
child.isInstantiated, 'Missing subclass ${child.cls} of ${a.cls}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkSets(
|
||||
Iterable set1,
|
||||
Iterable set2,
|
||||
String messagePrefix,
|
||||
bool areEquivalent(a, b),
|
||||
{bool failOnUnfound: true,
|
||||
bool verbose: false}) {
|
||||
List common = [];
|
||||
List unfound = [];
|
||||
Set remaining = computeSetDifference(
|
||||
set1, set2, common, unfound, areEquivalent);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write("$messagePrefix:");
|
||||
if (verbose) {
|
||||
sb.write("\n Common:\n ${common.join('\n ')}");
|
||||
}
|
||||
if (unfound.isNotEmpty || verbose) {
|
||||
sb.write("\n Unfound:\n ${unfound.join('\n ')}");
|
||||
}
|
||||
if (remaining.isNotEmpty || verbose) {
|
||||
sb.write("\n Extra: \n ${remaining.join('\n ')}");
|
||||
}
|
||||
String message = sb.toString();
|
||||
if (unfound.isNotEmpty || remaining.isNotEmpty) {
|
||||
|
||||
if (failOnUnfound || remaining.isNotEmpty) {
|
||||
Expect.fail(message);
|
||||
} else {
|
||||
print(message);
|
||||
}
|
||||
} else if (verbose) {
|
||||
print(message);
|
||||
}
|
||||
}
|
||||
@@ -171,19 +171,24 @@ bool checkListEquivalence(
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check equivalence of the two iterables, [set1] and [set1], as sets using
|
||||
/// [elementEquivalence] to compute the pair-wise equivalence.
|
||||
/// Computes the set difference between [set1] and [set2] using
|
||||
/// [elementEquivalence] to determine element equivalence.
|
||||
///
|
||||
/// Uses [object1], [object2] and [property] to provide context for failures.
|
||||
bool checkSetEquivalence(
|
||||
var object1,
|
||||
var object2,
|
||||
String property,
|
||||
/// 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,
|
||||
bool sameElement(a, b)) {
|
||||
List common = [];
|
||||
List unfound = [];
|
||||
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;
|
||||
@@ -200,6 +205,24 @@ bool checkSetEquivalence(
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user