blob: 095915bd8fd0cdd9459a8160ada0ab448c2f882f [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_helper;
import 'dart:async';
import 'dart:io';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/common.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/elements/elements.dart';
import 'package:compiler/src/io/source_file.dart';
import 'package:compiler/src/scanner/scanner.dart';
import 'package:compiler/src/script.dart';
import 'package:compiler/src/serialization/impact_serialization.dart';
import 'package:compiler/src/serialization/json_serializer.dart';
import 'package:compiler/src/serialization/modelz.dart';
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';
class Arguments {
final String filename;
final bool loadSerializedData;
final bool saveSerializedData;
final String serializedDataFileName;
final bool verbose;
const Arguments({
this.filename,
this.loadSerializedData: false,
this.saveSerializedData: false,
this.serializedDataFileName: 'out.data',
this.verbose: false});
factory Arguments.from(List<String> arguments) {
String filename;
for (String arg in arguments) {
if (!arg.startsWith('-')) {
filename = arg;
}
}
bool verbose = arguments.contains('-v');
bool loadSerializedData = arguments.contains('-l');
bool saveSerializedData = arguments.contains('-s');
return new Arguments(
filename: filename,
verbose: verbose,
loadSerializedData: loadSerializedData,
saveSerializedData: saveSerializedData);
}
}
Future<String> serializeDartCore(
{Arguments arguments: const Arguments(),
bool serializeResolvedAst: false}) async {
print('------------------------------------------------------------------');
print('serialize dart:core');
print('------------------------------------------------------------------');
String serializedData;
if (arguments.loadSerializedData) {
File file = new File(arguments.serializedDataFileName);
if (file.existsSync()) {
print('Loading data from $file');
serializedData = file.readAsStringSync();
}
}
if (serializedData == null) {
Compiler compiler = compilerFor(
options: [Flags.analyzeAll]);
compiler.serialization.supportSerialization = true;
await compiler.run(Uris.dart_core);
serializedData = serialize(
compiler,
compiler.libraryLoader.libraries,
serializeResolvedAst: serializeResolvedAst)
.toText(const JsonSerializationEncoder());
if (arguments.saveSerializedData) {
File file = new File(arguments.serializedDataFileName);
print('Saving data to $file');
file.writeAsStringSync(serializedData);
}
}
return serializedData;
}
Serializer serialize(
Compiler compiler,
Iterable<LibraryElement> libraries,
{bool serializeResolvedAst: false}) {
assert(compiler.serialization.supportSerialization);
Serializer serializer = new Serializer();
SerializerPlugin backendSerializer =
compiler.backend.serialization.serializer;
serializer.plugins.add(backendSerializer);
serializer.plugins.add(new ResolutionImpactSerializer(
compiler.resolution, backendSerializer));
if (serializeResolvedAst) {
serializer.plugins.add(new ResolvedAstSerializerPlugin(
compiler.resolution, backendSerializer));
}
for (LibraryElement library in libraries) {
serializer.serialize(library);
}
return serializer;
}
void deserialize(Compiler compiler,
String serializedData,
{bool deserializeResolvedAst: false}) {
Deserializer deserializer = new Deserializer.fromText(
new DeserializationContext(),
serializedData,
const JsonSerializationDecoder());
deserializer.plugins.add(compiler.backend.serialization.deserializer);
compiler.serialization.deserializer =
new _DeserializerSystem(
compiler,
deserializer,
compiler.backend.impactTransformer,
deserializeResolvedAst: deserializeResolvedAst);
}
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, 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) {
impactMap[element] =
ImpactDeserializer.deserializeImpact(
element, decoder, nativeDataDeserializer);
}
}
}
class _DeserializerSystem extends DeserializerSystem {
final Compiler _compiler;
final Deserializer _deserializer;
final List<LibraryElement> deserializedLibraries = <LibraryElement>[];
final ResolutionImpactDeserializer _resolutionImpactDeserializer;
final ResolvedAstDeserializerPlugin _resolvedAstDeserializer;
final ImpactTransformer _impactTransformer;
final bool deserializeResolvedAst;
factory _DeserializerSystem(
Compiler compiler,
Deserializer deserializer,
ImpactTransformer impactTransformer,
{bool deserializeResolvedAst: false}) {
List<DeserializerPlugin> plugins = <DeserializerPlugin>[];
DeserializerPlugin backendDeserializer =
compiler.backend.serialization.deserializer;
deserializer.plugins.add(backendDeserializer);
ResolutionImpactDeserializer resolutionImpactDeserializer =
new ResolutionImpactDeserializer(backendDeserializer);
deserializer.plugins.add(resolutionImpactDeserializer);
ResolvedAstDeserializerPlugin resolvedAstDeserializer;
if (deserializeResolvedAst) {
resolvedAstDeserializer = new ResolvedAstDeserializerPlugin(
compiler.parsingContext, backendDeserializer);
deserializer.plugins.add(resolvedAstDeserializer);
}
return new _DeserializerSystem._(
compiler,
deserializer,
impactTransformer,
resolutionImpactDeserializer,
resolvedAstDeserializer,
deserializeResolvedAst: deserializeResolvedAst);
}
_DeserializerSystem._(
this._compiler,
this._deserializer,
this._impactTransformer,
this._resolutionImpactDeserializer,
this._resolvedAstDeserializer,
{this.deserializeResolvedAst: false});
@override
Future<LibraryElement> readLibrary(Uri resolvedUri) {
LibraryElement library = _deserializer.lookupLibrary(resolvedUri);
if (library != null) {
deserializedLibraries.add(library);
if (deserializeResolvedAst) {
return Future.forEach(library.compilationUnits,
(CompilationUnitElement compilationUnit) {
ScriptZ script = compilationUnit.script;
return _compiler.readScript(script.readableUri)
.then((Script newScript) {
script.file = newScript.file;
_resolvedAstDeserializer.sourceFiles[script.resourceUri] =
newScript.file;
});
}).then((_) => library);
}
}
return new Future<LibraryElement>.value(library);
}
@override
bool hasResolvedAst(ExecutableElement element) {
if (_resolvedAstDeserializer != null) {
return _resolvedAstDeserializer.hasResolvedAst(element);
}
return false;
}
@override
ResolvedAst getResolvedAst(ExecutableElement element) {
if (_resolvedAstDeserializer != null) {
return _resolvedAstDeserializer.getResolvedAst(element);
}
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];
}
@override
WorldImpact computeWorldImpact(Element element) {
ResolutionImpact resolutionImpact = getResolutionImpact(element);
assert(invariant(element, resolutionImpact != null,
message: 'No impact found for $element (${element.library})'));
return _impactTransformer.transformResolutionImpact(resolutionImpact);
}
@override
bool isDeserialized(Element element) {
return deserializedLibraries.contains(element.library);
}
}
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 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, SourceFile> sourceFiles = <Uri, SourceFile>{};
Map<ExecutableElement, ResolvedAst> _resolvedAstMap =
<ExecutableElement, ResolvedAst>{};
Map<MemberElement, ObjectDecoder> _decoderMap =
<MemberElement, ObjectDecoder>{};
Map<Uri, Token> beginTokenMap = <Uri, Token>{};
ResolvedAstDeserializerPlugin(
this.parsingContext, this.nativeDataDeserializer);
bool hasResolvedAst(ExecutableElement element) {
return _resolvedAstMap.containsKey(element) ||
_decoderMap.containsKey(element.memberContext);
}
ResolvedAst getResolvedAst(ExecutableElement element) {
ResolvedAst resolvedAst = _resolvedAstMap[element];
if (resolvedAst == null) {
ObjectDecoder decoder = _decoderMap[element.memberContext];
if (decoder != null) {
ResolvedAstDeserializer.deserialize(
element.memberContext, decoder, parsingContext, findToken,
nativeDataDeserializer,
_resolvedAstMap);
_decoderMap.remove(element);
resolvedAst = _resolvedAstMap[element];
}
}
return resolvedAst;
}
Token findToken(Uri uri, int offset) {
Token beginToken = beginTokenMap.putIfAbsent(uri, () {
SourceFile sourceFile = sourceFiles[uri];
if (sourceFile == null) {
throw 'No source file found for $uri in:\n '
'${sourceFiles.keys.join('\n ')}';
}
return new Scanner(sourceFile).tokenize();
});
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;
}
}
}