blob: 12b4adb41cd2724d3feef0644eaac5c7092e948b [file] [log] [blame]
// 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_system;
import 'dart:async';
import '../common.dart';
import '../common/resolution.dart';
import '../compiler.dart';
import '../elements/resolution_types.dart';
import '../elements/elements.dart';
import '../scanner/scanner.dart';
import '../script.dart';
import '../serialization/impact_serialization.dart';
import '../tokens/token.dart';
import '../universe/call_structure.dart';
import '../universe/use.dart';
import '../universe/world_impact.dart';
import 'modelz.dart';
import 'resolved_ast_serialization.dart';
import 'serialization.dart';
import 'task.dart';
class ResolutionDeserializerSystem extends DeserializerSystem {
final Compiler _compiler;
final Resolution resolution;
final DeserializationContext deserializationContext;
final List<LibraryElement> deserializedLibraries = <LibraryElement>[];
factory ResolutionDeserializerSystem(Compiler compiler,
{bool deserializeCompilationDataForTesting: false}) {
DeserializationContext context = new DeserializationContext(
compiler.reporter, compiler.resolution, compiler.libraryLoader);
DeserializerPlugin backendDeserializer =
compiler.backend.serialization.deserializer;
context.plugins.add(backendDeserializer);
if (compiler.options.resolveOnly && !deserializeCompilationDataForTesting) {
return new ResolutionDeserializerSystem._(
compiler, compiler.resolution, context);
} else {
ResolutionImpactDeserializer resolutionImpactDeserializer =
new ResolutionImpactDeserializer(backendDeserializer);
context.plugins.add(resolutionImpactDeserializer);
ResolvedAstDeserializerPlugin resolvedAstDeserializer =
new ResolvedAstDeserializerPlugin(
compiler.parsingContext, backendDeserializer);
context.plugins.add(resolvedAstDeserializer);
return new CompilationDeserializerSystem._(compiler, compiler.resolution,
context, resolutionImpactDeserializer, resolvedAstDeserializer);
}
}
ResolutionDeserializerSystem._(
this._compiler, this.resolution, this.deserializationContext);
@override
Future<LibraryElement> readLibrary(Uri resolvedUri) {
LibraryElement library = deserializationContext.lookupLibrary(resolvedUri);
if (library != null) {
deserializedLibraries.add(library);
return onReadLibrary(library);
}
return new Future<LibraryElement>.value(library);
}
Future<LibraryElement> onReadLibrary(LibraryElement library) {
return new Future<LibraryElement>.value(library);
}
// TODO(johnniwinther): Remove the need for this method.
@override
bool hasResolvedAst(ExecutableElement element) {
return getResolvedAst(element) != null;
}
@override
ResolvedAst getResolvedAst(ExecutableElement element) => null;
@override
bool hasResolutionImpact(Element element) => true;
@override
ResolutionImpact getResolutionImpact(Element element) {
return const ResolutionImpact();
}
@override
WorldImpact computeWorldImpact(Element element) {
ResolutionImpact resolutionImpact = getResolutionImpact(element);
assert(invariant(element, resolutionImpact != null,
message: 'No impact found for $element (${element.library})'));
if (element is ExecutableElement) {
getResolvedAst(element);
}
if (element.isField && !element.isConst) {
FieldElement field = element;
if (field.isTopLevel || field.isStatic) {
if (field.constant == null) {
// TODO(johnniwinther): Find a cleaner way to do this. Maybe
// `Feature.LAZY_FIELD` of the resolution impact should be used
// instead.
_compiler.backend.constants.registerLazyStatic(element);
}
}
}
return resolution.transformResolutionImpact(element, resolutionImpact);
}
@override
bool isDeserialized(Element element) {
return deserializedLibraries.contains(element.library);
}
}
class CompilationDeserializerSystem extends ResolutionDeserializerSystem {
final ResolutionImpactDeserializer _resolutionImpactDeserializer;
final ResolvedAstDeserializerPlugin _resolvedAstDeserializer;
CompilationDeserializerSystem._(
Compiler compiler,
Resolution resolution,
DeserializationContext deserializationContext,
this._resolutionImpactDeserializer,
this._resolvedAstDeserializer)
: super._(compiler, resolution, deserializationContext);
@override
Future<LibraryElement> onReadLibrary(LibraryElement library) {
return Future.forEach(library.compilationUnits,
(CompilationUnitElement compilationUnit) {
ScriptZ script = compilationUnit.script;
return _compiler.readScript(script.readableUri).then((Script newScript) {
script.file = newScript.file;
script.isSynthesized = newScript.isSynthesized;
_resolvedAstDeserializer.scripts[script.resourceUri] = script;
});
}).then((_) => library);
}
@override
ResolvedAst getResolvedAst(ExecutableElement element) {
return _resolvedAstDeserializer.getResolvedAst(element);
}
@override
bool hasResolutionImpact(Element element) {
if (element.isConstructor &&
element.enclosingClass.isUnnamedMixinApplication) {
return true;
}
return _resolutionImpactDeserializer.hasResolutionImpact(element);
}
@override
ResolutionImpact getResolutionImpact(Element element) {
if (element.isConstructor &&
element.enclosingClass.isUnnamedMixinApplication) {
ConstructorElement constructor = element;
ClassElement superclass = constructor.enclosingClass.superclass;
ConstructorElement superclassConstructor =
superclass.lookupConstructor(constructor.name);
assert(invariant(element, superclassConstructor != null,
message: "Superclass constructor '${constructor.name}' called from "
"${element} not found in ${superclass}."));
// TODO(johnniwinther): Compute callStructure. Currently not used.
CallStructure callStructure;
return _resolutionImpactDeserializer.registerResolutionImpact(constructor,
() {
List<TypeUse> typeUses = <TypeUse>[];
void addCheckedModeCheck(ResolutionDartType type) {
if (!type.isDynamic) {
typeUses.add(new TypeUse.checkedModeCheck(type));
}
}
ResolutionFunctionType type = constructor.type;
// TODO(johnniwinther): Remove this substitution when synthesized
// constructors handle type variables correctly.
type = type.substByContext(constructor.enclosingClass
.asInstanceOf(constructor.enclosingClass));
type.parameterTypes.forEach(addCheckedModeCheck);
type.optionalParameterTypes.forEach(addCheckedModeCheck);
type.namedParameterTypes.forEach(addCheckedModeCheck);
return new DeserializedResolutionImpact(staticUses: <StaticUse>[
new StaticUse.superConstructorInvoke(
superclassConstructor, callStructure)
], typeUses: typeUses);
});
}
return _resolutionImpactDeserializer.getResolutionImpact(element);
}
}
const String WORLD_IMPACT_TAG = 'worldImpact';
class ResolutionImpactSerializer extends SerializerPlugin {
final Resolution resolution;
final SerializerPlugin nativeDataSerializer;
ResolutionImpactSerializer(this.resolution, this.nativeDataSerializer);
@override
void onElement(Element element, ObjectEncoder createEncoder(String tag)) {
if (resolution.hasBeenResolved(element)) {
ResolutionImpact impact = resolution.getResolutionImpact(element);
ObjectEncoder encoder = createEncoder(WORLD_IMPACT_TAG);
new ImpactSerializer(element, encoder, nativeDataSerializer)
.serialize(impact);
}
}
}
class ResolutionImpactDeserializer extends DeserializerPlugin {
Map<Element, ObjectDecoder> _decoderMap = <Element, ObjectDecoder>{};
Map<Element, ResolutionImpact> _impactMap = <Element, ResolutionImpact>{};
final DeserializerPlugin nativeDataDeserializer;
ResolutionImpactDeserializer(this.nativeDataDeserializer);
@override
void onElement(Element element, ObjectDecoder getDecoder(String tag)) {
ObjectDecoder decoder = getDecoder(WORLD_IMPACT_TAG);
if (decoder != null) {
_decoderMap[element] = decoder;
}
}
bool hasResolutionImpact(Element element) {
return _impactMap.containsKey(element) || _decoderMap.containsKey(element);
}
ResolutionImpact registerResolutionImpact(
Element element, ResolutionImpact ifAbsent()) {
return _impactMap.putIfAbsent(element, ifAbsent);
}
ResolutionImpact getResolutionImpact(Element element) {
return registerResolutionImpact(element, () {
ObjectDecoder decoder = _decoderMap[element];
if (decoder != null) {
_decoderMap.remove(element);
return ImpactDeserializer.deserializeImpact(
element, decoder, nativeDataDeserializer);
}
return null;
});
}
}
const String RESOLVED_AST_TAG = 'resolvedAst';
class ResolvedAstSerializerPlugin extends SerializerPlugin {
final Resolution resolution;
final SerializerPlugin nativeDataSerializer;
ResolvedAstSerializerPlugin(this.resolution, this.nativeDataSerializer);
@override
void onElement(Element element, ObjectEncoder createEncoder(String tag)) {
assert(invariant(element, element.isDeclaration,
message: "Element $element must be the declaration"));
if (element.isError) return;
if (element is MemberElement) {
assert(invariant(element, resolution.hasResolvedAst(element),
message: "Element $element must have a resolved ast"));
ResolvedAst resolvedAst = resolution.getResolvedAst(element);
ObjectEncoder objectEncoder = createEncoder(RESOLVED_AST_TAG);
new ResolvedAstSerializer(
objectEncoder, resolvedAst, nativeDataSerializer)
.serialize();
}
}
}
class ResolvedAstDeserializerPlugin extends DeserializerPlugin {
final ParsingContext parsingContext;
final DeserializerPlugin nativeDataDeserializer;
final Map<Uri, Script> scripts = <Uri, Script>{};
Map<MemberElement, ObjectDecoder> _decoderMap =
<MemberElement, ObjectDecoder>{};
Map<Uri, Token> beginTokenMap = <Uri, Token>{};
ResolvedAstDeserializerPlugin(
this.parsingContext, this.nativeDataDeserializer);
bool hasResolvedAst(ExecutableElement element) {
return getResolvedAst(element) != null;
}
ResolvedAst getResolvedAst(ExecutableElement element) {
if (element.hasResolvedAst) {
return element.resolvedAst;
}
ObjectDecoder decoder = _decoderMap[element.memberContext];
if (decoder != null) {
ResolvedAstDeserializer.deserialize(element.memberContext, decoder,
parsingContext, findToken, nativeDataDeserializer);
_decoderMap.remove(element);
assert(invariant(element, element.hasResolvedAst,
message: "ResolvedAst not computed for $element."));
return element.resolvedAst;
}
return null;
}
Token findToken(Uri uri, int offset) {
Token beginToken = beginTokenMap.putIfAbsent(uri, () {
Script script = scripts[uri];
if (script == null) {
parsingContext.reporter.internalError(NO_LOCATION_SPANNABLE,
'No source file found for $uri in:\n ${scripts.keys.join('\n ')}');
}
if (script.isSynthesized) return null;
return new Scanner(script.file).tokenize();
});
if (beginToken == null) return null;
return ResolvedAstDeserializer.findTokenInStream(beginToken, offset);
}
@override
void onElement(Element element, ObjectDecoder getDecoder(String tag)) {
ObjectDecoder decoder = getDecoder(RESOLVED_AST_TAG);
if (decoder != null) {
_decoderMap[element] = decoder;
}
}
}