OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library dart2js.serialization.resolved_ast; |
| 6 |
| 7 import '../common.dart'; |
| 8 import '../common/resolution.dart'; |
| 9 import '../constants/expressions.dart'; |
| 10 import '../dart_types.dart'; |
| 11 import '../diagnostics/diagnostic_listener.dart'; |
| 12 import '../elements/elements.dart'; |
| 13 import '../parser/parser.dart' show Parser; |
| 14 import '../parser/listener.dart' show ParserError; |
| 15 import '../parser/node_listener.dart' show NodeListener; |
| 16 import '../resolution/enum_creator.dart'; |
| 17 import '../resolution/send_structure.dart'; |
| 18 import '../resolution/tree_elements.dart'; |
| 19 import '../tree/tree.dart'; |
| 20 import '../tokens/token.dart'; |
| 21 import '../universe/selector.dart'; |
| 22 import 'keys.dart'; |
| 23 import 'serialization.dart'; |
| 24 import 'serialization_util.dart'; |
| 25 |
| 26 /// Visitor that computes a node-index mapping. |
| 27 class AstIndexComputer extends Visitor { |
| 28 final Map<Node, int> nodeIndices = <Node, int>{}; |
| 29 final List<Node> nodeList = <Node>[]; |
| 30 |
| 31 @override |
| 32 visitNode(Node node) { |
| 33 nodeIndices.putIfAbsent(node, () { |
| 34 // Some nodes (like Modifier and empty NodeList) can be reused. |
| 35 nodeList.add(node); |
| 36 return nodeIndices.length; |
| 37 }); |
| 38 node.visitChildren(this); |
| 39 } |
| 40 } |
| 41 |
| 42 /// The kind of AST node. Used for determining how to deserialize |
| 43 /// [ResolvedAst]s. |
| 44 enum AstKind { |
| 45 ENUM_CONSTRUCTOR, |
| 46 ENUM_CONSTANT, |
| 47 ENUM_INDEX_FIELD, |
| 48 ENUM_VALUES_FIELD, |
| 49 ENUM_TO_STRING, |
| 50 FACTORY, |
| 51 FIELD, |
| 52 FUNCTION, |
| 53 } |
| 54 |
| 55 /// Serializer for [ResolvedAst]s. |
| 56 class ResolvedAstSerializer extends Visitor { |
| 57 final ObjectEncoder objectEncoder; |
| 58 final ResolvedAst resolvedAst; |
| 59 final AstIndexComputer indexComputer = new AstIndexComputer(); |
| 60 final Map<int, ObjectEncoder> nodeData = <int, ObjectEncoder>{}; |
| 61 ListEncoder _nodeDataEncoder; |
| 62 |
| 63 ResolvedAstSerializer(this.objectEncoder, this.resolvedAst); |
| 64 |
| 65 AstElement get element => resolvedAst.element; |
| 66 |
| 67 TreeElements get elements => resolvedAst.elements; |
| 68 |
| 69 Node get root => resolvedAst.node; |
| 70 |
| 71 Map<Node, int> get nodeIndices => indexComputer.nodeIndices; |
| 72 List<Node> get nodeList => indexComputer.nodeList; |
| 73 |
| 74 /// Serializes [resolvedAst] into [objectEncoder]. |
| 75 void serialize() { |
| 76 objectEncoder.setUri( |
| 77 Key.URI, |
| 78 elements.analyzedElement.compilationUnit.script.resourceUri, |
| 79 elements.analyzedElement.compilationUnit.script.resourceUri); |
| 80 AstKind kind; |
| 81 if (element.enclosingClass is EnumClassElement) { |
| 82 if (element.name == 'index') { |
| 83 kind = AstKind.ENUM_INDEX_FIELD; |
| 84 } else if (element.name == 'values') { |
| 85 kind = AstKind.ENUM_VALUES_FIELD; |
| 86 } else if (element.name == 'toString') { |
| 87 kind = AstKind.ENUM_TO_STRING; |
| 88 } else if (element.isConstructor) { |
| 89 kind = AstKind.ENUM_CONSTRUCTOR; |
| 90 } else { |
| 91 assert(invariant(element, element.isConst, |
| 92 message: "Unexpected enum member: $element")); |
| 93 kind = AstKind.ENUM_CONSTANT; |
| 94 } |
| 95 } else { |
| 96 // [element] has a body that we'll need to re-parse. We store where to |
| 97 // start parsing from. |
| 98 objectEncoder.setInt(Key.OFFSET, root.getBeginToken().charOffset); |
| 99 if (element.isFactoryConstructor) { |
| 100 kind = AstKind.FACTORY; |
| 101 } else if (element.isField) { |
| 102 kind = AstKind.FIELD; |
| 103 } else { |
| 104 kind = AstKind.FUNCTION; |
| 105 FunctionExpression functionExpression = root.asFunctionExpression(); |
| 106 if (functionExpression.getOrSet != null) { |
| 107 // Getters/setters need the get/set token to be parsed. |
| 108 objectEncoder.setInt( |
| 109 Key.GET_OR_SET, functionExpression.getOrSet.charOffset); |
| 110 } |
| 111 } |
| 112 } |
| 113 objectEncoder.setEnum(Key.KIND, kind); |
| 114 root.accept(indexComputer); |
| 115 root.accept(this); |
| 116 } |
| 117 |
| 118 /// Computes the [ListEncoder] for serializing data for nodes. |
| 119 ListEncoder get nodeDataEncoder { |
| 120 if (_nodeDataEncoder == null) { |
| 121 _nodeDataEncoder = objectEncoder.createList(Key.DATA); |
| 122 } |
| 123 return _nodeDataEncoder; |
| 124 } |
| 125 |
| 126 /// Computes the [ObjectEncoder] for serializing data for [node]. |
| 127 ObjectEncoder getNodeDataEncoder(Node node) { |
| 128 int id = nodeIndices[node]; |
| 129 return nodeData.putIfAbsent(id, () { |
| 130 ObjectEncoder objectEncoder = nodeDataEncoder.createObject(); |
| 131 objectEncoder.setInt(Key.ID, id); |
| 132 return objectEncoder; |
| 133 }); |
| 134 } |
| 135 |
| 136 @override |
| 137 visitNode(Node node) { |
| 138 Element nodeElement = elements[node]; |
| 139 if (nodeElement != null) { |
| 140 if (nodeElement.enclosingClass != null && |
| 141 nodeElement.enclosingClass.isUnnamedMixinApplication) { |
| 142 // TODO(johnniwinther): Handle references to members of unnamed mixin |
| 143 // applications. |
| 144 } else { |
| 145 getNodeDataEncoder(node).setElement(Key.ELEMENT, nodeElement); |
| 146 } |
| 147 } |
| 148 DartType type = elements.getType(node); |
| 149 if (type != null) { |
| 150 getNodeDataEncoder(node).setType(Key.TYPE, type); |
| 151 } |
| 152 Selector selector = elements.getSelector(node); |
| 153 if (selector != null) { |
| 154 serializeSelector( |
| 155 selector, getNodeDataEncoder(node).createObject(Key.SELECTOR)); |
| 156 } |
| 157 ConstantExpression constant = elements.getConstant(node); |
| 158 if (constant != null) { |
| 159 getNodeDataEncoder(node).setConstant(Key.CONSTANT, constant); |
| 160 } |
| 161 DartType cachedType = elements.typesCache[node]; |
| 162 if (cachedType != null) { |
| 163 getNodeDataEncoder(node).setType(Key.CACHED_TYPE, cachedType); |
| 164 } |
| 165 // TODO(johnniwinther): Serialize [JumpTarget]s. |
| 166 node.visitChildren(this); |
| 167 } |
| 168 |
| 169 @override |
| 170 visitSend(Send node) { |
| 171 visitExpression(node); |
| 172 SendStructure structure = elements.getSendStructure(node); |
| 173 if (structure != null) { |
| 174 serializeSendStructure( |
| 175 structure, getNodeDataEncoder(node).createObject(Key.SEND_STRUCTURE)); |
| 176 } |
| 177 } |
| 178 |
| 179 @override |
| 180 visitNewExpression(NewExpression node) { |
| 181 visitExpression(node); |
| 182 NewStructure structure = elements.getNewStructure(node); |
| 183 if (structure != null) { |
| 184 serializeNewStructure( |
| 185 structure, getNodeDataEncoder(node).createObject(Key.NEW_STRUCTURE)); |
| 186 } |
| 187 } |
| 188 |
| 189 @override |
| 190 visitGotoStatement(GotoStatement node) { |
| 191 visitStatement(node); |
| 192 // TODO(johnniwinther): Serialize [JumpTarget]s and [LabelDefinition]s. |
| 193 } |
| 194 |
| 195 @override |
| 196 visitLabel(Label node) { |
| 197 visitNode(node); |
| 198 // TODO(johnniwinther): Serialize[LabelDefinition]s. |
| 199 } |
| 200 } |
| 201 |
| 202 class ResolvedAstDeserializer { |
| 203 /// Find the [Token] at [offset] searching through successors of [token]. |
| 204 static Token findTokenInStream(Token token, int offset) { |
| 205 while (token.charOffset <= offset && token.next != token) { |
| 206 if (token.charOffset == offset) { |
| 207 return token; |
| 208 } |
| 209 token = token.next; |
| 210 } |
| 211 return null; |
| 212 } |
| 213 |
| 214 /// Deserializes the [ResolvedAst] for [element] from [objectDecoder]. |
| 215 /// [parsing] and [getBeginToken] are used for parsing the [Node] for |
| 216 /// [element] from its source code. |
| 217 static ResolvedAst deserialize(Element element, ObjectDecoder objectDecoder, |
| 218 Parsing parsing, Token getBeginToken(Uri uri, int charOffset)) { |
| 219 CompilationUnitElement compilationUnit = element.compilationUnit; |
| 220 DiagnosticReporter reporter = parsing.reporter; |
| 221 |
| 222 /// Returns the first [Token] for parsing the [Node] for [element]. |
| 223 Token readBeginToken() { |
| 224 Uri uri = objectDecoder.getUri(Key.URI); |
| 225 int charOffset = objectDecoder.getInt(Key.OFFSET); |
| 226 Token beginToken = getBeginToken(uri, charOffset); |
| 227 if (beginToken == null) { |
| 228 reporter.internalError( |
| 229 element, "No token found for $element in $uri @ $charOffset"); |
| 230 } |
| 231 return beginToken; |
| 232 } |
| 233 |
| 234 /// Create the [Node] for the element by parsing the source code. |
| 235 Node doParse(parse(Parser parser)) { |
| 236 return parsing.measure(() { |
| 237 return reporter.withCurrentElement(element, () { |
| 238 CompilationUnitElement unit = element.compilationUnit; |
| 239 NodeListener listener = new NodeListener( |
| 240 parsing.getScannerOptionsFor(element), reporter, null); |
| 241 listener.memberErrors = listener.memberErrors.prepend(false); |
| 242 try { |
| 243 Parser parser = new Parser(listener, parsing.parserOptions); |
| 244 parse(parser); |
| 245 } on ParserError catch (e) { |
| 246 reporter.internalError(element, '$e'); |
| 247 } |
| 248 return listener.popNode(); |
| 249 }); |
| 250 }); |
| 251 } |
| 252 |
| 253 /// Computes the [Node] for the element based on the [AstKind]. |
| 254 Node computeNode(AstKind kind) { |
| 255 switch (kind) { |
| 256 case AstKind.ENUM_INDEX_FIELD: |
| 257 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| 258 Identifier identifier = builder.identifier('index'); |
| 259 VariableDefinitions node = new VariableDefinitions( |
| 260 null, |
| 261 builder.modifiers(isFinal: true), |
| 262 new NodeList.singleton(identifier)); |
| 263 return node; |
| 264 case AstKind.ENUM_VALUES_FIELD: |
| 265 EnumClassElement enumClass = element.enclosingClass; |
| 266 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| 267 List<FieldElement> enumValues = <FieldElement>[]; |
| 268 List<Node> valueReferences = <Node>[]; |
| 269 for (EnumConstantElement enumConstant in enumClass.enumValues) { |
| 270 AstBuilder valueBuilder = |
| 271 new AstBuilder(enumConstant.sourcePosition.begin); |
| 272 Identifier name = valueBuilder.identifier(enumConstant.name); |
| 273 |
| 274 // Add reference for the `values` field. |
| 275 valueReferences.add(valueBuilder.reference(name)); |
| 276 } |
| 277 |
| 278 Identifier valuesIdentifier = builder.identifier('values'); |
| 279 // TODO(johnniwinther): Add type argument. |
| 280 Expression initializer = |
| 281 builder.listLiteral(valueReferences, isConst: true); |
| 282 |
| 283 Node definition = |
| 284 builder.createDefinition(valuesIdentifier, initializer); |
| 285 VariableDefinitions node = new VariableDefinitions( |
| 286 null, |
| 287 builder.modifiers(isStatic: true, isConst: true), |
| 288 new NodeList.singleton(definition)); |
| 289 return node; |
| 290 case AstKind.ENUM_TO_STRING: |
| 291 EnumClassElement enumClass = element.enclosingClass; |
| 292 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| 293 List<LiteralMapEntry> mapEntries = <LiteralMapEntry>[]; |
| 294 for (EnumConstantElement enumConstant in enumClass.enumValues) { |
| 295 AstBuilder valueBuilder = |
| 296 new AstBuilder(enumConstant.sourcePosition.begin); |
| 297 Identifier name = valueBuilder.identifier(enumConstant.name); |
| 298 |
| 299 // Add map entry for `toString` implementation. |
| 300 mapEntries.add(valueBuilder.mapLiteralEntry( |
| 301 valueBuilder.literalInt(enumConstant.index), |
| 302 valueBuilder |
| 303 .literalString('${enumClass.name}.${name.source}'))); |
| 304 } |
| 305 |
| 306 // TODO(johnniwinther): Support return type. Note `String` might be |
| 307 // prefixed or not imported within the current library. |
| 308 FunctionExpression toStringNode = builder.functionExpression( |
| 309 Modifiers.EMPTY, |
| 310 'toString', |
| 311 builder.argumentList([]), |
| 312 builder.returnStatement(builder.indexGet( |
| 313 builder.mapLiteral(mapEntries, isConst: true), |
| 314 builder.reference(builder.identifier('index'))))); |
| 315 return toStringNode; |
| 316 case AstKind.ENUM_CONSTRUCTOR: |
| 317 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| 318 VariableDefinitions indexDefinition = |
| 319 builder.initializingFormal('index'); |
| 320 FunctionExpression constructorNode = builder.functionExpression( |
| 321 builder.modifiers(isConst: true), |
| 322 element.enclosingClass.name, |
| 323 builder.argumentList([indexDefinition]), |
| 324 builder.emptyStatement()); |
| 325 return constructorNode; |
| 326 case AstKind.ENUM_CONSTANT: |
| 327 EnumConstantElement enumConstant = element; |
| 328 EnumClassElement enumClass = element.enclosingClass; |
| 329 int index = enumConstant.index; |
| 330 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); |
| 331 Identifier name = builder.identifier(element.name); |
| 332 |
| 333 Expression initializer = builder.newExpression( |
| 334 enumClass.name, builder.argumentList([builder.literalInt(index)]), |
| 335 isConst: true); |
| 336 SendSet definition = builder.createDefinition(name, initializer); |
| 337 |
| 338 VariableDefinitions node = new VariableDefinitions( |
| 339 null, |
| 340 builder.modifiers(isStatic: true, isConst: true), |
| 341 new NodeList.singleton(definition)); |
| 342 return node; |
| 343 case AstKind.FACTORY: |
| 344 Token beginToken = readBeginToken(); |
| 345 return doParse((parser) => parser.parseFactoryMethod(beginToken)); |
| 346 case AstKind.FIELD: |
| 347 Token beginToken = readBeginToken(); |
| 348 return doParse((parser) => parser.parseMember(beginToken)); |
| 349 case AstKind.FUNCTION: |
| 350 Token beginToken = readBeginToken(); |
| 351 int getOrSetOffset = |
| 352 objectDecoder.getInt(Key.GET_OR_SET, isOptional: true); |
| 353 Token getOrSet; |
| 354 if (getOrSetOffset != null) { |
| 355 getOrSet = findTokenInStream(beginToken, getOrSetOffset); |
| 356 if (getOrSet == null) { |
| 357 reporter.internalError( |
| 358 element, |
| 359 "No token found for $element in " |
| 360 "${objectDecoder.getUri(Key.URI)} @ $getOrSetOffset"); |
| 361 } |
| 362 } |
| 363 return doParse((parser) { |
| 364 parser.parseFunction(beginToken, getOrSet); |
| 365 }); |
| 366 } |
| 367 } |
| 368 |
| 369 AstKind kind = objectDecoder.getEnum(Key.KIND, AstKind.values); |
| 370 Node root = computeNode(kind); |
| 371 TreeElementMapping elements = new TreeElementMapping(element); |
| 372 AstIndexComputer indexComputer = new AstIndexComputer(); |
| 373 Map<Node, int> nodeIndices = indexComputer.nodeIndices; |
| 374 List<Node> nodeList = indexComputer.nodeList; |
| 375 root.accept(indexComputer); |
| 376 ListDecoder dataDecoder = objectDecoder.getList(Key.DATA); |
| 377 if (dataDecoder != null) { |
| 378 for (int i = 0; i < dataDecoder.length; i++) { |
| 379 ObjectDecoder objectDecoder = dataDecoder.getObject(i); |
| 380 int id = objectDecoder.getInt(Key.ID); |
| 381 Node node = nodeList[id]; |
| 382 Element nodeElement = |
| 383 objectDecoder.getElement(Key.ELEMENT, isOptional: true); |
| 384 if (nodeElement != null) { |
| 385 elements[node] = nodeElement; |
| 386 } |
| 387 DartType type = objectDecoder.getType(Key.TYPE, isOptional: true); |
| 388 if (type != null) { |
| 389 elements.setType(node, type); |
| 390 } |
| 391 ObjectDecoder selectorDecoder = |
| 392 objectDecoder.getObject(Key.SELECTOR, isOptional: true); |
| 393 if (selectorDecoder != null) { |
| 394 elements.setSelector(node, deserializeSelector(selectorDecoder)); |
| 395 } |
| 396 ConstantExpression constant = |
| 397 objectDecoder.getConstant(Key.CONSTANT, isOptional: true); |
| 398 if (constant != null) { |
| 399 elements.setConstant(node, constant); |
| 400 } |
| 401 DartType cachedType = |
| 402 objectDecoder.getType(Key.CACHED_TYPE, isOptional: true); |
| 403 if (cachedType != null) { |
| 404 elements.typesCache[node] = cachedType; |
| 405 } |
| 406 ObjectDecoder sendStructureDecoder = |
| 407 objectDecoder.getObject(Key.SEND_STRUCTURE, isOptional: true); |
| 408 if (sendStructureDecoder != null) { |
| 409 elements.setSendStructure( |
| 410 node, deserializeSendStructure(sendStructureDecoder)); |
| 411 } |
| 412 ObjectDecoder newStructureDecoder = |
| 413 objectDecoder.getObject(Key.NEW_STRUCTURE, isOptional: true); |
| 414 if (newStructureDecoder != null) { |
| 415 elements.setNewStructure( |
| 416 node, deserializeNewStructure(newStructureDecoder)); |
| 417 } |
| 418 } |
| 419 } |
| 420 return new ResolvedAst(element, root, elements); |
| 421 } |
| 422 } |
OLD | NEW |