From fefa2188ce4957b16ec6c020f710ebd090fbc07c Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Mon, 11 Apr 2016 11:16:10 +0200 Subject: [PATCH] Serialize TreeElements R=sigmund@google.com Review URL: https://codereview.chromium.org/1873573004 . --- pkg/compiler/lib/src/common/resolution.dart | 7 + pkg/compiler/lib/src/compiler.dart | 19 + pkg/compiler/lib/src/library_loader.dart | 79 +-- .../serialization/constant_serialization.dart | 3 +- .../lib/src/serialization/equivalence.dart | 212 +++++++- .../serialization/impact_serialization.dart | 29 +- pkg/compiler/lib/src/serialization/keys.dart | 9 + .../lib/src/serialization/modelz.dart | 4 +- .../resolved_ast_serialization.dart | 422 +++++++++++++++ .../src/serialization/serialization_util.dart | 481 ++++++++++++++++++ pkg/compiler/lib/src/serialization/task.dart | 10 +- .../dart2js/serialization_helper.dart | 137 ++++- .../serialization_resolved_ast_test.dart | 92 ++++ 13 files changed, 1419 insertions(+), 85 deletions(-) create mode 100644 pkg/compiler/lib/src/serialization/resolved_ast_serialization.dart create mode 100644 pkg/compiler/lib/src/serialization/serialization_util.dart create mode 100644 tests/compiler/dart2js/serialization_resolved_ast_test.dart diff --git a/pkg/compiler/lib/src/common/resolution.dart b/pkg/compiler/lib/src/common/resolution.dart index b55e195814d..42f961730ba 100644 --- a/pkg/compiler/lib/src/common/resolution.dart +++ b/pkg/compiler/lib/src/common/resolution.dart @@ -20,6 +20,7 @@ import '../elements/elements.dart' LocalFunctionElement, MetadataAnnotation, MethodElement, + ResolvedAst, TypedefElement, TypeVariableElement; import '../enqueue.dart' show ResolutionEnqueuer; @@ -207,6 +208,12 @@ abstract class Resolution { ResolutionWorkItem createWorkItem( Element element, ItemCompilationContext compilationContext); + /// Returns `true` if [element] as a fully computed [ResolvedAst]. + bool hasResolvedAst(Element element); + + /// Returns the `ResolvedAst` for the [element]. + ResolvedAst getResolvedAst(Element element); + /// Returns `true` if the [ResolutionImpact] for [element] is cached. bool hasResolutionImpact(Element element); diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index 3d03876821b..1b01849c955 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart @@ -1893,6 +1893,25 @@ class _CompilerResolution implements Resolution { return compiler.resolver.resolveTypeAnnotation(element, node); } + @override + bool hasResolvedAst(Element element) { + return element is AstElement && + hasBeenResolved(element) && + element.hasResolvedAst; + } + + @override + ResolvedAst getResolvedAst(Element element) { + if (hasResolvedAst(element)) { + AstElement astElement = element; + return astElement.resolvedAst; + } + assert(invariant(element, hasResolvedAst(element), + message: "ResolvedAst not available for $element.")); + return null; + } + + @override bool hasResolutionImpact(Element element) { return _resolutionImpactCache.containsKey(element); diff --git a/pkg/compiler/lib/src/library_loader.dart b/pkg/compiler/lib/src/library_loader.dart index 77d18aa9565..b666bfa8955 100644 --- a/pkg/compiler/lib/src/library_loader.dart +++ b/pkg/compiler/lib/src/library_loader.dart @@ -634,46 +634,49 @@ class _LibraryLoaderTask extends CompilerTask implements LibraryLoaderTask { if (library != null) { return new Future.value(library); } - library = deserializer.readLibrary(resolvedUri); - if (library != null) { - return loadDeserializedLibrary(handler, library); - } - return reporter.withCurrentElement(importingLibrary, () { - return _readScript(node, readableUri, resolvedUri).then((Script script) { - if (script == null) return null; - LibraryElement element = - createLibrarySync(handler, script, resolvedUri); - CompilationUnitElementX compilationUnit = element.entryCompilationUnit; - if (compilationUnit.partTag != null) { - if (skipFileWithPartOfTag) { - // TODO(johnniwinther): Avoid calling [listener.onLibraryCreated] - // for this library. - libraryCanonicalUriMap.remove(resolvedUri); - return null; + return deserializer.readLibrary(resolvedUri).then((LibraryElement library) { + if (library != null) { + return loadDeserializedLibrary(handler, library); + } + return reporter.withCurrentElement(importingLibrary, () { + return _readScript(node, readableUri, resolvedUri) + .then((Script script) { + if (script == null) return null; + LibraryElement element = + createLibrarySync(handler, script, resolvedUri); + CompilationUnitElementX compilationUnit = + element.entryCompilationUnit; + if (compilationUnit.partTag != null) { + if (skipFileWithPartOfTag) { + // TODO(johnniwinther): Avoid calling [listener.onLibraryCreated] + // for this library. + libraryCanonicalUriMap.remove(resolvedUri); + return null; + } + if (importingLibrary == null) { + DiagnosticMessage error = reporter.withCurrentElement( + compilationUnit, + () => reporter.createMessage( + compilationUnit.partTag, MessageKind.MAIN_HAS_PART_OF)); + reporter.reportError(error); + } else { + DiagnosticMessage error = reporter.withCurrentElement( + compilationUnit, + () => reporter.createMessage( + compilationUnit.partTag, MessageKind.IMPORT_PART_OF)); + DiagnosticMessage info = reporter.withCurrentElement( + importingLibrary, + () => reporter.createMessage( + node, MessageKind.IMPORT_PART_OF_HERE)); + reporter.reportError(error, [info]); + } } - if (importingLibrary == null) { - DiagnosticMessage error = reporter.withCurrentElement( - compilationUnit, - () => reporter.createMessage( - compilationUnit.partTag, MessageKind.MAIN_HAS_PART_OF)); - reporter.reportError(error); - } else { - DiagnosticMessage error = reporter.withCurrentElement( - compilationUnit, - () => reporter.createMessage( - compilationUnit.partTag, MessageKind.IMPORT_PART_OF)); - DiagnosticMessage info = reporter.withCurrentElement( - importingLibrary, - () => reporter.createMessage( - node, MessageKind.IMPORT_PART_OF_HERE)); - reporter.reportError(error, [info]); - } - } - return processLibraryTags(handler, element).then((_) { - reporter.withCurrentElement(element, () { - handler.registerLibraryExports(element); + return processLibraryTags(handler, element).then((_) { + reporter.withCurrentElement(element, () { + handler.registerLibraryExports(element); + }); + return element; }); - return element; }); }); }); diff --git a/pkg/compiler/lib/src/serialization/constant_serialization.dart b/pkg/compiler/lib/src/serialization/constant_serialization.dart index 6a81082535d..fae88188fcb 100644 --- a/pkg/compiler/lib/src/serialization/constant_serialization.dart +++ b/pkg/compiler/lib/src/serialization/constant_serialization.dart @@ -105,8 +105,7 @@ class ConstantSerializer @override void visitSymbol(SymbolConstantExpression exp, ObjectEncoder encoder) { - throw new UnsupportedError( - "ConstantSerializer.visitSymbol: ${exp.getText()}"); + encoder.setString(Key.NAME, exp.name); } @override diff --git a/pkg/compiler/lib/src/serialization/equivalence.dart b/pkg/compiler/lib/src/serialization/equivalence.dart index 5df0a19cae4..be281214474 100644 --- a/pkg/compiler/lib/src/serialization/equivalence.dart +++ b/pkg/compiler/lib/src/serialization/equivalence.dart @@ -11,8 +11,13 @@ import '../constants/expressions.dart'; import '../dart_types.dart'; import '../elements/elements.dart'; import '../elements/visitor.dart'; +import '../resolution/send_structure.dart'; +import '../resolution/tree_elements.dart'; +import '../tokens/token.dart'; +import '../tree/nodes.dart'; import '../universe/selector.dart'; import '../universe/use.dart'; +import 'resolved_ast_serialization.dart'; /// Equality based equivalence function. bool equality(a, b) => a == b; @@ -90,6 +95,8 @@ bool areConstantListsEquivalent( /// Returns `true` if the selectors [a] and [b] are equivalent. bool areSelectorsEquivalent(Selector a, Selector b) { + if (identical(a, b)) return true; + if (a == null || b == null) return false; return a.kind == b.kind && a.callStructure == b.callStructure && areNamesEquivalent(a.memberName, b.memberName); @@ -131,14 +138,33 @@ bool areMapLiteralUsesEquivalent(MapLiteralUse a, MapLiteralUse b) { a.isEmpty == b.isEmpty; } +/// Returns `true` if the send structures [a] and [b] are equivalent. +bool areSendStructuresEquivalent(SendStructure a, SendStructure b) { + if (identical(a, b)) return true; + if (a == null || b == null) return false; + if (a.kind != b.kind) return false; + // TODO(johnniwinther): Compute a deep equivalence. + return true; +} + +/// Returns `true` if the new structures [a] and [b] are equivalent. +bool areNewStructuresEquivalent(NewStructure a, NewStructure b) { + if (identical(a, b)) return true; + if (a == null || b == null) return false; + if (a.kind != b.kind) return false; + // TODO(johnniwinther): Compute a deep equivalence. + return true; +} + /// Strategy for testing equivalence. /// /// Use this strategy to determine equivalence without failing on inequivalence. class TestStrategy { const TestStrategy(); - bool test(var object1, var object2, String property, var value1, var value2) { - return value1 == value2; + bool test(var object1, var object2, String property, var value1, var value2, + [bool equivalence(a, b) = equality]) { + return equivalence(value1, value2); } bool testLists( @@ -483,8 +509,9 @@ class ConstantEquivalence @override bool visitSymbol( SymbolConstantExpression exp1, SymbolConstantExpression exp2) { - // TODO: implement visitSymbol - return true; + // TODO(johnniwinther): Handle private names. Currently not even supported + // in resolution. + return strategy.test(exp1, exp2, 'name', exp1.name, exp2.name); } @override @@ -572,7 +599,7 @@ class ConstantEquivalence @override bool visitDeferred( DeferredConstantExpression exp1, DeferredConstantExpression exp2) { - // TODO: implement visitDeferred + // TODO(johnniwinther): Implement this. return true; } } @@ -603,3 +630,178 @@ bool testResolutionImpactEquivalence( strategy.testSets(impact1, impact2, 'typeUses', impact1.typeUses, impact2.typeUses, areTypeUsesEquivalent); } + +/// Tests the equivalence of [resolvedAst1] and [resolvedAst2] using [strategy]. +bool testResolvedAstEquivalence( + ResolvedAst resolvedAst1, ResolvedAst resolvedAst2, + [TestStrategy strategy = const TestStrategy()]) { + return strategy.testElements(resolvedAst1, resolvedAst2, 'element', + resolvedAst1.element, resolvedAst2.element) && + // Compute AST equivalence by structural comparison. + strategy.test( + resolvedAst1, + resolvedAst2, + 'node', + resolvedAst1.node.toDebugString(), + resolvedAst2.node.toDebugString()) && + testTreeElementsEquivalence(resolvedAst1, resolvedAst2, strategy); +} + +/// Tests the equivalence of the data stored in the [TreeElements] of +/// [resolvedAst1] and [resolvedAst2] using [strategy]. +bool testTreeElementsEquivalence( + ResolvedAst resolvedAst1, ResolvedAst resolvedAst2, + [TestStrategy strategy = const TestStrategy()]) { + AstIndexComputer indices1 = new AstIndexComputer(); + resolvedAst1.node.accept(indices1); + AstIndexComputer indices2 = new AstIndexComputer(); + resolvedAst2.node.accept(indices2); + + TreeElements elements1 = resolvedAst1.elements; + TreeElements elements2 = resolvedAst2.elements; + + TreeElementsEquivalenceVisitor visitor = new TreeElementsEquivalenceVisitor( + indices1, indices2, elements1, elements2, strategy); + resolvedAst1.node.accept(visitor); + return visitor.success; +} + +/// Visitor that checks the equivalence of [TreeElements] data. +class TreeElementsEquivalenceVisitor extends Visitor { + final TestStrategy strategy; + final AstIndexComputer indices1; + final AstIndexComputer indices2; + final TreeElements elements1; + final TreeElements elements2; + bool success = true; + + TreeElementsEquivalenceVisitor( + this.indices1, this.indices2, this.elements1, this.elements2, + [this.strategy = const TestStrategy()]); + + visitNode(Node node1) { + if (!success) return; + int index = indices1.nodeIndices[node1]; + Node node2 = indices2.nodeList[index]; + success = strategy.testElements( + node1, node2, '[$index]', elements1[node1], elements2[node2]) && + strategy.testTypes(node1, node2, 'getType($index)', + elements1.getType(node1), elements2.getType(node2)) && + strategy.test( + node1, + node2, + 'getSelector($index)', + elements1.getSelector(node1), + elements2.getSelector(node2), + areSelectorsEquivalent) && + strategy.testConstants(node1, node2, 'getConstant($index)', + elements1.getConstant(node1), elements2.getConstant(node2)) && + strategy.testTypes(node1, node2, 'typesCache[$index]', + elements1.typesCache[node1], elements2.typesCache[node2]); + + node1.visitChildren(this); + } + + @override + visitSend(Send node1) { + visitExpression(node1); + if (!success) return; + int index = indices1.nodeIndices[node1]; + Send node2 = indices2.nodeList[index]; + success = strategy.test(node1, node2, 'isTypeLiteral($index)', + elements1.isTypeLiteral(node1), elements2.isTypeLiteral(node2)) && + strategy.testTypes( + node1, + node2, + 'getTypeLiteralType($index)', + elements1.getTypeLiteralType(node1), + elements2.getTypeLiteralType(node2)) && + strategy.test( + node1, + node2, + 'getSendStructure($index)', + elements1.getSendStructure(node1), + elements2.getSendStructure(node2), + areSendStructuresEquivalent); + } + + @override + visitNewExpression(NewExpression node1) { + visitExpression(node1); + if (!success) return; + int index = indices1.nodeIndices[node1]; + NewExpression node2 = indices2.nodeList[index]; + success = strategy.test( + node1, + node2, + 'getNewStructure($index)', + elements1.getNewStructure(node1), + elements2.getNewStructure(node2), + areNewStructuresEquivalent); + } + + @override + visitSendSet(SendSet node1) { + visitSend(node1); + if (!success) return; + int index = indices1.nodeIndices[node1]; + SendSet node2 = indices2.nodeList[index]; + success = strategy.test( + node1, + node2, + 'getGetterSelectorInComplexSendSet($index)', + elements1.getGetterSelectorInComplexSendSet(node1), + elements2.getGetterSelectorInComplexSendSet(node2), + areSelectorsEquivalent) && + strategy.test( + node1, + node2, + 'getOperatorSelectorInComplexSendSet($index)', + elements1.getOperatorSelectorInComplexSendSet(node1), + elements2.getOperatorSelectorInComplexSendSet(node2), + areSelectorsEquivalent); + } + + @override + visitFunctionExpression(FunctionExpression node1) { + visitNode(node1); + if (!success) return; + int index = indices1.nodeIndices[node1]; + FunctionExpression node2 = indices2.nodeList[index]; + if (elements1[node1] is! FunctionElement) { + // [getFunctionDefinition] is currently stored in [] which doesn't always + // contain a [FunctionElement]. + return; + } + success = strategy.testElements( + node1, + node2, + 'getFunctionDefinition($index)', + elements1.getFunctionDefinition(node1), + elements2.getFunctionDefinition(node2)); + } + + @override + visitForIn(ForIn node1) { + visitLoop(node1); + if (!success) return; + int index = indices1.nodeIndices[node1]; + ForIn node2 = indices2.nodeList[index]; + success = strategy.testElements(node1, node2, 'getForInVariable($index)', + elements1.getForInVariable(node1), elements2.getForInVariable(node2)); + } + + @override + visitRedirectingFactoryBody(RedirectingFactoryBody node1) { + visitStatement(node1); + if (!success) return; + int index = indices1.nodeIndices[node1]; + RedirectingFactoryBody node2 = indices2.nodeList[index]; + success = strategy.testElements( + node1, + node2, + 'getRedirectingTargetConstructor($index)', + elements1.getRedirectingTargetConstructor(node1), + elements2.getRedirectingTargetConstructor(node2)); + } +} diff --git a/pkg/compiler/lib/src/serialization/impact_serialization.dart b/pkg/compiler/lib/src/serialization/impact_serialization.dart index 8ab13073e34..af3cf5d75fe 100644 --- a/pkg/compiler/lib/src/serialization/impact_serialization.dart +++ b/pkg/compiler/lib/src/serialization/impact_serialization.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// 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. @@ -16,6 +16,7 @@ import '../util/enumset.dart'; import 'keys.dart'; import 'serialization.dart'; +import 'serialization_util.dart'; /// Visitor that serializes a [ResolutionImpact] object using an /// [ObjectEncoder]. @@ -60,18 +61,7 @@ class ImpactSerializer implements WorldImpactVisitor { @override void visitDynamicUse(DynamicUse dynamicUse) { ObjectEncoder object = dynamicUses.createObject(); - object.setEnum(Key.KIND, dynamicUse.selector.kind); - - object.setInt( - Key.ARGUMENTS, dynamicUse.selector.callStructure.argumentCount); - object.setStrings( - Key.NAMED_ARGUMENTS, dynamicUse.selector.callStructure.namedArguments); - - object.setString(Key.NAME, dynamicUse.selector.memberName.text); - object.setBool(Key.IS_SETTER, dynamicUse.selector.memberName.isSetter); - if (dynamicUse.selector.memberName.library != null) { - object.setElement(Key.LIBRARY, dynamicUse.selector.memberName.library); - } + serializeSelector(dynamicUse.selector, object); } @override @@ -136,17 +126,8 @@ class ImpactDeserializer { List dynamicUses = []; for (int index = 0; index < dynamicUseDecoder.length; index++) { ObjectDecoder object = dynamicUseDecoder.getObject(index); - SelectorKind kind = object.getEnum(Key.KIND, SelectorKind.values); - int argumentCount = object.getInt(Key.ARGUMENTS); - List namedArguments = - object.getStrings(Key.NAMED_ARGUMENTS, isOptional: true); - String name = object.getString(Key.NAME); - bool isSetter = object.getBool(Key.IS_SETTER); - LibraryElement library = object.getElement(Key.LIBRARY, isOptional: true); - dynamicUses.add(new DynamicUse( - new Selector(kind, new Name(name, library, isSetter: isSetter), - new CallStructure(argumentCount, namedArguments)), - null)); + Selector selector = deserializeSelector(object); + dynamicUses.add(new DynamicUse(selector, null)); } ListDecoder typeUseDecoder = objectDecoder.getList(Key.TYPE_USES); diff --git a/pkg/compiler/lib/src/serialization/keys.dart b/pkg/compiler/lib/src/serialization/keys.dart index 7758be67bdb..d74c8209ce9 100644 --- a/pkg/compiler/lib/src/serialization/keys.dart +++ b/pkg/compiler/lib/src/serialization/keys.dart @@ -9,6 +9,7 @@ class Key { static const Key ALIAS = const Key('alias'); static const Key ARGUMENTS = const Key('arguments'); static const Key BOUND = const Key('bound'); + static const Key CACHED_TYPE = const Key('cachedType'); static const Key CALL_STRUCTURE = const Key('callStructure'); static const Key CALL_TYPE = const Key('callType'); static const Key CANONICAL_URI = const Key('canonicalUri'); @@ -34,6 +35,8 @@ class Key { static const Key FIELD = const Key('field'); static const Key FIELDS = const Key('fields'); static const Key FUNCTION = const Key('function'); + static const Key GET_OR_SET = const Key('getOrSet'); + static const Key GETTER = const Key('getter'); static const Key ID = const Key('id'); static const Key IMPACTS = const Key('impacts'); static const Key IMPORT = const Key('import'); @@ -71,6 +74,7 @@ class Key { static const Key NAMED_ARGUMENTS = const Key('named-arguments'); static const Key NAMED_PARAMETERS = const Key('named-parameters'); static const Key NAMED_PARAMETER_TYPES = const Key('named-parameter-types'); + static const Key NEW_STRUCTURE = const Key('newStructure'); static const Key OFFSET = const Key('offset'); static const Key OPERATOR = const Key('operator'); static const Key OPTIONAL_PARAMETER_TYPES = @@ -80,7 +84,12 @@ class Key { static const Key PREFIX = const Key('prefix'); static const Key RETURN_TYPE = const Key('return-type'); static const Key RIGHT = const Key('right'); + static const Key SELECTOR = const Key('selector'); + static const Key SEMANTICS = const Key('semantics'); + static const Key SEND_STRUCTURE = const Key('sendStructure'); + static const Key SETTER = const Key('setter'); static const Key STATIC_USES = const Key('static-uses'); + static const Key SUB_KIND = const Key('subKind'); static const Key SUPERTYPE = const Key('supertype'); static const Key SUPERTYPES = const Key('supertypes'); static const Key SYMBOLS = const Key('symbols'); diff --git a/pkg/compiler/lib/src/serialization/modelz.dart b/pkg/compiler/lib/src/serialization/modelz.dart index 97a327a1c2b..e06cb037efe 100644 --- a/pkg/compiler/lib/src/serialization/modelz.dart +++ b/pkg/compiler/lib/src/serialization/modelz.dart @@ -1306,8 +1306,7 @@ class StaticFieldElementZ extends FieldElementZ class EnumConstantElementZ extends StaticFieldElementZ implements EnumConstantElement { - EnumConstantElementZ(ObjectDecoder decoder) - : super(decoder); + EnumConstantElementZ(ObjectDecoder decoder) : super(decoder); int get index => _decoder.getInt(Key.INDEX); } @@ -1788,7 +1787,6 @@ class InitializingFormalElementZ extends ParameterElementZ ElementKind get kind => ElementKind.INITIALIZING_FORMAL; } - class LocalVariableElementZ extends DeserializedElementZ with AnalyzableElementMixin, diff --git a/pkg/compiler/lib/src/serialization/resolved_ast_serialization.dart b/pkg/compiler/lib/src/serialization/resolved_ast_serialization.dart new file mode 100644 index 00000000000..d25426ec875 --- /dev/null +++ b/pkg/compiler/lib/src/serialization/resolved_ast_serialization.dart @@ -0,0 +1,422 @@ +// 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.resolved_ast; + +import '../common.dart'; +import '../common/resolution.dart'; +import '../constants/expressions.dart'; +import '../dart_types.dart'; +import '../diagnostics/diagnostic_listener.dart'; +import '../elements/elements.dart'; +import '../parser/parser.dart' show Parser; +import '../parser/listener.dart' show ParserError; +import '../parser/node_listener.dart' show NodeListener; +import '../resolution/enum_creator.dart'; +import '../resolution/send_structure.dart'; +import '../resolution/tree_elements.dart'; +import '../tree/tree.dart'; +import '../tokens/token.dart'; +import '../universe/selector.dart'; +import 'keys.dart'; +import 'serialization.dart'; +import 'serialization_util.dart'; + +/// Visitor that computes a node-index mapping. +class AstIndexComputer extends Visitor { + final Map nodeIndices = {}; + final List nodeList = []; + + @override + visitNode(Node node) { + nodeIndices.putIfAbsent(node, () { + // Some nodes (like Modifier and empty NodeList) can be reused. + nodeList.add(node); + return nodeIndices.length; + }); + node.visitChildren(this); + } +} + +/// The kind of AST node. Used for determining how to deserialize +/// [ResolvedAst]s. +enum AstKind { + ENUM_CONSTRUCTOR, + ENUM_CONSTANT, + ENUM_INDEX_FIELD, + ENUM_VALUES_FIELD, + ENUM_TO_STRING, + FACTORY, + FIELD, + FUNCTION, +} + +/// Serializer for [ResolvedAst]s. +class ResolvedAstSerializer extends Visitor { + final ObjectEncoder objectEncoder; + final ResolvedAst resolvedAst; + final AstIndexComputer indexComputer = new AstIndexComputer(); + final Map nodeData = {}; + ListEncoder _nodeDataEncoder; + + ResolvedAstSerializer(this.objectEncoder, this.resolvedAst); + + AstElement get element => resolvedAst.element; + + TreeElements get elements => resolvedAst.elements; + + Node get root => resolvedAst.node; + + Map get nodeIndices => indexComputer.nodeIndices; + List get nodeList => indexComputer.nodeList; + + /// Serializes [resolvedAst] into [objectEncoder]. + void serialize() { + objectEncoder.setUri( + Key.URI, + elements.analyzedElement.compilationUnit.script.resourceUri, + elements.analyzedElement.compilationUnit.script.resourceUri); + AstKind kind; + if (element.enclosingClass is EnumClassElement) { + if (element.name == 'index') { + kind = AstKind.ENUM_INDEX_FIELD; + } else if (element.name == 'values') { + kind = AstKind.ENUM_VALUES_FIELD; + } else if (element.name == 'toString') { + kind = AstKind.ENUM_TO_STRING; + } else if (element.isConstructor) { + kind = AstKind.ENUM_CONSTRUCTOR; + } else { + assert(invariant(element, element.isConst, + message: "Unexpected enum member: $element")); + kind = AstKind.ENUM_CONSTANT; + } + } else { + // [element] has a body that we'll need to re-parse. We store where to + // start parsing from. + objectEncoder.setInt(Key.OFFSET, root.getBeginToken().charOffset); + if (element.isFactoryConstructor) { + kind = AstKind.FACTORY; + } else if (element.isField) { + kind = AstKind.FIELD; + } else { + kind = AstKind.FUNCTION; + FunctionExpression functionExpression = root.asFunctionExpression(); + if (functionExpression.getOrSet != null) { + // Getters/setters need the get/set token to be parsed. + objectEncoder.setInt( + Key.GET_OR_SET, functionExpression.getOrSet.charOffset); + } + } + } + objectEncoder.setEnum(Key.KIND, kind); + root.accept(indexComputer); + root.accept(this); + } + + /// Computes the [ListEncoder] for serializing data for nodes. + ListEncoder get nodeDataEncoder { + if (_nodeDataEncoder == null) { + _nodeDataEncoder = objectEncoder.createList(Key.DATA); + } + return _nodeDataEncoder; + } + + /// Computes the [ObjectEncoder] for serializing data for [node]. + ObjectEncoder getNodeDataEncoder(Node node) { + int id = nodeIndices[node]; + return nodeData.putIfAbsent(id, () { + ObjectEncoder objectEncoder = nodeDataEncoder.createObject(); + objectEncoder.setInt(Key.ID, id); + return objectEncoder; + }); + } + + @override + 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); + } + } + DartType type = elements.getType(node); + if (type != null) { + getNodeDataEncoder(node).setType(Key.TYPE, type); + } + Selector selector = elements.getSelector(node); + if (selector != null) { + serializeSelector( + selector, getNodeDataEncoder(node).createObject(Key.SELECTOR)); + } + ConstantExpression constant = elements.getConstant(node); + if (constant != null) { + getNodeDataEncoder(node).setConstant(Key.CONSTANT, constant); + } + DartType cachedType = elements.typesCache[node]; + if (cachedType != null) { + getNodeDataEncoder(node).setType(Key.CACHED_TYPE, cachedType); + } + // TODO(johnniwinther): Serialize [JumpTarget]s. + node.visitChildren(this); + } + + @override + visitSend(Send node) { + visitExpression(node); + SendStructure structure = elements.getSendStructure(node); + if (structure != null) { + serializeSendStructure( + structure, getNodeDataEncoder(node).createObject(Key.SEND_STRUCTURE)); + } + } + + @override + visitNewExpression(NewExpression node) { + visitExpression(node); + NewStructure structure = elements.getNewStructure(node); + if (structure != null) { + serializeNewStructure( + structure, getNodeDataEncoder(node).createObject(Key.NEW_STRUCTURE)); + } + } + + @override + visitGotoStatement(GotoStatement node) { + visitStatement(node); + // TODO(johnniwinther): Serialize [JumpTarget]s and [LabelDefinition]s. + } + + @override + visitLabel(Label node) { + visitNode(node); + // TODO(johnniwinther): Serialize[LabelDefinition]s. + } +} + +class ResolvedAstDeserializer { + /// Find the [Token] at [offset] searching through successors of [token]. + static Token findTokenInStream(Token token, int offset) { + while (token.charOffset <= offset && token.next != token) { + if (token.charOffset == offset) { + return token; + } + token = token.next; + } + return null; + } + + /// Deserializes the [ResolvedAst] for [element] from [objectDecoder]. + /// [parsing] and [getBeginToken] are used for parsing the [Node] for + /// [element] from its source code. + static ResolvedAst deserialize(Element element, ObjectDecoder objectDecoder, + Parsing parsing, Token getBeginToken(Uri uri, int charOffset)) { + CompilationUnitElement compilationUnit = element.compilationUnit; + DiagnosticReporter reporter = parsing.reporter; + + /// Returns the first [Token] for parsing the [Node] for [element]. + Token readBeginToken() { + Uri uri = objectDecoder.getUri(Key.URI); + int charOffset = objectDecoder.getInt(Key.OFFSET); + Token beginToken = getBeginToken(uri, charOffset); + if (beginToken == null) { + reporter.internalError( + element, "No token found for $element in $uri @ $charOffset"); + } + return beginToken; + } + + /// Create the [Node] for the element by parsing the source code. + Node doParse(parse(Parser parser)) { + return parsing.measure(() { + return reporter.withCurrentElement(element, () { + CompilationUnitElement unit = element.compilationUnit; + NodeListener listener = new NodeListener( + parsing.getScannerOptionsFor(element), reporter, null); + listener.memberErrors = listener.memberErrors.prepend(false); + try { + Parser parser = new Parser(listener, parsing.parserOptions); + parse(parser); + } on ParserError catch (e) { + reporter.internalError(element, '$e'); + } + return listener.popNode(); + }); + }); + } + + /// Computes the [Node] for the element based on the [AstKind]. + Node computeNode(AstKind kind) { + switch (kind) { + case AstKind.ENUM_INDEX_FIELD: + AstBuilder builder = new AstBuilder(element.sourcePosition.begin); + Identifier identifier = builder.identifier('index'); + VariableDefinitions node = new VariableDefinitions( + null, + builder.modifiers(isFinal: true), + new NodeList.singleton(identifier)); + return node; + case AstKind.ENUM_VALUES_FIELD: + EnumClassElement enumClass = element.enclosingClass; + AstBuilder builder = new AstBuilder(element.sourcePosition.begin); + List enumValues = []; + List valueReferences = []; + for (EnumConstantElement enumConstant in enumClass.enumValues) { + AstBuilder valueBuilder = + new AstBuilder(enumConstant.sourcePosition.begin); + Identifier name = valueBuilder.identifier(enumConstant.name); + + // Add reference for the `values` field. + valueReferences.add(valueBuilder.reference(name)); + } + + Identifier valuesIdentifier = builder.identifier('values'); + // TODO(johnniwinther): Add type argument. + Expression initializer = + builder.listLiteral(valueReferences, isConst: true); + + Node definition = + builder.createDefinition(valuesIdentifier, initializer); + VariableDefinitions node = new VariableDefinitions( + null, + builder.modifiers(isStatic: true, isConst: true), + new NodeList.singleton(definition)); + return node; + case AstKind.ENUM_TO_STRING: + EnumClassElement enumClass = element.enclosingClass; + AstBuilder builder = new AstBuilder(element.sourcePosition.begin); + List mapEntries = []; + for (EnumConstantElement enumConstant in enumClass.enumValues) { + AstBuilder valueBuilder = + new AstBuilder(enumConstant.sourcePosition.begin); + Identifier name = valueBuilder.identifier(enumConstant.name); + + // Add map entry for `toString` implementation. + mapEntries.add(valueBuilder.mapLiteralEntry( + valueBuilder.literalInt(enumConstant.index), + valueBuilder + .literalString('${enumClass.name}.${name.source}'))); + } + + // TODO(johnniwinther): Support return type. Note `String` might be + // prefixed or not imported within the current library. + FunctionExpression toStringNode = builder.functionExpression( + Modifiers.EMPTY, + 'toString', + builder.argumentList([]), + builder.returnStatement(builder.indexGet( + builder.mapLiteral(mapEntries, isConst: true), + builder.reference(builder.identifier('index'))))); + return toStringNode; + case AstKind.ENUM_CONSTRUCTOR: + AstBuilder builder = new AstBuilder(element.sourcePosition.begin); + VariableDefinitions indexDefinition = + builder.initializingFormal('index'); + FunctionExpression constructorNode = builder.functionExpression( + builder.modifiers(isConst: true), + element.enclosingClass.name, + builder.argumentList([indexDefinition]), + builder.emptyStatement()); + return constructorNode; + case AstKind.ENUM_CONSTANT: + EnumConstantElement enumConstant = element; + EnumClassElement enumClass = element.enclosingClass; + int index = enumConstant.index; + AstBuilder builder = new AstBuilder(element.sourcePosition.begin); + Identifier name = builder.identifier(element.name); + + Expression initializer = builder.newExpression( + enumClass.name, builder.argumentList([builder.literalInt(index)]), + isConst: true); + SendSet definition = builder.createDefinition(name, initializer); + + VariableDefinitions node = new VariableDefinitions( + null, + builder.modifiers(isStatic: true, isConst: true), + new NodeList.singleton(definition)); + return node; + case AstKind.FACTORY: + Token beginToken = readBeginToken(); + return doParse((parser) => parser.parseFactoryMethod(beginToken)); + case AstKind.FIELD: + Token beginToken = readBeginToken(); + return doParse((parser) => parser.parseMember(beginToken)); + case AstKind.FUNCTION: + Token beginToken = readBeginToken(); + int getOrSetOffset = + objectDecoder.getInt(Key.GET_OR_SET, isOptional: true); + Token getOrSet; + if (getOrSetOffset != null) { + getOrSet = findTokenInStream(beginToken, getOrSetOffset); + if (getOrSet == null) { + reporter.internalError( + element, + "No token found for $element in " + "${objectDecoder.getUri(Key.URI)} @ $getOrSetOffset"); + } + } + return doParse((parser) { + parser.parseFunction(beginToken, getOrSet); + }); + } + } + + AstKind kind = objectDecoder.getEnum(Key.KIND, AstKind.values); + Node root = computeNode(kind); + TreeElementMapping elements = new TreeElementMapping(element); + AstIndexComputer indexComputer = new AstIndexComputer(); + Map nodeIndices = indexComputer.nodeIndices; + List nodeList = indexComputer.nodeList; + root.accept(indexComputer); + ListDecoder dataDecoder = objectDecoder.getList(Key.DATA); + if (dataDecoder != null) { + for (int i = 0; i < dataDecoder.length; i++) { + ObjectDecoder objectDecoder = dataDecoder.getObject(i); + int id = objectDecoder.getInt(Key.ID); + Node node = nodeList[id]; + Element nodeElement = + objectDecoder.getElement(Key.ELEMENT, isOptional: true); + if (nodeElement != null) { + elements[node] = nodeElement; + } + DartType type = objectDecoder.getType(Key.TYPE, isOptional: true); + if (type != null) { + elements.setType(node, type); + } + ObjectDecoder selectorDecoder = + objectDecoder.getObject(Key.SELECTOR, isOptional: true); + if (selectorDecoder != null) { + elements.setSelector(node, deserializeSelector(selectorDecoder)); + } + ConstantExpression constant = + objectDecoder.getConstant(Key.CONSTANT, isOptional: true); + if (constant != null) { + elements.setConstant(node, constant); + } + DartType cachedType = + objectDecoder.getType(Key.CACHED_TYPE, isOptional: true); + if (cachedType != null) { + elements.typesCache[node] = cachedType; + } + ObjectDecoder sendStructureDecoder = + objectDecoder.getObject(Key.SEND_STRUCTURE, isOptional: true); + if (sendStructureDecoder != null) { + elements.setSendStructure( + node, deserializeSendStructure(sendStructureDecoder)); + } + ObjectDecoder newStructureDecoder = + objectDecoder.getObject(Key.NEW_STRUCTURE, isOptional: true); + if (newStructureDecoder != null) { + elements.setNewStructure( + node, deserializeNewStructure(newStructureDecoder)); + } + } + } + return new ResolvedAst(element, root, elements); + } +} diff --git a/pkg/compiler/lib/src/serialization/serialization_util.dart b/pkg/compiler/lib/src/serialization/serialization_util.dart new file mode 100644 index 00000000000..cd5e77a753f --- /dev/null +++ b/pkg/compiler/lib/src/serialization/serialization_util.dart @@ -0,0 +1,481 @@ +// 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.util; + +import '../dart_types.dart'; +import '../common/resolution.dart'; +import '../constants/expressions.dart'; +import '../elements/elements.dart'; +import '../resolution/access_semantics.dart'; +import '../resolution/operators.dart'; +import '../resolution/send_structure.dart'; +import '../universe/call_structure.dart'; +import '../universe/selector.dart'; +import '../universe/world_impact.dart'; +import '../universe/use.dart'; +import '../util/enumset.dart'; + +import 'keys.dart'; +import 'serialization.dart'; + +/// Serialize [name] into [encoder]. +void serializeName(Name name, ObjectEncoder encoder) { + encoder.setString(Key.NAME, name.text); + encoder.setBool(Key.IS_SETTER, name.isSetter); + if (name.library != null) { + encoder.setElement(Key.LIBRARY, name.library); + } +} + +/// Deserialize a [Name] from [decoder]. +Name deserializeName(ObjectDecoder decoder) { + String name = decoder.getString(Key.NAME); + bool isSetter = decoder.getBool(Key.IS_SETTER); + LibraryElement library = decoder.getElement(Key.LIBRARY, isOptional: true); + return new Name(name, library, isSetter: isSetter); +} + +/// Serialize [selector] into [encoder]. +void serializeSelector(Selector selector, ObjectEncoder encoder) { + encoder.setEnum(Key.KIND, selector.kind); + + encoder.setInt(Key.ARGUMENTS, selector.callStructure.argumentCount); + encoder.setStrings( + Key.NAMED_ARGUMENTS, selector.callStructure.namedArguments); + serializeName(selector.memberName, encoder); +} + +/// Deserialize a [Selector] from [decoder]. +Selector deserializeSelector(ObjectDecoder decoder) { + SelectorKind kind = decoder.getEnum(Key.KIND, SelectorKind.values); + int argumentCount = decoder.getInt(Key.ARGUMENTS); + List namedArguments = + decoder.getStrings(Key.NAMED_ARGUMENTS, isOptional: true); + String name = decoder.getString(Key.NAME); + bool isSetter = decoder.getBool(Key.IS_SETTER); + LibraryElement library = decoder.getElement(Key.LIBRARY, isOptional: true); + return new Selector(kind, deserializeName(decoder), + new CallStructure(argumentCount, namedArguments)); +} + +/// Serialize [sendStructure] into [encoder]. +void serializeSendStructure( + SendStructure sendStructure, ObjectEncoder encoder) { + encoder.setEnum(Key.KIND, sendStructure.kind); + switch (sendStructure.kind) { + case SendStructureKind.IF_NULL: + case SendStructureKind.LOGICAL_AND: + case SendStructureKind.LOGICAL_OR: + case SendStructureKind.NOT: + case SendStructureKind.INVALID_UNARY: + case SendStructureKind.INVALID_BINARY: + // No additional properties. + break; + case SendStructureKind.IS: + IsStructure structure = sendStructure; + encoder.setType(Key.TYPE, structure.type); + break; + case SendStructureKind.IS_NOT: + IsNotStructure structure = sendStructure; + encoder.setType(Key.TYPE, structure.type); + break; + case SendStructureKind.AS: + AsStructure structure = sendStructure; + encoder.setType(Key.TYPE, structure.type); + break; + case SendStructureKind.INVOKE: + InvokeStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + serializeSelector(structure.selector, encoder.createObject(Key.SELECTOR)); + break; + case SendStructureKind.INCOMPATIBLE_INVOKE: + IncompatibleInvokeStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + serializeSelector(structure.selector, encoder.createObject(Key.SELECTOR)); + break; + case SendStructureKind.GET: + GetStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.SET: + SetStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.UNARY: + UnaryStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.INDEX: + IndexStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.EQUALS: + EqualsStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.NOT_EQUALS: + NotEqualsStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.BINARY: + BinaryStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.INDEX_SET: + IndexSetStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.INDEX_PREFIX: + IndexPrefixStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.INDEX_POSTFIX: + IndexPostfixStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.COMPOUND: + CompoundStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.SET_IF_NULL: + SetIfNullStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.COMPOUND_INDEX_SET: + CompoundIndexSetStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.INDEX_SET_IF_NULL: + IndexSetIfNullStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + break; + case SendStructureKind.PREFIX: + PrefixStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.POSTFIX: + PostfixStructure structure = sendStructure; + serializeAccessSemantics( + structure.semantics, encoder.createObject(Key.SEMANTICS)); + encoder.setEnum(Key.OPERATOR, structure.operator.kind); + break; + case SendStructureKind.DEFERRED_PREFIX: + DeferredPrefixStructure structure = sendStructure; + encoder.setElement(Key.PREFIX, structure.prefix); + serializeSendStructure( + structure.sendStructure, encoder.createObject(Key.SEND_STRUCTURE)); + break; + } +} + +/// Deserialize a [SendStructure] from [decoder]. +SendStructure deserializeSendStructure(ObjectDecoder decoder) { + SendStructureKind kind = decoder.getEnum(Key.KIND, SendStructureKind.values); + switch (kind) { + case SendStructureKind.IF_NULL: + return const IfNullStructure(); + case SendStructureKind.LOGICAL_AND: + return const LogicalAndStructure(); + case SendStructureKind.LOGICAL_OR: + return const LogicalOrStructure(); + case SendStructureKind.IS: + return new IsStructure(decoder.getType(Key.TYPE)); + case SendStructureKind.IS_NOT: + return new IsNotStructure(decoder.getType(Key.TYPE)); + case SendStructureKind.AS: + return new AsStructure(decoder.getType(Key.TYPE)); + case SendStructureKind.INVOKE: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + Selector selector = deserializeSelector(decoder.getObject(Key.SELECTOR)); + return new InvokeStructure(semantics, selector); + case SendStructureKind.INCOMPATIBLE_INVOKE: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + Selector selector = deserializeSelector(decoder.getObject(Key.SELECTOR)); + return new IncompatibleInvokeStructure(semantics, selector); + case SendStructureKind.GET: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new GetStructure(semantics); + case SendStructureKind.SET: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new SetStructure(semantics); + case SendStructureKind.NOT: + return const NotStructure(); + case SendStructureKind.UNARY: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new UnaryStructure( + semantics, + UnaryOperator.fromKind( + decoder.getEnum(Key.OPERATOR, UnaryOperatorKind.values))); + case SendStructureKind.INVALID_UNARY: + return new InvalidUnaryStructure(); + case SendStructureKind.INDEX: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new IndexStructure(semantics); + case SendStructureKind.EQUALS: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new EqualsStructure(semantics); + case SendStructureKind.NOT_EQUALS: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new NotEqualsStructure(semantics); + case SendStructureKind.BINARY: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new BinaryStructure( + semantics, + BinaryOperator.fromKind( + decoder.getEnum(Key.OPERATOR, BinaryOperatorKind.values))); + case SendStructureKind.INVALID_BINARY: + return const InvalidBinaryStructure(); + case SendStructureKind.INDEX_SET: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new IndexSetStructure(semantics); + case SendStructureKind.INDEX_PREFIX: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new IndexPrefixStructure( + semantics, + IncDecOperator.fromKind( + decoder.getEnum(Key.OPERATOR, IncDecOperatorKind.values))); + case SendStructureKind.INDEX_POSTFIX: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new IndexPostfixStructure( + semantics, + IncDecOperator.fromKind( + decoder.getEnum(Key.OPERATOR, IncDecOperatorKind.values))); + case SendStructureKind.COMPOUND: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new CompoundStructure( + semantics, + AssignmentOperator.fromKind( + decoder.getEnum(Key.OPERATOR, AssignmentOperatorKind.values))); + case SendStructureKind.SET_IF_NULL: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new SetIfNullStructure(semantics); + case SendStructureKind.COMPOUND_INDEX_SET: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new CompoundIndexSetStructure( + semantics, + AssignmentOperator.fromKind( + decoder.getEnum(Key.OPERATOR, AssignmentOperatorKind.values))); + case SendStructureKind.INDEX_SET_IF_NULL: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new IndexSetIfNullStructure(semantics); + case SendStructureKind.PREFIX: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new PrefixStructure( + semantics, + IncDecOperator.fromKind( + decoder.getEnum(Key.OPERATOR, IncDecOperatorKind.values))); + case SendStructureKind.POSTFIX: + AccessSemantics semantics = + deserializeAccessSemantics(decoder.getObject(Key.SEMANTICS)); + return new PostfixStructure( + semantics, + IncDecOperator.fromKind( + decoder.getEnum(Key.OPERATOR, IncDecOperatorKind.values))); + case SendStructureKind.DEFERRED_PREFIX: + PrefixElement prefix = decoder.getElement(Key.PREFIX); + SendStructure sendStructure = + deserializeSendStructure(decoder.getObject(Key.SEND_STRUCTURE)); + return new DeferredPrefixStructure(prefix, sendStructure); + } +} + +/// Serialize [newStructure] into [encoder]. +void serializeNewStructure(NewStructure newStructure, ObjectEncoder encoder) { + encoder.setEnum(Key.KIND, newStructure.kind); + switch (newStructure.kind) { + case NewStructureKind.NEW_INVOKE: + NewInvokeStructure structure = newStructure; + encoder.setEnum(Key.SUB_KIND, structure.semantics.kind); + encoder.setElement(Key.ELEMENT, structure.semantics.element); + encoder.setType(Key.TYPE, structure.semantics.type); + serializeSelector(structure.selector, encoder.createObject(Key.SELECTOR)); + break; + case NewStructureKind.CONST_INVOKE: + ConstInvokeStructure structure = newStructure; + encoder.setEnum(Key.SUB_KIND, structure.constantInvokeKind); + encoder.setConstant(Key.CONSTANT, structure.constant); + break; + case NewStructureKind.LATE_CONST: + throw new UnsupportedError( + 'Unsupported NewStructure kind ${newStructure.kind}.'); + } +} + +/// Deserialize a [NewStructure] from [decoder]. +NewStructure deserializeNewStructure(ObjectDecoder decoder) { + NewStructureKind kind = decoder.getEnum(Key.KIND, NewStructureKind.values); + switch (kind) { + case NewStructureKind.NEW_INVOKE: + ConstructorAccessKind constructorAccessKind = + decoder.getEnum(Key.SUB_KIND, ConstructorAccessKind.values); + Element element = decoder.getElement(Key.ELEMENT); + DartType type = decoder.getType(Key.TYPE); + ConstructorAccessSemantics semantics = + new ConstructorAccessSemantics(constructorAccessKind, element, type); + Selector selector = deserializeSelector(decoder.getObject(Key.SELECTOR)); + return new NewInvokeStructure(semantics, selector); + + case NewStructureKind.CONST_INVOKE: + ConstantInvokeKind constantInvokeKind = + decoder.getEnum(Key.SUB_KIND, ConstantInvokeKind.values); + ConstantExpression constant = decoder.getConstant(Key.CONSTANT); + return new ConstInvokeStructure(constantInvokeKind, constant); + case NewStructureKind.LATE_CONST: + throw new UnsupportedError('Unsupported NewStructure kind $kind.'); + } +} + +/// Serialize [semantics] into [encoder]. +void serializeAccessSemantics( + AccessSemantics semantics, ObjectEncoder encoder) { + encoder.setEnum(Key.KIND, semantics.kind); + switch (semantics.kind) { + case AccessKind.EXPRESSION: + case AccessKind.THIS: + // No additional properties. + break; + case AccessKind.THIS_PROPERTY: + case AccessKind.DYNAMIC_PROPERTY: + case AccessKind.CONDITIONAL_DYNAMIC_PROPERTY: + serializeName(semantics.name, encoder); + break; + case AccessKind.CLASS_TYPE_LITERAL: + case AccessKind.TYPEDEF_TYPE_LITERAL: + case AccessKind.DYNAMIC_TYPE_LITERAL: + encoder.setConstant(Key.CONSTANT, semantics.constant); + break; + case AccessKind.LOCAL_FUNCTION: + case AccessKind.LOCAL_VARIABLE: + case AccessKind.FINAL_LOCAL_VARIABLE: + case AccessKind.PARAMETER: + case AccessKind.FINAL_PARAMETER: + case AccessKind.STATIC_FIELD: + case AccessKind.FINAL_STATIC_FIELD: + case AccessKind.STATIC_METHOD: + case AccessKind.STATIC_GETTER: + case AccessKind.STATIC_SETTER: + case AccessKind.TOPLEVEL_FIELD: + case AccessKind.FINAL_TOPLEVEL_FIELD: + case AccessKind.TOPLEVEL_METHOD: + case AccessKind.TOPLEVEL_GETTER: + case AccessKind.TOPLEVEL_SETTER: + case AccessKind.SUPER_FIELD: + case AccessKind.SUPER_FINAL_FIELD: + case AccessKind.SUPER_METHOD: + case AccessKind.SUPER_GETTER: + case AccessKind.SUPER_SETTER: + case AccessKind.TYPE_PARAMETER_TYPE_LITERAL: + case AccessKind.UNRESOLVED: + case AccessKind.UNRESOLVED_SUPER: + case AccessKind.INVALID: + encoder.setElement(Key.ELEMENT, semantics.element); + break; + case AccessKind.COMPOUND: + CompoundAccessSemantics compoundAccess = semantics; + encoder.setEnum(Key.SUB_KIND, compoundAccess.compoundAccessKind); + encoder.setElement(Key.GETTER, semantics.getter); + encoder.setElement(Key.SETTER, semantics.setter); + break; + case AccessKind.CONSTANT: + throw new UnsupportedError('Unsupported access kind: ${semantics.kind}'); + } +} + +/// Deserialize a [AccessSemantics] from [decoder]. +AccessSemantics deserializeAccessSemantics(ObjectDecoder decoder) { + AccessKind kind = decoder.getEnum(Key.KIND, AccessKind.values); + switch (kind) { + case AccessKind.EXPRESSION: + return const DynamicAccess.expression(); + case AccessKind.THIS: + return const DynamicAccess.thisAccess(); + case AccessKind.THIS_PROPERTY: + return new DynamicAccess.thisProperty(deserializeName(decoder)); + case AccessKind.DYNAMIC_PROPERTY: + return new DynamicAccess.dynamicProperty(deserializeName(decoder)); + case AccessKind.CONDITIONAL_DYNAMIC_PROPERTY: + return new DynamicAccess.ifNotNullProperty(deserializeName(decoder)); + case AccessKind.CLASS_TYPE_LITERAL: + case AccessKind.TYPEDEF_TYPE_LITERAL: + case AccessKind.DYNAMIC_TYPE_LITERAL: + return new ConstantAccess(kind, decoder.getConstant(Key.CONSTANT)); + + case AccessKind.LOCAL_FUNCTION: + case AccessKind.LOCAL_VARIABLE: + case AccessKind.FINAL_LOCAL_VARIABLE: + case AccessKind.PARAMETER: + case AccessKind.FINAL_PARAMETER: + case AccessKind.STATIC_FIELD: + case AccessKind.FINAL_STATIC_FIELD: + case AccessKind.STATIC_METHOD: + case AccessKind.STATIC_GETTER: + case AccessKind.STATIC_SETTER: + case AccessKind.TOPLEVEL_FIELD: + case AccessKind.FINAL_TOPLEVEL_FIELD: + case AccessKind.TOPLEVEL_METHOD: + case AccessKind.TOPLEVEL_GETTER: + case AccessKind.TOPLEVEL_SETTER: + case AccessKind.SUPER_FIELD: + case AccessKind.SUPER_FINAL_FIELD: + case AccessKind.SUPER_METHOD: + case AccessKind.SUPER_GETTER: + case AccessKind.SUPER_SETTER: + case AccessKind.TYPE_PARAMETER_TYPE_LITERAL: + case AccessKind.UNRESOLVED: + case AccessKind.UNRESOLVED_SUPER: + case AccessKind.INVALID: + return new StaticAccess.internal(kind, decoder.getElement(Key.ELEMENT)); + + case AccessKind.COMPOUND: + CompoundAccessKind compoundAccessKind = + decoder.getEnum(Key.SUB_KIND, CompoundAccessKind.values); + Element getter = decoder.getElement(Key.GETTER); + Element setter = decoder.getElement(Key.SETTER); + return new CompoundAccessSemantics(compoundAccessKind, getter, setter); + case AccessKind.CONSTANT: + throw new UnsupportedError('Unsupported access kind: $kind'); + } +} diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart index 72d702bd275..1289b77f152 100644 --- a/pkg/compiler/lib/src/serialization/task.dart +++ b/pkg/compiler/lib/src/serialization/task.dart @@ -4,6 +4,7 @@ library dart2js.serialization.task; +import 'dart:async' show Future; import '../common/resolution.dart' show ResolutionImpact, ResolutionWorkItem; import '../common/tasks.dart' show CompilerTask; import '../common/work.dart' show ItemCompilationContext; @@ -17,7 +18,7 @@ import '../universe/world_impact.dart' show WorldImpact; abstract class LibraryDeserializer { /// Loads the [LibraryElement] associated with a library under [uri], or null /// if no serialized information is available for the given library. - LibraryElement readLibrary(Uri uri); + Future readLibrary(Uri uri); } /// Task that supports deserialization of elements. @@ -35,8 +36,8 @@ class SerializationTask extends CompilerTask implements LibraryDeserializer { /// Returns the [LibraryElement] for [resolvedUri] if available from /// serialization. - LibraryElement readLibrary(Uri resolvedUri) { - if (deserializer == null) return null; + Future readLibrary(Uri resolvedUri) { + if (deserializer == null) return new Future.value(); return deserializer.readLibrary(resolvedUri); } @@ -81,8 +82,9 @@ class DeserializedResolutionWorkItem implements ResolutionWorkItem { /// The interface for a system that supports deserialization of libraries and /// elements. abstract class DeserializerSystem { - LibraryElement readLibrary(Uri resolvedUri); + Future readLibrary(Uri resolvedUri); bool isDeserialized(Element element); + ResolvedAst getResolvedAst(Element element); ResolutionImpact getResolutionImpact(Element element); WorldImpact computeWorldImpact(Element element); } diff --git a/tests/compiler/dart2js/serialization_helper.dart b/tests/compiler/dart2js/serialization_helper.dart index a8f95a6bcc9..c8632652030 100644 --- a/tests/compiler/dart2js/serialization_helper.dart +++ b/tests/compiler/dart2js/serialization_helper.dart @@ -7,6 +7,7 @@ library dart2js.serialization_helper; import 'dart:async'; import 'package:async_helper/async_helper.dart'; import 'package:expect/expect.dart'; +import 'package:compiler/compiler_new.dart'; import 'package:compiler/src/commandline_options.dart'; import 'package:compiler/src/common/backend_api.dart'; import 'package:compiler/src/common/names.dart'; @@ -14,30 +15,46 @@ import 'package:compiler/src/common/resolution.dart'; import 'package:compiler/src/compiler.dart'; import 'package:compiler/src/elements/elements.dart'; import 'package:compiler/src/filenames.dart'; +import 'package:compiler/src/io/source_file.dart'; +import 'package:compiler/src/scanner/scanner.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/resolved_ast_serialization.dart'; import 'package:compiler/src/serialization/serialization.dart'; +import 'package:compiler/src/serialization/modelz.dart'; import 'package:compiler/src/serialization/task.dart'; +import 'package:compiler/src/tokens/token.dart'; +import 'package:compiler/src/script.dart'; import 'package:compiler/src/universe/world_impact.dart'; import 'memory_compiler.dart'; -Future serializeDartCore() async { +Future serializeDartCore({bool serializeResolvedAst: false}) async { Compiler compiler = compilerFor( options: [Flags.analyzeAll]); compiler.serialization.supportSerialization = true; await compiler.run(Uris.dart_core); - return serialize(compiler, compiler.libraryLoader.libraries) - .toText(const JsonSerializationEncoder()); + return serialize( + compiler, + compiler.libraryLoader.libraries, + serializeResolvedAst: serializeResolvedAst) + .toText(const JsonSerializationEncoder()); } -Serializer serialize(Compiler compiler, Iterable libraries) { +Serializer serialize( + Compiler compiler, + Iterable libraries, + {bool serializeResolvedAst: false}) { assert(compiler.serialization.supportSerialization); Serializer serializer = new Serializer(); serializer.plugins.add(compiler.backend.serialization.serializer); serializer.plugins.add(new ResolutionImpactSerializer(compiler.resolution)); + if (serializeResolvedAst) { + serializer.plugins.add( + new ResolvedAstSerializerPlugin(compiler.resolution)); + } for (LibraryElement library in libraries) { serializer.serialize(library); @@ -45,7 +62,9 @@ Serializer serialize(Compiler compiler, Iterable libraries) { return serializer; } -void deserialize(Compiler compiler, String serializedData) { +void deserialize(Compiler compiler, + String serializedData, + {bool deserializeResolvedAst: false}) { Deserializer deserializer = new Deserializer.fromText( new DeserializationContext(), serializedData, @@ -53,8 +72,10 @@ void deserialize(Compiler compiler, String serializedData) { deserializer.plugins.add(compiler.backend.serialization.deserializer); compiler.serialization.deserializer = new _DeserializerSystem( + compiler, deserializer, - compiler.backend.impactTransformer); + compiler.backend.impactTransformer, + deserializeResolvedAst: deserializeResolvedAst); } @@ -88,24 +109,59 @@ class ResolutionImpactDeserializer extends DeserializerPlugin { } class _DeserializerSystem extends DeserializerSystem { + final Compiler _compiler; final Deserializer _deserializer; final List deserializedLibraries = []; final ResolutionImpactDeserializer _resolutionImpactDeserializer = new ResolutionImpactDeserializer(); + final ResolvedAstDeserializerPlugin _resolvedAstDeserializer; final ImpactTransformer _impactTransformer; + final bool _deserializeResolvedAst; - _DeserializerSystem(this._deserializer, this._impactTransformer) { + _DeserializerSystem( + Compiler compiler, + this._deserializer, + this._impactTransformer, + {bool deserializeResolvedAst: false}) + : this._compiler = compiler, + this._deserializeResolvedAst = deserializeResolvedAst, + this._resolvedAstDeserializer = deserializeResolvedAst + ? new ResolvedAstDeserializerPlugin(compiler.parsing) : null { _deserializer.plugins.add(_resolutionImpactDeserializer); + if (_deserializeResolvedAst) { + _deserializer.plugins.add(_resolvedAstDeserializer); + } } - LibraryElement readLibrary(Uri resolvedUri) { + @override + Future readLibrary(Uri resolvedUri) { LibraryElement library = _deserializer.lookupLibrary(resolvedUri); if (library != null) { deserializedLibraries.add(library); + if (_deserializeResolvedAst) { + return Future.forEach(library.compilationUnits, + (CompilationUnitElement compilationUnit) { + Script script = compilationUnit.script; + return _compiler.readScript(script.readableUri) + .then((Script newScript) { + _resolvedAstDeserializer.sourceFiles[script.resourceUri] = + newScript.file; + }); + }).then((_) => library); + } } - return library; + return new Future.value(library); } + @override + ResolvedAst getResolvedAst(Element element) { + if (_resolvedAstDeserializer != null) { + return _resolvedAstDeserializer.getResolvedAst(element); + } + return null; + } + + @override ResolutionImpact getResolutionImpact(Element element) { return _resolutionImpactDeserializer.impactMap[element]; } @@ -126,3 +182,66 @@ class _DeserializerSystem extends DeserializerSystem { return deserializedLibraries.contains(element.library); } } + +const String RESOLVED_AST_TAG = 'resolvedAst'; + +class ResolvedAstSerializerPlugin extends SerializerPlugin { + final Resolution resolution; + + ResolvedAstSerializerPlugin(this.resolution); + + @override + void onElement(Element element, ObjectEncoder createEncoder(String tag)) { + if (element is MemberElement && resolution.hasResolvedAst(element)) { + ResolvedAst resolvedAst = resolution.getResolvedAst(element); + ObjectEncoder objectEncoder = createEncoder(RESOLVED_AST_TAG); + new ResolvedAstSerializer(objectEncoder, resolvedAst).serialize(); + } + } +} + +class ResolvedAstDeserializerPlugin extends DeserializerPlugin { + final Parsing parsing; + final Map sourceFiles = {}; + + Map _resolvedAstMap = {}; + Map _decoderMap = {}; + Map beginTokenMap = {}; + + ResolvedAstDeserializerPlugin(this.parsing); + + ResolvedAst getResolvedAst(Element element) { + ResolvedAst resolvedAst = _resolvedAstMap[element]; + if (resolvedAst == null) { + ObjectDecoder decoder = _decoderMap[element]; + if (decoder != null) { + resolvedAst = _resolvedAstMap[element] = + ResolvedAstDeserializer.deserialize( + element, decoder, parsing, findToken); + _decoderMap.remove(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; + } + } +} + diff --git a/tests/compiler/dart2js/serialization_resolved_ast_test.dart b/tests/compiler/dart2js/serialization_resolved_ast_test.dart new file mode 100644 index 00000000000..af5d8a35c08 --- /dev/null +++ b/tests/compiler/dart2js/serialization_resolved_ast_test.dart @@ -0,0 +1,92 @@ +// 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_resolved_ast_test; + +import 'dart:async'; +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/compiler.dart'; +import 'package:compiler/src/elements/elements.dart'; +import 'package:compiler/src/filenames.dart'; +import 'package:compiler/src/serialization/equivalence.dart'; +import 'memory_compiler.dart'; +import 'serialization_helper.dart'; +import 'serialization_test_data.dart'; +import 'serialization_test_helper.dart'; + + +main(List arguments) { + asyncTest(() async { + String serializedData = await serializeDartCore(serializeResolvedAst: true); + if (arguments.isNotEmpty) { + Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.last)); + await check(serializedData, entryPoint); + } else { + Uri entryPoint = Uri.parse('memory:main.dart'); + // TODO(johnniwinther): Change to test all serialized resolved ast instead + // only those used in the test. + Test test = TESTS.last; + await check(serializedData, entryPoint, test.sourceFiles); + } + }); +} + +Future check( + String serializedData, + Uri entryPoint, + [Map sourceFiles = const {}]) async { + + Compiler compilerNormal = compilerFor( + memorySourceFiles: sourceFiles, + options: [Flags.analyzeOnly]); + compilerNormal.resolution.retainCachesForTesting = true; + await compilerNormal.run(entryPoint); + + Compiler compilerDeserialized = compilerFor( + memorySourceFiles: sourceFiles, + options: [Flags.analyzeOnly]); + compilerDeserialized.resolution.retainCachesForTesting = true; + deserialize( + compilerDeserialized, serializedData, deserializeResolvedAst: true); + await compilerDeserialized.run(entryPoint); + + checkAllResolvedAsts(compilerNormal, compilerDeserialized, verbose: true); +} + +void checkAllResolvedAsts( + Compiler compiler1, + Compiler compiler2, + {bool verbose: false}) { + checkLoadedLibraryMembers( + compiler1, + compiler2, + (Element member1) { + return compiler1.resolution.hasResolvedAst(member1); + }, + checkResolvedAsts, + verbose: true); +} + + +/// Check equivalence of [impact1] and [impact2]. +void checkResolvedAsts(Compiler compiler1, Element member1, + Compiler compiler2, Element member2, + {bool verbose: false}) { + ResolvedAst resolvedAst1 = compiler1.resolution.getResolvedAst(member1); + ResolvedAst resolvedAst2 = + compiler2.serialization.deserializer.getResolvedAst(member2); + + if (resolvedAst1 == null || resolvedAst2 == null) return; + + if (verbose) { + print('Checking resolved asts for $member1 vs $member2'); + } + + testResolvedAstEquivalence( + resolvedAst1, resolvedAst2, const CheckStrategy()); +}