| // 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 '../dart_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(DartType type) { |
| if (!type.isDynamic) { |
| typeUses.add(new TypeUse.checkedModeCheck(type)); |
| } |
| } |
| |
| FunctionType 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; |
| } |
| } |
| } |