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 | |
14 Parser; | |
15 import '../parser/listener.dart' show | |
16 ParserError; | |
17 import '../parser/node_listener.dart' show | |
18 NodeListener; | |
19 import '../resolution/enum_creator.dart'; | |
20 import '../resolution/send_structure.dart'; | |
21 import '../resolution/tree_elements.dart'; | |
22 import '../tree/tree.dart'; | |
23 import '../tokens/token.dart'; | |
24 import '../universe/selector.dart'; | |
25 import 'keys.dart'; | |
26 import 'serialization.dart'; | |
27 import 'serialization_util.dart'; | |
28 | |
29 /// Visitor that computes a node-index mapping. | |
30 class AstIndexComputer extends Visitor { | |
31 final Map<Node, int> nodeIndices = <Node, int>{}; | |
32 final List<Node> nodeList = <Node>[]; | |
33 | |
34 @override | |
35 visitNode(Node node) { | |
36 nodeIndices.putIfAbsent(node, () { | |
37 // Some nodes (like Modifier and empty NodeList) can be reused. | |
38 nodeList.add(node); | |
39 return nodeIndices.length; | |
40 }); | |
41 node.visitChildren(this); | |
42 } | |
43 } | |
44 | |
45 /// The kind of AST node. Used for determining how to deserialize | |
46 /// [ResolvedAst]s. | |
47 enum AstKind { | |
48 ENUM_CONSTRUCTOR, | |
49 ENUM_CONSTANT, | |
50 ENUM_INDEX_FIELD, | |
51 ENUM_VALUES_FIELD, | |
52 ENUM_TO_STRING, | |
53 FACTORY, | |
54 FIELD, | |
55 FUNCTION, | |
56 } | |
57 | |
58 /// Serializer for [ResolvedAst]s. | |
59 class ResolvedAstSerializer extends Visitor { | |
60 final ObjectEncoder objectEncoder; | |
61 final ResolvedAst resolvedAst; | |
62 final AstIndexComputer indexComputer = new AstIndexComputer(); | |
63 final Map<int, ObjectEncoder> nodeData = <int, ObjectEncoder>{}; | |
64 ListEncoder _nodeDataEncoder; | |
65 | |
66 ResolvedAstSerializer(this.objectEncoder, this.resolvedAst); | |
67 | |
68 AstElement get element => resolvedAst.element; | |
69 | |
70 TreeElements get elements => resolvedAst.elements; | |
71 | |
72 Node get root => resolvedAst.node; | |
73 | |
74 Map<Node, int> get nodeIndices => indexComputer.nodeIndices; | |
75 List<Node> get nodeList => indexComputer.nodeList; | |
76 | |
77 /// Serializes [resolvedAst] into [objectEncoder]. | |
78 void serialize() { | |
79 objectEncoder.setUri(Key.URI, | |
80 elements.analyzedElement.compilationUnit.script.resourceUri, | |
Siggi Cherem (dart-lang)
2016/04/08 16:55:33
eventually we should look into what URI to use - r
Johnni Winther
2016/04/11 08:54:04
This is a known issue for the serialization itself
Siggi Cherem (dart-lang)
2016/04/11 17:48:07
could we file a bug/add a todo to track this issue
Johnni Winther
2016/04/12 08:05:47
Done: #26244
| |
81 elements.analyzedElement.compilationUnit.script.resourceUri); | |
82 AstKind kind; | |
83 if (element.enclosingClass is EnumClassElement) { | |
84 if (element.name == 'index') { | |
85 kind = AstKind.ENUM_INDEX_FIELD; | |
86 } else if (element.name == 'values') { | |
87 kind = AstKind.ENUM_VALUES_FIELD; | |
88 } else if (element.name == 'toString') { | |
89 kind = AstKind.ENUM_TO_STRING; | |
90 } else if (element.isConstructor) { | |
91 kind = AstKind.ENUM_CONSTRUCTOR; | |
92 } else { | |
93 assert(invariant(element, element.isConst, | |
94 message: "Unexpected enum member: $element")); | |
95 kind = AstKind.ENUM_CONSTANT; | |
96 } | |
97 } else if (element.isFactoryConstructor) { | |
Siggi Cherem (dart-lang)
2016/04/08 16:55:33
nit: small refactor just to make it more self expl
Johnni Winther
2016/04/11 08:54:04
Done.
| |
98 objectEncoder.setInt(Key.OFFSET, root.getBeginToken().charOffset); | |
99 kind = AstKind.FACTORY; | |
100 } else if (element.isField) { | |
101 objectEncoder.setInt(Key.OFFSET, root.getBeginToken().charOffset); | |
Siggi Cherem (dart-lang)
2016/04/08 16:55:33
question: as we make progress we'll drop the token
Johnni Winther
2016/04/11 08:54:04
Yes. Source information such replace tokens/nodes
| |
102 kind = AstKind.FIELD; | |
103 } else { | |
104 objectEncoder.setInt(Key.OFFSET, root.getBeginToken().charOffset); | |
105 kind = AstKind.FUNCTION; | |
106 FunctionExpression functionExpression = root.asFunctionExpression(); | |
107 if (functionExpression.getOrSet != null) { | |
108 objectEncoder.setInt( | |
109 Key.GET_OR_SET, functionExpression.getOrSet.charOffset); | |
Siggi Cherem (dart-lang)
2016/04/08 16:55:33
follow up question: this seems trickier to do with
Johnni Winther
2016/04/11 08:54:04
This is only for reparsing. When we have a complet
| |
110 } | |
111 } | |
112 objectEncoder.setEnum(Key.KIND, kind); | |
113 root.accept(indexComputer); | |
114 root.accept(this); | |
115 } | |
116 | |
117 /// Computes the [ListEncoder] for serializing data for nodes. | |
118 ListEncoder get nodeDataEncoder { | |
119 if (_nodeDataEncoder == null) { | |
120 _nodeDataEncoder = objectEncoder.createList(Key.DATA); | |
121 } | |
122 return _nodeDataEncoder; | |
123 } | |
124 | |
125 /// Computes the [ObjectEncoder] for serializing data for [node]. | |
126 ObjectEncoder getNodeDataEncoder(Node node) { | |
127 int id = nodeIndices[node]; | |
128 return nodeData.putIfAbsent(id, () { | |
129 ObjectEncoder objectEncoder = nodeDataEncoder.createObject(); | |
130 objectEncoder.setInt(Key.ID, id); | |
131 return objectEncoder; | |
132 }); | |
133 } | |
134 | |
135 @override | |
136 visitNode(Node node) { | |
137 Element nodeElement = elements[node]; | |
138 if (nodeElement != null) { | |
139 if (nodeElement.enclosingClass != null && | |
140 nodeElement.enclosingClass.isUnnamedMixinApplication) { | |
141 // TODO(johnniwinther): Handle references to members of unnamed mixin | |
142 // applications. | |
143 } else { | |
144 getNodeDataEncoder(node).setElement(Key.ELEMENT, nodeElement); | |
145 } | |
146 } | |
147 DartType type = elements.getType(node); | |
148 if (type != null) { | |
149 getNodeDataEncoder(node).setType(Key.TYPE, type); | |
150 } | |
151 Selector selector = elements.getSelector(node); | |
152 if (selector != null) { | |
153 serializeSelector( | |
154 selector, | |
155 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(structure, | |
175 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(structure, | |
185 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( | |
218 Element element, | |
219 ObjectDecoder objectDecoder, | |
220 Parsing parsing, | |
221 Token getBeginToken(Uri uri, int charOffset)) { | |
222 CompilationUnitElement compilationUnit = element.compilationUnit; | |
223 DiagnosticReporter reporter = parsing.reporter; | |
224 | |
225 /// Returns the first [Token] for parsing the [Node] for [element]. | |
226 Token readBeginToken() { | |
227 Uri uri = objectDecoder.getUri(Key.URI); | |
228 int charOffset = objectDecoder.getInt(Key.OFFSET); | |
229 Token beginToken = getBeginToken(uri, charOffset); | |
230 if (beginToken == null) { | |
231 reporter.internalError( | |
232 element, "No token found for $element in $uri @ $charOffset"); | |
233 } | |
234 return beginToken; | |
235 } | |
236 | |
237 /// Create the [Node] for the element by parsing the source code. | |
238 Node doParse(parse(Parser parser)) { | |
239 return parsing.measure(() { | |
240 return reporter.withCurrentElement(element, () { | |
241 CompilationUnitElement unit = element.compilationUnit; | |
242 NodeListener listener = new NodeListener( | |
243 parsing.getScannerOptionsFor(element), reporter, null); | |
244 listener.memberErrors = listener.memberErrors.prepend(false); | |
245 try { | |
246 Parser parser = new Parser(listener, parsing.parserOptions); | |
247 parse(parser); | |
248 } on ParserError catch (e) { | |
249 reporter.internalError(element, '$e'); | |
250 } | |
251 return listener.popNode(); | |
252 }); | |
253 }); | |
254 } | |
255 | |
256 /// Computes the [Node] for the element based on the [AstKind]. | |
257 Node computeNode(AstKind kind) { | |
258 switch (kind) { | |
259 case AstKind.ENUM_INDEX_FIELD: | |
260 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); | |
261 Identifier identifier = builder.identifier('index'); | |
262 VariableDefinitions node = new VariableDefinitions( | |
263 null, | |
264 builder.modifiers(isFinal: true), | |
265 new NodeList.singleton(identifier)); | |
266 return node; | |
267 case AstKind.ENUM_VALUES_FIELD: | |
268 EnumClassElement enumClass = element.enclosingClass; | |
269 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); | |
270 List<FieldElement> enumValues = <FieldElement>[]; | |
271 List<Node> valueReferences = <Node>[]; | |
272 for (EnumConstantElement enumConstant in enumClass.enumValues) { | |
273 AstBuilder valueBuilder = | |
274 new AstBuilder(enumConstant.sourcePosition.begin); | |
275 Identifier name = valueBuilder.identifier(enumConstant.name); | |
276 | |
277 // Add reference for the `values` field. | |
278 valueReferences.add(valueBuilder.reference(name)); | |
279 } | |
280 | |
281 Identifier valuesIdentifier = builder.identifier('values'); | |
282 // TODO(johnniwinther): Add type argument. | |
283 Expression initializer = builder.listLiteral( | |
284 valueReferences, isConst: true); | |
285 | |
286 Node definition = | |
287 builder.createDefinition(valuesIdentifier, initializer); | |
288 VariableDefinitions node = new VariableDefinitions( | |
289 null, | |
290 builder.modifiers(isStatic: true, isConst: true), | |
291 new NodeList.singleton(definition)); | |
292 return node; | |
293 case AstKind.ENUM_TO_STRING: | |
294 EnumClassElement enumClass = element.enclosingClass; | |
295 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); | |
296 List<LiteralMapEntry> mapEntries = <LiteralMapEntry>[]; | |
297 for (EnumConstantElement enumConstant in enumClass.enumValues) { | |
298 AstBuilder valueBuilder = | |
299 new AstBuilder(enumConstant.sourcePosition.begin); | |
300 Identifier name = valueBuilder.identifier(enumConstant.name); | |
301 | |
302 // Add map entry for `toString` implementation. | |
303 mapEntries.add(valueBuilder.mapLiteralEntry( | |
304 valueBuilder.literalInt(enumConstant.index), | |
305 valueBuilder.literalString( | |
306 '${enumClass.name}.${name.source}'))); | |
307 } | |
308 | |
309 // TODO(johnniwinther): Support return type. Note `String` might be | |
310 // prefixed or not imported within the current library. | |
311 FunctionExpression toStringNode = builder.functionExpression( | |
312 Modifiers.EMPTY, | |
313 'toString', | |
314 builder.argumentList([]), | |
315 builder.returnStatement( | |
316 builder.indexGet( | |
317 builder.mapLiteral(mapEntries, isConst: true), | |
318 builder.reference(builder.identifier('index'))) | |
319 ) | |
320 ); | |
321 return toStringNode; | |
322 case AstKind.ENUM_CONSTRUCTOR: | |
323 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); | |
324 VariableDefinitions indexDefinition = | |
325 builder.initializingFormal('index'); | |
326 FunctionExpression constructorNode = builder.functionExpression( | |
327 builder.modifiers(isConst: true), | |
328 element.enclosingClass.name, | |
329 builder.argumentList([indexDefinition]), | |
330 builder.emptyStatement()); | |
331 return constructorNode; | |
332 case AstKind.ENUM_CONSTANT: | |
333 EnumConstantElement enumConstant = element; | |
334 EnumClassElement enumClass = element.enclosingClass; | |
335 int index = enumConstant.index; | |
336 AstBuilder builder = new AstBuilder(element.sourcePosition.begin); | |
337 Identifier name = builder.identifier(element.name); | |
338 | |
339 Expression initializer = builder.newExpression( | |
340 enumClass.name, | |
341 builder.argumentList([builder.literalInt(index)]), | |
342 isConst: true); | |
343 SendSet definition = builder.createDefinition(name, initializer); | |
344 | |
345 VariableDefinitions node = new VariableDefinitions( | |
346 null, | |
347 builder.modifiers(isStatic: true, isConst: true), | |
348 new NodeList.singleton(definition)); | |
349 return node; | |
350 case AstKind.FACTORY: | |
351 Token beginToken = readBeginToken(); | |
352 return doParse((parser) => parser.parseFactoryMethod(beginToken)); | |
353 case AstKind.FIELD: | |
354 Token beginToken = readBeginToken(); | |
355 return doParse((parser) => parser.parseMember(beginToken)); | |
356 case AstKind.FUNCTION: | |
357 Token beginToken = readBeginToken(); | |
358 int getOrSetOffset = | |
359 objectDecoder.getInt(Key.GET_OR_SET, isOptional: true); | |
360 Token getOrSet; | |
361 if (getOrSetOffset != null) { | |
362 getOrSet = findTokenInStream(beginToken, getOrSetOffset); | |
363 if (getOrSet == null) { | |
364 reporter.internalError( | |
365 element, | |
366 "No token found for $element in " | |
367 "${objectDecoder.getUri(Key.URI)} @ $getOrSetOffset"); | |
368 } | |
369 } | |
370 return doParse((parser) { | |
371 parser.parseFunction(beginToken, getOrSet); | |
372 }); | |
373 } | |
374 } | |
375 | |
376 AstKind kind = objectDecoder.getEnum(Key.KIND, AstKind.values); | |
377 Node root = computeNode(kind); | |
378 TreeElementMapping elements = new TreeElementMapping(element); | |
379 AstIndexComputer indexComputer = new AstIndexComputer(); | |
380 Map<Node, int> nodeIndices = indexComputer.nodeIndices; | |
381 List<Node> nodeList = indexComputer.nodeList; | |
382 root.accept(indexComputer); | |
383 ListDecoder dataDecoder = objectDecoder.getList(Key.DATA); | |
384 if (dataDecoder != null) { | |
385 for (int i = 0; i < dataDecoder.length; i++) { | |
386 ObjectDecoder objectDecoder = dataDecoder.getObject(i); | |
387 int id = objectDecoder.getInt(Key.ID); | |
388 Node node = nodeList[id]; | |
389 Element nodeElement = | |
390 objectDecoder.getElement(Key.ELEMENT, isOptional: true); | |
391 if (nodeElement != null) { | |
392 elements[node] = nodeElement; | |
393 } | |
394 DartType type = objectDecoder.getType(Key.TYPE, isOptional: true); | |
395 if (type != null) { | |
396 elements.setType(node, type); | |
397 } | |
398 ObjectDecoder selectorDecoder = | |
399 objectDecoder.getObject(Key.SELECTOR, isOptional: true); | |
400 if (selectorDecoder != null) { | |
401 elements.setSelector(node, deserializeSelector(selectorDecoder)); | |
402 } | |
403 ConstantExpression constant = | |
404 objectDecoder.getConstant(Key.CONSTANT, isOptional: true); | |
405 if (constant != null) { | |
406 elements.setConstant(node, constant); | |
407 } | |
408 DartType cachedType = | |
409 objectDecoder.getType(Key.CACHED_TYPE, isOptional: true); | |
410 if (cachedType != null) { | |
411 elements.typesCache[node] = cachedType; | |
412 } | |
413 ObjectDecoder sendStructureDecoder = | |
414 objectDecoder.getObject(Key.SEND_STRUCTURE, isOptional: true); | |
415 if (sendStructureDecoder != null) { | |
416 elements.setSendStructure(node, | |
417 deserializeSendStructure(sendStructureDecoder)); | |
418 } | |
419 ObjectDecoder newStructureDecoder = | |
420 objectDecoder.getObject(Key.NEW_STRUCTURE, isOptional: true); | |
421 if (newStructureDecoder != null) { | |
422 elements.setNewStructure(node, | |
423 deserializeNewStructure(newStructureDecoder)); | |
424 } | |
425 } | |
426 } | |
427 return new ResolvedAst(element, root, elements); | |
428 } | |
429 } | |
430 | |
431 | |
OLD | NEW |