Support deserialization of ResolutionImpact for members of unnamed mixin applications

R=sigmund@google.com

Review URL: https://codereview.chromium.org/1901683002 .
This commit is contained in:
Johnni Winther 2016-04-19 10:26:42 +02:00
parent 553eec6047
commit 4b01f4136e
9 changed files with 185 additions and 37 deletions

View file

@ -1878,6 +1878,9 @@ class _CompilerResolution implements Resolution {
bool hasResolutionImpact(Element element) {
assert(invariant(element, element.isDeclaration,
message: "Element $element must be the declaration."));
if (compiler.serialization.isDeserialized(element)) {
return compiler.serialization.hasResolutionImpact(element);
}
return _resolutionImpactCache.containsKey(element);
}
@ -1885,7 +1888,12 @@ class _CompilerResolution implements Resolution {
ResolutionImpact getResolutionImpact(Element element) {
assert(invariant(element, element.isDeclaration,
message: "Element $element must be the declaration."));
ResolutionImpact resolutionImpact = _resolutionImpactCache[element];
ResolutionImpact resolutionImpact;
if (compiler.serialization.isDeserialized(element)) {
resolutionImpact = compiler.serialization.getResolutionImpact(element);
} else {
resolutionImpact = _resolutionImpactCache[element];
}
assert(invariant(element, resolutionImpact != null,
message: "ResolutionImpact not available for $element."));
return resolutionImpact;
@ -1910,6 +1918,7 @@ class _CompilerResolution implements Resolution {
Node tree = compiler.parser.parse(element);
assert(invariant(element, !element.isSynthesized || tree == null));
ResolutionImpact resolutionImpact = compiler.resolver.resolve(element);
if (compiler.serialization.supportSerialization ||
retainCachesForTesting) {
// [ResolutionImpact] is currently only used by serialization. The

View file

@ -20,7 +20,7 @@ import '../util/util.dart' show Setlet;
import '../universe/call_structure.dart' show CallStructure;
import '../universe/selector.dart' show Selector;
import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse;
import '../universe/world_impact.dart' show WorldImpactBuilder;
import '../universe/world_impact.dart' show WorldImpact, WorldImpactBuilder;
import '../util/enumset.dart' show EnumSet;
import '../world.dart' show World;
@ -104,7 +104,39 @@ class _ResolutionWorldImpact extends ResolutionImpact with WorldImpactBuilder {
: const <ConstantExpression>[];
}
String toString() => '_ResolutionWorldImpact($name)';
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('_ResolutionWorldImpact($name)');
WorldImpact.printOn(sb, this);
if (_features != null) {
sb.write('\n features:');
for (Feature feature in _features.iterable(Feature.values)) {
sb.write('\n $feature');
}
}
if (_mapLiterals != null) {
sb.write('\n map-literals:');
for (MapLiteralUse use in _mapLiterals) {
sb.write('\n $use');
}
}
if (_listLiterals != null) {
sb.write('\n list-literals:');
for (ListLiteralUse use in _listLiterals) {
sb.write('\n $use');
}
}
if (_constantLiterals != null) {
sb.write('\n const-literals:');
for (ConstantExpression constant in _constantLiterals) {
sb.write('\n ${constant.getText()}');
}
}
if (_constSymbolNames != null) {
sb.write('\n const-symbol-names: $_constSymbolNames');
}
return sb.toString();
}
}
/// [ResolutionRegistry] collects all resolution information. It stores node

View file

@ -753,6 +753,14 @@ bool testResolutionImpactEquivalence(
bool testResolvedAstEquivalence(
ResolvedAst resolvedAst1, ResolvedAst resolvedAst2,
[TestStrategy strategy = const TestStrategy()]) {
if (!strategy.test(resolvedAst1, resolvedAst1, 'kind', resolvedAst1.kind,
resolvedAst2.kind)) {
return false;
}
if (resolvedAst1.kind != ResolvedAstKind.PARSED) {
// Nothing more to check.
return true;
}
return strategy.testElements(resolvedAst1, resolvedAst2, 'element',
resolvedAst1.element, resolvedAst2.element) &&
new NodeEquivalenceVisitor(strategy).testNodes(resolvedAst1, resolvedAst2,

View file

@ -4,6 +4,7 @@
library dart2js.serialization.impact;
import '../common.dart';
import '../common/resolution.dart';
import '../constants/expressions.dart';
import '../dart_types.dart';
@ -19,12 +20,13 @@ import 'serialization_util.dart';
/// Visitor that serializes a [ResolutionImpact] object using an
/// [ObjectEncoder].
class ImpactSerializer implements WorldImpactVisitor {
final Element element;
final ObjectEncoder objectEncoder;
final ListEncoder staticUses;
final ListEncoder dynamicUses;
final ListEncoder typeUses;
ImpactSerializer(ObjectEncoder objectEncoder)
ImpactSerializer(this.element, ObjectEncoder objectEncoder)
: this.objectEncoder = objectEncoder,
staticUses = objectEncoder.createList(Key.STATIC_USES),
dynamicUses = objectEncoder.createList(Key.DYNAMIC_USES),
@ -64,14 +66,10 @@ class ImpactSerializer implements WorldImpactVisitor {
@override
void visitStaticUse(StaticUse staticUse) {
if (staticUse.element.isGenerativeConstructor &&
staticUse.element.enclosingClass.isUnnamedMixinApplication) {
// TODO(johnniwinther): Handle static use of forwarding constructors.
return;
}
ObjectEncoder object = staticUses.createObject();
object.setEnum(Key.KIND, staticUse.kind);
object.setElement(Key.ELEMENT, staticUse.element);
serializeElementReference(
element, Key.ELEMENT, Key.NAME, object, staticUse.element);
}
@override
@ -95,29 +93,35 @@ class DeserializedResolutionImpact extends WorldImpact
final Iterable<TypeUse> typeUses;
DeserializedResolutionImpact(
{this.constSymbolNames,
this.constantLiterals,
this.dynamicUses,
{this.constSymbolNames: const <String>[],
this.constantLiterals: const <ConstantExpression>[],
this.dynamicUses: const <DynamicUse>[],
EnumSet<Feature> features,
this.listLiterals,
this.mapLiterals,
this.staticUses,
this.typeUses})
this.listLiterals: const <ListLiteralUse>[],
this.mapLiterals: const <MapLiteralUse>[],
this.staticUses: const <StaticUse>[],
this.typeUses: const <TypeUse>[]})
: this._features = features;
Iterable<Feature> get features => _features.iterable(Feature.values);
Iterable<Feature> get features {
return _features != null
? _features.iterable(Feature.values)
: const <Feature>[];
}
}
class ImpactDeserializer {
/// Deserializes a [WorldImpact] from [objectDecoder].
static ResolutionImpact deserializeImpact(ObjectDecoder objectDecoder) {
static ResolutionImpact deserializeImpact(
Element element, ObjectDecoder objectDecoder) {
ListDecoder staticUseDecoder = objectDecoder.getList(Key.STATIC_USES);
List<StaticUse> staticUses = <StaticUse>[];
for (int index = 0; index < staticUseDecoder.length; index++) {
ObjectDecoder object = staticUseDecoder.getObject(index);
StaticUseKind kind = object.getEnum(Key.KIND, StaticUseKind.values);
Element element = object.getElement(Key.ELEMENT);
staticUses.add(new StaticUse.internal(element, kind));
Element usedElement =
deserializeElementReference(element, Key.ELEMENT, Key.NAME, object);
staticUses.add(new StaticUse.internal(usedElement, kind));
}
ListDecoder dynamicUseDecoder = objectDecoder.getList(Key.DYNAMIC_USES);

View file

@ -209,13 +209,8 @@ class ResolvedAstSerializer extends Visitor {
visitNode(Node node) {
Element nodeElement = elements[node];
if (nodeElement != null) {
if (nodeElement.enclosingClass != null &&
nodeElement.enclosingClass.isUnnamedMixinApplication) {
// TODO(johnniwinther): Handle references to members of unnamed mixin
// applications.
} else {
getNodeDataEncoder(node).setElement(Key.ELEMENT, nodeElement);
}
serializeElementReference(element, Key.ELEMENT, Key.NAME,
getNodeDataEncoder(node), nodeElement);
}
DartType type = elements.getType(node);
if (type != null) {
@ -544,8 +539,9 @@ class ResolvedAstDeserializer {
ObjectDecoder objectDecoder = dataDecoder.getObject(i);
int id = objectDecoder.getInt(Key.ID);
Node node = nodeList[id];
Element nodeElement =
objectDecoder.getElement(Key.ELEMENT, isOptional: true);
Element nodeElement = deserializeElementReference(
element, Key.ELEMENT, Key.NAME, objectDecoder,
isOptional: true);
if (nodeElement != null) {
elements[node] = nodeElement;
}

View file

@ -4,6 +4,7 @@
library dart2js.serialization.util;
import '../common.dart';
import '../constants/expressions.dart';
import '../dart_types.dart';
import '../elements/elements.dart';
@ -474,3 +475,45 @@ AccessSemantics deserializeAccessSemantics(ObjectDecoder decoder) {
throw new UnsupportedError('Unsupported access kind: $kind');
}
}
/// Serialize a reference from [context] to an [element] which might be a member
/// of an unnamed mixin application. If it is, [element] is by serialized
/// indirectly by name in the [nameKey] of [encoder], otherwise [element] is
/// serialized directly in [elementKey] in [encoder].
void serializeElementReference(Element context, Key elementKey, Key nameKey,
ObjectEncoder encoder, Element element) {
if (element.isGenerativeConstructor &&
element.enclosingClass.isUnnamedMixinApplication) {
assert(invariant(element, element.isConstructor,
message: "Unexpected reference of forwarding constructor "
"${element} from $context."));
encoder.setString(nameKey, element.name);
} else {
encoder.setElement(elementKey, element);
}
}
/// Deserialize a reference from [context] to an [Element] which might be a
/// member of an unnamed mixin application. If it is, the [Element] is by
/// deserialized indirectly by name from [nameKey] in [decoder], otherwise
/// the [Element] is deserialized directly from [elementKey] in [encoder].
Element deserializeElementReference(
Element context, Key elementKey, Key nameKey, ObjectDecoder decoder,
{bool isOptional: false}) {
Element element = decoder.getElement(elementKey, isOptional: true);
if (element == null) {
String elementName = decoder.getString(nameKey, isOptional: isOptional);
if (elementName == null) {
return null;
}
assert(invariant(NO_LOCATION_SPANNABLE, context.isConstructor,
message: "Unexpected reference of forwarding constructor "
"'${elementName}' from $context."));
ClassElement superclass = context.enclosingClass.superclass;
element = superclass.lookupConstructor(elementName);
assert(invariant(NO_LOCATION_SPANNABLE, element != null,
message: "Unresolved reference of forwarding constructor "
"'${elementName}' from $context."));
}
return element;
}

View file

@ -46,6 +46,16 @@ class SerializationTask extends CompilerTask implements LibraryDeserializer {
return deserializer != null && deserializer.isDeserialized(element);
}
bool hasResolutionImpact(Element element) {
return deserializer != null && deserializer.hasResolutionImpact(element);
}
ResolutionImpact getResolutionImpact(Element element) {
return deserializer != null
? deserializer.getResolutionImpact(element)
: null;
}
/// Creates the [ResolutionWorkItem] for the deserialized [element].
ResolutionWorkItem createResolutionWorkItem(
Element element, ItemCompilationContext context) {
@ -94,6 +104,7 @@ abstract class DeserializerSystem {
bool isDeserialized(Element element);
bool hasResolvedAst(Element element);
ResolvedAst getResolvedAst(Element element);
bool hasResolutionImpact(Element element);
ResolutionImpact getResolutionImpact(Element element);
WorldImpact computeWorldImpact(Element element);
}

View file

@ -23,7 +23,9 @@ import 'package:compiler/src/serialization/resolved_ast_serialization.dart';
import 'package:compiler/src/serialization/serialization.dart';
import 'package:compiler/src/serialization/task.dart';
import 'package:compiler/src/tokens/token.dart';
import 'package:compiler/src/universe/call_structure.dart';
import 'package:compiler/src/universe/world_impact.dart';
import 'package:compiler/src/universe/use.dart';
import 'memory_compiler.dart';
@ -142,7 +144,7 @@ class ResolutionImpactSerializer extends SerializerPlugin {
if (resolution.hasBeenResolved(element)) {
ResolutionImpact impact = resolution.getResolutionImpact(element);
ObjectEncoder encoder = createEncoder(WORLD_IMPACT_TAG);
new ImpactSerializer(encoder).serialize(impact);
new ImpactSerializer(element, encoder).serialize(impact);
}
}
}
@ -154,7 +156,8 @@ class ResolutionImpactDeserializer extends DeserializerPlugin {
void onElement(Element element, ObjectDecoder getDecoder(String tag)) {
ObjectDecoder decoder = getDecoder(WORLD_IMPACT_TAG);
if (decoder != null) {
impactMap[element] = ImpactDeserializer.deserializeImpact(decoder);
impactMap[element] =
ImpactDeserializer.deserializeImpact(element, decoder);
}
}
}
@ -220,8 +223,33 @@ class _DeserializerSystem extends DeserializerSystem {
return null;
}
@override
bool hasResolutionImpact(Element element) {
if (element.isConstructor &&
element.enclosingClass.isUnnamedMixinApplication) {
return true;
}
return _resolutionImpactDeserializer.impactMap.containsKey(element);
}
@override
ResolutionImpact getResolutionImpact(Element element) {
if (element.isConstructor &&
element.enclosingClass.isUnnamedMixinApplication) {
ClassElement superclass = element.enclosingClass.superclass;
ConstructorElement superclassConstructor =
superclass.lookupConstructor(element.name);
assert(invariant(element, superclassConstructor != null,
message: "Superclass constructor '${element.name}' called from "
"${element} not found in ${superclass}."));
// TODO(johnniwinther): Compute callStructure. Currently not used.
CallStructure callStructure;
return _resolutionImpactDeserializer.impactMap.putIfAbsent(element, () {
return new DeserializedResolutionImpact(
staticUses: <StaticUse>[new StaticUse.superConstructorInvoke(
superclassConstructor, callStructure)]);
});
}
return _resolutionImpactDeserializer.impactMap[element];
}

View file

@ -294,9 +294,20 @@ void checkLoadedLibraryMembers(
if (member1.isClass && member2.isClass) {
ClassElement class1 = member1;
ClassElement class2 = member2;
if (!class1.isResolved) return;
class1.forEachLocalMember((m1) {
checkMembers(m1, class2.lookupLocalMember(m1.name));
checkMembers(m1, class2.localLookup(m1.name));
});
ClassElement superclass1 = class1.superclass;
ClassElement superclass2 = class2.superclass;
while (superclass1 != null && superclass1.isUnnamedMixinApplication) {
for (ConstructorElement c1 in superclass1.constructors) {
checkMembers(c1, superclass2.lookupConstructor(c1.name));
}
superclass1 = superclass1.superclass;
superclass2 = superclass2.superclass;
}
return;
}
@ -305,7 +316,7 @@ void checkLoadedLibraryMembers(
}
if (member2 == null) {
return;
throw 'Missing member for ${member1}';
}
if (areElementsEquivalent(member1, member2)) {
@ -348,14 +359,20 @@ 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);
ResolutionImpact impact2 = compiler2.resolution.getResolutionImpact(member2);
if (impact1 == null || impact2 == null) return;
if (impact1 == null && impact2 == null) return;
if (verbose) {
print('Checking impacts for $member1 vs $member2');
}
if (impact1 == null) {
throw 'Missing impact for $member1. $member2 has $impact2';
}
if (impact2 == null) {
throw 'Missing impact for $member2. $member1 has $impact1';
}
testResolutionImpactEquivalence(impact1, impact2, const CheckStrategy());
}