| // 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 '../elements/resolution_types.dart'; |
| import '../diagnostics/diagnostic_listener.dart'; |
| import '../elements/elements.dart'; |
| import '../elements/modelx.dart'; |
| import 'package:front_end/src/fasta/parser.dart' show Parser, ParserError; |
| import '../parser/node_listener.dart' show NodeListener; |
| import '../resolution/enum_creator.dart'; |
| import '../resolution/send_structure.dart'; |
| import '../resolution/tree_elements.dart'; |
| import 'package:front_end/src/fasta/scanner.dart'; |
| import '../tree/tree.dart'; |
| import '../universe/selector.dart'; |
| import 'keys.dart'; |
| import 'modelz.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_NAME_FIELD, |
| ENUM_VALUES_FIELD, |
| ENUM_TO_STRING, |
| FACTORY, |
| FIELD, |
| FUNCTION, |
| } |
| |
| /// Serializer for [ResolvedAst]s. |
| class ResolvedAstSerializer extends Visitor { |
| final SerializerPlugin nativeDataSerializer; |
| 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, this.nativeDataSerializer); |
| |
| 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; |
| |
| Map<JumpTarget, int> jumpTargetMap = <JumpTarget, int>{}; |
| Map<LabelDefinition, int> labelDefinitionMap = <LabelDefinition, int>{}; |
| |
| /// Returns the unique id for [jumpTarget], creating it if necessary. |
| int getJumpTargetId(JumpTarget jumpTarget) { |
| return jumpTargetMap.putIfAbsent(jumpTarget, () => jumpTargetMap.length); |
| } |
| |
| /// Returns the unique id for [labelDefinition], creating it if necessary. |
| int getLabelDefinitionId(LabelDefinition labelDefinition) { |
| return labelDefinitionMap.putIfAbsent( |
| labelDefinition, () => labelDefinitionMap.length); |
| } |
| |
| /// Serializes [resolvedAst] into [objectEncoder]. |
| void serialize() { |
| objectEncoder.setEnum(Key.KIND, resolvedAst.kind); |
| switch (resolvedAst.kind) { |
| case ResolvedAstKind.PARSED: |
| serializeParsed(); |
| break; |
| case ResolvedAstKind.DEFAULT_CONSTRUCTOR: |
| case ResolvedAstKind.FORWARDING_CONSTRUCTOR: |
| case ResolvedAstKind.DEFERRED_LOAD_LIBRARY: |
| // No additional properties. |
| break; |
| } |
| } |
| |
| /// Serialize [ResolvedAst] that is defined in terms of an AST together with |
| /// [TreeElements]. |
| void serializeParsed() { |
| objectEncoder.setUri(Key.URI, resolvedAst.sourceUri, resolvedAst.sourceUri); |
| AstKind kind; |
| if (element.enclosingClass is EnumClassElement) { |
| if (element.name == 'index') { |
| kind = AstKind.ENUM_INDEX_FIELD; |
| } else if (element.name == '_name') { |
| kind = AstKind.ENUM_NAME_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.SUB_KIND, kind); |
| root.accept(indexComputer); |
| objectEncoder.setBool(Key.CONTAINS_TRY, elements.containsTryStatement); |
| if (resolvedAst.body != null) { |
| int index = nodeIndices[resolvedAst.body]; |
| assert(invariant(element, index != null, |
| message: "No index for body of $element: " |
| "${resolvedAst.body} ($nodeIndices).")); |
| objectEncoder.setInt(Key.BODY, index); |
| } |
| root.accept(this); |
| if (jumpTargetMap.isNotEmpty) { |
| ListEncoder list = objectEncoder.createList(Key.JUMP_TARGETS); |
| for (JumpTarget jumpTarget in jumpTargetMap.keys) { |
| serializeJumpTarget(jumpTarget, list.createObject()); |
| } |
| } |
| if (labelDefinitionMap.isNotEmpty) { |
| ListEncoder list = objectEncoder.createList(Key.LABEL_DEFINITIONS); |
| for (LabelDefinition labelDefinition in labelDefinitionMap.keys) { |
| serializeLabelDefinition(labelDefinition, list.createObject()); |
| } |
| } |
| |
| if (element is FunctionElement) { |
| serializeParameterNodes(element); |
| } |
| } |
| |
| void serializeParameterNodes(FunctionElement function) { |
| function.functionSignature.forEachParameter((ParameterElement parameter) { |
| ParameterElement parameterImpl = parameter.implementation; |
| // TODO(johnniwinther): Should we support element->node mapping as well? |
| getNodeDataEncoder(parameterImpl.node) |
| .setElement(PARAMETER_NODE, parameter); |
| if (parameter.initializer != null) { |
| getNodeDataEncoder(parameterImpl.initializer) |
| .setElement(PARAMETER_INITIALIZER, parameter); |
| } |
| }); |
| } |
| |
| /// Serialize [target] into [encoder]. |
| void serializeJumpTarget(JumpTarget jumpTarget, ObjectEncoder encoder) { |
| encoder.setElement(Key.EXECUTABLE_CONTEXT, jumpTarget.executableContext); |
| encoder.setInt(Key.NODE, nodeIndices[jumpTarget.statement]); |
| encoder.setInt(Key.NESTING_LEVEL, jumpTarget.nestingLevel); |
| encoder.setBool(Key.IS_BREAK_TARGET, jumpTarget.isBreakTarget); |
| encoder.setBool(Key.IS_CONTINUE_TARGET, jumpTarget.isContinueTarget); |
| if (jumpTarget.labels.isNotEmpty) { |
| List<int> labelIdList = <int>[]; |
| for (LabelDefinition label in jumpTarget.labels) { |
| labelIdList.add(getLabelDefinitionId(label)); |
| } |
| encoder.setInts(Key.LABELS, labelIdList); |
| } |
| } |
| |
| /// Serialize [label] into [encoder]. |
| void serializeLabelDefinition( |
| LabelDefinition labelDefinition, ObjectEncoder encoder) { |
| encoder.setInt(Key.NODE, nodeIndices[labelDefinition.label]); |
| encoder.setString(Key.NAME, labelDefinition.labelName); |
| encoder.setBool(Key.IS_BREAK_TARGET, labelDefinition.isBreakTarget); |
| encoder.setBool(Key.IS_CONTINUE_TARGET, labelDefinition.isContinueTarget); |
| encoder.setInt(Key.JUMP_TARGET, getJumpTargetId(labelDefinition.target)); |
| } |
| |
| /// 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) { |
| assert(invariant(element, node != null, message: "Node must be non-null.")); |
| int id = nodeIndices[node]; |
| assert(invariant(element, id != null, message: "Node without id: $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) { |
| serializeElementReference(element, Key.ELEMENT, Key.NAME, |
| getNodeDataEncoder(node), nodeElement); |
| } |
| ResolutionDartType 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); |
| } |
| ResolutionDartType cachedType = elements.typesCache[node]; |
| if (cachedType != null) { |
| getNodeDataEncoder(node).setType(Key.CACHED_TYPE, cachedType); |
| } |
| JumpTarget jumpTargetDefinition = elements.getTargetDefinition(node); |
| if (jumpTargetDefinition != null) { |
| getNodeDataEncoder(node).setInt( |
| Key.JUMP_TARGET_DEFINITION, getJumpTargetId(jumpTargetDefinition)); |
| } |
| var nativeData = elements.getNativeData(node); |
| if (nativeData != null) { |
| nativeDataSerializer.onData( |
| nativeData, getNodeDataEncoder(node).createObject(Key.NATIVE)); |
| } |
| 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); |
| JumpTarget jumpTarget = elements.getTargetOf(node); |
| if (jumpTarget != null) { |
| getNodeDataEncoder(node) |
| .setInt(Key.JUMP_TARGET, getJumpTargetId(jumpTarget)); |
| } |
| if (node.target != null) { |
| LabelDefinition targetLabel = elements.getTargetLabel(node); |
| if (targetLabel != null) { |
| getNodeDataEncoder(node) |
| .setInt(Key.TARGET_LABEL, getLabelDefinitionId(targetLabel)); |
| } |
| } |
| } |
| |
| @override |
| visitLabel(Label node) { |
| visitNode(node); |
| LabelDefinition labelDefinition = elements.getLabelDefinition(node); |
| if (labelDefinition != null) { |
| getNodeDataEncoder(node) |
| .setInt(Key.LABEL_DEFINITION, getLabelDefinitionId(labelDefinition)); |
| } |
| } |
| |
| @override |
| visitFunctionExpression(FunctionExpression node) { |
| visitExpression(node); |
| Element function = elements.getFunctionDefinition(node); |
| if (function != null && function.isFunction && function.isLocal) { |
| // Mark root nodes of local functions; these need their own ResolvedAst. |
| getNodeDataEncoder(node).setElement(Key.FUNCTION, function); |
| serializeParameterNodes(function); |
| } |
| } |
| } |
| |
| 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]s for [element] and its nested local |
| /// functions from [objectDecoder] and adds these to [resolvedAstMap]. |
| /// [parsing] and [getBeginToken] are used for parsing the [Node] for |
| /// [element] from its source code. |
| static void deserialize( |
| Element element, |
| ObjectDecoder objectDecoder, |
| ParsingContext parsing, |
| Token getBeginToken(Uri uri, int charOffset), |
| DeserializerPlugin nativeDataDeserializer) { |
| ResolvedAstKind kind = |
| objectDecoder.getEnum(Key.KIND, ResolvedAstKind.values); |
| switch (kind) { |
| case ResolvedAstKind.PARSED: |
| deserializeParsed(element, objectDecoder, parsing, getBeginToken, |
| nativeDataDeserializer); |
| break; |
| case ResolvedAstKind.DEFAULT_CONSTRUCTOR: |
| case ResolvedAstKind.FORWARDING_CONSTRUCTOR: |
| (element as AstElementMixinZ).resolvedAst = |
| new SynthesizedResolvedAst(element, kind); |
| break; |
| case ResolvedAstKind.DEFERRED_LOAD_LIBRARY: |
| break; |
| } |
| } |
| |
| /// Deserialize the [ResolvedAst]s for the member [element] (constructor, |
| /// method, or field) and its nested closures. The [ResolvedAst]s are added |
| /// to [resolvedAstMap]. |
| static void deserializeParsed( |
| AstElementMixinZ element, |
| ObjectDecoder objectDecoder, |
| ParsingContext parsing, |
| Token getBeginToken(Uri uri, int charOffset), |
| DeserializerPlugin nativeDataDeserializer) { |
| DiagnosticReporter reporter = parsing.reporter; |
| Uri uri = objectDecoder.getUri(Key.URI); |
| |
| /// Returns the first [Token] for parsing the [Node] for [element]. |
| Token readBeginToken() { |
| int charOffset = objectDecoder.getInt(Key.OFFSET); |
| Token beginToken = getBeginToken(uri, charOffset); |
| if (beginToken == null) { |
| // TODO(johnniwinther): Handle unfound tokens by adding an erronous |
| // resolved ast kind. |
| 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, () { |
| NodeListener listener = new NodeListener( |
| parsing.getScannerOptionsFor(element), reporter, null); |
| listener.memberErrors = listener.memberErrors.prepend(false); |
| try { |
| Parser parser = new Parser(listener); |
| 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_NAME_FIELD: |
| AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| Identifier identifier = builder.identifier('_name'); |
| 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<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', |
| null, |
| builder.argumentList([]), |
| builder.returnStatement( |
| builder.reference(builder.identifier('_name')))); |
| return toStringNode; |
| case AstKind.ENUM_CONSTRUCTOR: |
| AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| VariableDefinitions indexDefinition = |
| builder.initializingFormal('index'); |
| VariableDefinitions nameDefinition = |
| builder.initializingFormal('_name'); |
| FunctionExpression constructorNode = builder.functionExpression( |
| builder.modifiers(isConst: true), |
| element.enclosingClass.name, |
| null, |
| builder.argumentList([indexDefinition, nameDefinition]), |
| builder.emptyStatement()); |
| return constructorNode; |
| case AstKind.ENUM_CONSTANT: |
| EnumConstantElementZ enumConstant = element; |
| EnumClassElement enumClass = element.enclosingClass; |
| int index = enumConstant.index; |
| AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| Identifier name = builder.identifier(element.name); |
| |
| String enumString = "${enumClass.name}.${element.name}"; |
| Expression initializer = builder.newExpression( |
| enumClass.name, |
| builder.argumentList([ |
| builder.literalInt(index), |
| builder.literalString(enumString) |
| ]), |
| 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 " |
| "${uri} @ $getOrSetOffset"); |
| } |
| } |
| return doParse((parser) { |
| parser.parseFunction(beginToken, getOrSet); |
| }); |
| } |
| } |
| |
| AstKind kind = objectDecoder.getEnum(Key.SUB_KIND, AstKind.values); |
| Node root = computeNode(kind); |
| TreeElementMapping elements = new TreeElementMapping(element); |
| AstIndexComputer indexComputer = new AstIndexComputer(); |
| List<Node> nodeList = indexComputer.nodeList; |
| root.accept(indexComputer); |
| elements.containsTryStatement = objectDecoder.getBool(Key.CONTAINS_TRY); |
| |
| Node body; |
| int bodyNodeIndex = objectDecoder.getInt(Key.BODY, isOptional: true); |
| if (bodyNodeIndex != null) { |
| assert(invariant(element, bodyNodeIndex < nodeList.length, |
| message: "Body node index ${bodyNodeIndex} out of range. " |
| "Node count: ${nodeList.length}")); |
| body = nodeList[bodyNodeIndex]; |
| } |
| |
| List<JumpTarget> jumpTargets = <JumpTarget>[]; |
| Map<JumpTarget, List<int>> jumpTargetLabels = <JumpTarget, List<int>>{}; |
| List<LabelDefinition> labelDefinitions = <LabelDefinition>[]; |
| |
| ListDecoder jumpTargetsDecoder = |
| objectDecoder.getList(Key.JUMP_TARGETS, isOptional: true); |
| if (jumpTargetsDecoder != null) { |
| for (int i = 0; i < jumpTargetsDecoder.length; i++) { |
| ObjectDecoder decoder = jumpTargetsDecoder.getObject(i); |
| ExecutableElement executableContext = |
| decoder.getElement(Key.EXECUTABLE_CONTEXT); |
| Node statement = nodeList[decoder.getInt(Key.NODE)]; |
| int nestingLevel = decoder.getInt(Key.NESTING_LEVEL); |
| JumpTarget jumpTarget = |
| new JumpTargetX(statement, nestingLevel, executableContext); |
| jumpTarget.isBreakTarget = decoder.getBool(Key.IS_BREAK_TARGET); |
| jumpTarget.isContinueTarget = decoder.getBool(Key.IS_CONTINUE_TARGET); |
| jumpTargetLabels[jumpTarget] = |
| decoder.getInts(Key.LABELS, isOptional: true); |
| jumpTargets.add(jumpTarget); |
| } |
| } |
| |
| ListDecoder labelDefinitionsDecoder = |
| objectDecoder.getList(Key.LABEL_DEFINITIONS, isOptional: true); |
| if (labelDefinitionsDecoder != null) { |
| for (int i = 0; i < labelDefinitionsDecoder.length; i++) { |
| ObjectDecoder decoder = labelDefinitionsDecoder.getObject(i); |
| Label label = nodeList[decoder.getInt(Key.NODE)]; |
| String labelName = decoder.getString(Key.NAME); |
| JumpTarget target = jumpTargets[decoder.getInt(Key.JUMP_TARGET)]; |
| LabelDefinitionX labelDefinition = |
| new LabelDefinitionX(label, labelName, target); |
| labelDefinition.isBreakTarget = decoder.getBool(Key.IS_BREAK_TARGET); |
| labelDefinition.isContinueTarget = |
| decoder.getBool(Key.IS_CONTINUE_TARGET); |
| labelDefinitions.add(labelDefinition); |
| } |
| } |
| jumpTargetLabels.forEach((JumpTargetX jumpTarget, List<int> labelIds) { |
| if (labelIds.isEmpty) return; |
| List<LabelDefinition> labels = <LabelDefinition>[]; |
| for (int labelId in labelIds) { |
| labels.add(labelDefinitions[labelId]); |
| } |
| jumpTarget.labels = labels; |
| }); |
| |
| ListDecoder dataDecoder = objectDecoder.getList(Key.DATA, isOptional: true); |
| 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 = deserializeElementReference( |
| element, Key.ELEMENT, Key.NAME, objectDecoder, |
| isOptional: true); |
| if (nodeElement != null) { |
| elements[node] = nodeElement; |
| } |
| ResolutionDartType 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); |
| } |
| ResolutionDartType 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)); |
| } |
| int targetDefinitionId = |
| objectDecoder.getInt(Key.JUMP_TARGET_DEFINITION, isOptional: true); |
| if (targetDefinitionId != null) { |
| elements.defineTarget(node, jumpTargets[targetDefinitionId]); |
| } |
| int targetOfId = |
| objectDecoder.getInt(Key.JUMP_TARGET, isOptional: true); |
| if (targetOfId != null) { |
| elements.registerTargetOf(node, jumpTargets[targetOfId]); |
| } |
| int labelDefinitionId = |
| objectDecoder.getInt(Key.LABEL_DEFINITION, isOptional: true); |
| if (labelDefinitionId != null) { |
| elements.defineLabel(node, labelDefinitions[labelDefinitionId]); |
| } |
| int targetLabelId = |
| objectDecoder.getInt(Key.TARGET_LABEL, isOptional: true); |
| if (targetLabelId != null) { |
| elements.registerTargetLabel(node, labelDefinitions[targetLabelId]); |
| } |
| ObjectDecoder nativeDataDecoder = |
| objectDecoder.getObject(Key.NATIVE, isOptional: true); |
| if (nativeDataDecoder != null) { |
| var nativeData = nativeDataDeserializer.onData(nativeDataDecoder); |
| if (nativeData != null) { |
| elements.registerNativeData(node, nativeData); |
| } |
| } |
| LocalFunctionElementZ function = |
| objectDecoder.getElement(Key.FUNCTION, isOptional: true); |
| if (function != null) { |
| FunctionExpression functionExpression = node; |
| function.resolvedAst = new ParsedResolvedAst(function, |
| functionExpression, functionExpression.body, elements, uri); |
| } |
| // TODO(johnniwinther): Remove these when inference doesn't need `.node` |
| // and `.initializer` of [ParameterElement]s. |
| ParameterElementZ parameter = |
| objectDecoder.getElement(PARAMETER_NODE, isOptional: true); |
| if (parameter != null) { |
| parameter.node = node; |
| } |
| parameter = |
| objectDecoder.getElement(PARAMETER_INITIALIZER, isOptional: true); |
| if (parameter != null) { |
| parameter.initializer = node; |
| } |
| } |
| } |
| element.resolvedAst = |
| new ParsedResolvedAst(element, root, body, elements, uri); |
| } |
| } |
| |
| const Key PARAMETER_NODE = const Key('parameter.node'); |
| const Key PARAMETER_INITIALIZER = const Key('parameter.initializer'); |