Serialize TreeElements

R=sigmund@google.com

Review URL: https://codereview.chromium.org/1873573004 .
This commit is contained in:
Johnni Winther
2016-04-11 11:16:10 +02:00
parent 1f6c0dd14b
commit fefa2188ce
13 changed files with 1419 additions and 85 deletions
@@ -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);
+19
View File
@@ -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);
+41 -38
View File
@@ -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;
});
});
});
@@ -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
@@ -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));
}
}
@@ -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<DynamicUse> dynamicUses = <DynamicUse>[];
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<String> 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);
@@ -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');
@@ -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,
@@ -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);
}
}
@@ -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<String> 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');
}
}
+6 -4
View File
@@ -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<LibraryElement> 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<LibraryElement> readLibrary(Uri resolvedUri) {
if (deserializer == null) return new Future<LibraryElement>.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<LibraryElement> readLibrary(Uri resolvedUri);
bool isDeserialized(Element element);
ResolvedAst getResolvedAst(Element element);
ResolutionImpact getResolutionImpact(Element element);
WorldImpact computeWorldImpact(Element element);
}
@@ -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<String> serializeDartCore() async {
Future<String> 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<LibraryElement> libraries) {
Serializer serialize(
Compiler compiler,
Iterable<LibraryElement> 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<LibraryElement> 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<LibraryElement> deserializedLibraries = <LibraryElement>[];
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<LibraryElement> 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<LibraryElement>.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<Uri, SourceFile> sourceFiles = <Uri, SourceFile>{};
Map<Element, ResolvedAst> _resolvedAstMap = <Element, ResolvedAst>{};
Map<Element, ObjectDecoder> _decoderMap = <Element, ObjectDecoder>{};
Map<Uri, Token> beginTokenMap = <Uri, Token>{};
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;
}
}
}
@@ -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<String> 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<String, String> sourceFiles = const <String, String>{}]) 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());
}