| Index: pkg/compiler/lib/src/serialization/resolved_ast_serialization.dart
|
| 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 0000000000000000000000000000000000000000..d25426ec8756b5e7093ada4bdc9d26106697ad47
|
| --- /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<Node, int> nodeIndices = <Node, int>{};
|
| + final List<Node> nodeList = <Node>[];
|
| +
|
| + @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<int, ObjectEncoder> nodeData = <int, ObjectEncoder>{};
|
| + ListEncoder _nodeDataEncoder;
|
| +
|
| + ResolvedAstSerializer(this.objectEncoder, this.resolvedAst);
|
| +
|
| + AstElement get element => resolvedAst.element;
|
| +
|
| + TreeElements get elements => resolvedAst.elements;
|
| +
|
| + Node get root => resolvedAst.node;
|
| +
|
| + Map<Node, int> get nodeIndices => indexComputer.nodeIndices;
|
| + List<Node> 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<FieldElement> enumValues = <FieldElement>[];
|
| + List<Node> valueReferences = <Node>[];
|
| + 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<LiteralMapEntry> mapEntries = <LiteralMapEntry>[];
|
| + 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<Node, int> nodeIndices = indexComputer.nodeIndices;
|
| + List<Node> 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);
|
| + }
|
| +}
|
|
|