OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 dart_tree_printer; | |
6 | |
7 import '../common.dart'; | |
8 import '../constants/values.dart' as values; | |
9 import '../dart_types.dart' as types; | |
10 import '../elements/elements.dart' as elements; | |
11 import '../resolution/tree_elements.dart' show TreeElementMapping; | |
12 import '../tokens/token.dart'; | |
13 import '../tokens/token_constants.dart'; | |
14 import '../tokens/precedence.dart'; | |
15 import '../tokens/precedence_constants.dart'; | |
16 import '../tree/tree.dart' as tree; | |
17 import '../util/util.dart'; | |
18 import 'backend_ast_nodes.dart'; | |
19 import 'backend_ast_emitter.dart' show TypeGenerator; | |
20 | |
21 /// Translates the backend AST to Dart frontend AST. | |
22 tree.Node emit(TreeElementMapping treeElements, RootNode root) { | |
23 return new TreePrinter(treeElements).makeDefinition(root); | |
24 } | |
25 | |
26 /// If true, the unparser will insert a coment in front of every function | |
27 /// it emits. This helps indicate which functions were translated by the new | |
28 /// backend. | |
29 bool INSERT_NEW_BACKEND_COMMENT = | |
30 const bool.fromEnvironment("INSERT_NEW_BACKEND_COMMENT"); | |
31 | |
32 /// Converts backend ASTs to frontend ASTs. | |
33 class TreePrinter { | |
34 TreeElementMapping treeElements; | |
35 | |
36 TreePrinter([this.treeElements]); | |
37 | |
38 tree.Node makeDefinition(RootNode node) { | |
39 if (node is FieldDefinition) { | |
40 tree.Node definition; | |
41 if (node.initializer == null) { | |
42 definition = makeIdentifier(node.element.name); | |
43 } else { | |
44 definition = new tree.SendSet( | |
45 null, | |
46 makeIdentifier(node.element.name), | |
47 new tree.Operator(assignmentToken("=")), | |
48 singleton(makeExpression(node.initializer))); | |
49 } | |
50 setElement(definition, node.element, node); | |
51 return new tree.VariableDefinitions( | |
52 null, // TODO(sigurdm): Type | |
53 makeVarModifiers( | |
54 useVar: true, | |
55 isFinal: node.element.isFinal, | |
56 isStatic: node.element.isStatic, | |
57 isConst: node.element.isConst), | |
58 makeList(null, [definition], close: semicolon)); | |
59 } else if (node is FunctionExpression) { | |
60 return makeExpression(node); | |
61 } else { | |
62 assert(false); | |
63 return null; | |
64 } | |
65 } | |
66 | |
67 void setElement(tree.Node node, elements.Element element, source) { | |
68 if (treeElements != null) { | |
69 if (element == null) { | |
70 throw "Missing element from ${source}"; | |
71 } | |
72 treeElements[node] = element; | |
73 } | |
74 } | |
75 | |
76 void setType(tree.Node node, types.DartType type, source) { | |
77 if (treeElements != null) { | |
78 if (type == null) { | |
79 throw "Missing type from ${source}"; | |
80 } | |
81 treeElements.setType(node, type); | |
82 } | |
83 } | |
84 | |
85 // Group tokens: () [] {} <> | |
86 static BeginGroupToken makeGroup(PrecedenceInfo open, PrecedenceInfo close) { | |
87 BeginGroupToken openTok = new BeginGroupToken(open, -1); | |
88 openTok.endGroup = new SymbolToken(close, -1); | |
89 return openTok; | |
90 } | |
91 | |
92 final BeginGroupToken openParen = | |
93 makeGroup(OPEN_PAREN_INFO, CLOSE_PAREN_INFO); | |
94 final BeginGroupToken openBrace = | |
95 makeGroup(OPEN_CURLY_BRACKET_INFO, CLOSE_CURLY_BRACKET_INFO); | |
96 final BeginGroupToken openBracket = | |
97 makeGroup(OPEN_SQUARE_BRACKET_INFO, CLOSE_SQUARE_BRACKET_INFO); | |
98 final BeginGroupToken lt = makeGroup(LT_INFO, GT_INFO); | |
99 | |
100 Token get closeParen => openParen.endGroup; | |
101 Token get closeBrace => openBrace.endGroup; | |
102 Token get closeBracket => openBracket.endGroup; | |
103 Token get gt => lt.endGroup; | |
104 | |
105 // Symbol tokens | |
106 final Token semicolon = new SymbolToken(SEMICOLON_INFO, -1); | |
107 final Token indexToken = new SymbolToken(INDEX_INFO, -1); // "[]" | |
108 final Token question = new SymbolToken(QUESTION_INFO, -1); | |
109 final Token colon = new SymbolToken(COLON_INFO, -1); | |
110 final Token hash = new SymbolToken(HASH_INFO, -1); | |
111 final Token bang = new SymbolToken(BANG_INFO, -1); | |
112 final Token eq = new SymbolToken(EQ_INFO, -1); | |
113 | |
114 // Keyword tokens | |
115 static Token makeIdToken(String text) { | |
116 return new StringToken.fromString(IDENTIFIER_INFO, text, -1); | |
117 } | |
118 | |
119 final Token newToken = makeIdToken('new'); | |
120 final Token constToken = makeIdToken('const'); | |
121 final Token throwToken = makeIdToken('throw'); | |
122 final Token rethrowToken = makeIdToken('rethrow'); | |
123 final Token breakToken = makeIdToken('break'); | |
124 final Token continueToken = makeIdToken('continue'); | |
125 final Token doToken = makeIdToken('do'); | |
126 final Token whileToken = makeIdToken('while'); | |
127 final Token ifToken = makeIdToken('if'); | |
128 final Token elseToken = makeIdToken('else'); | |
129 final Token awaitToken = makeIdToken('await'); | |
130 final Token forToken = makeIdToken('for'); | |
131 final Token inToken = makeIdToken('in'); | |
132 final Token returnToken = makeIdToken('return'); | |
133 final Token switchToken = makeIdToken('switch'); | |
134 final Token caseToken = makeIdToken('case'); | |
135 final Token defaultToken = makeIdToken('default'); | |
136 final Token tryToken = makeIdToken('try'); | |
137 final Token catchToken = makeIdToken('catch'); | |
138 final Token onToken = makeIdToken('on'); | |
139 final Token finallyToken = makeIdToken('finally'); | |
140 final Token getToken = makeIdToken('get'); | |
141 final Token setToken = makeIdToken('set'); | |
142 final Token classToken = makeIdToken('class'); | |
143 final Token extendsToken = makeIdToken('extends'); | |
144 final Token withToken = makeIdToken('with'); | |
145 final Token implementsToken = makeIdToken('implements'); | |
146 final Token typedefToken = makeIdToken('typedef'); | |
147 final Token enumToken = makeIdToken('enum'); | |
148 | |
149 static tree.Identifier makeIdentifier(String name) { | |
150 return new tree.Identifier( | |
151 new StringToken.fromString(IDENTIFIER_INFO, name, -1)); | |
152 } | |
153 | |
154 static tree.Operator makeOperator(String name) { | |
155 return new tree.Operator( | |
156 new StringToken.fromString(IDENTIFIER_INFO, name, -1)); | |
157 } | |
158 | |
159 // Utilities for creating NodeLists | |
160 Link<tree.Node> makeLink(Iterable<tree.Node> nodes) { | |
161 LinkBuilder builder = new LinkBuilder(); | |
162 for (tree.Node node in nodes) { | |
163 builder.addLast(node); | |
164 } | |
165 return builder.toLink(); | |
166 } | |
167 | |
168 tree.NodeList blankList() { | |
169 return new tree.NodeList(null, makeLink([]), null, ''); | |
170 } | |
171 | |
172 tree.NodeList singleton(tree.Node node) { | |
173 return new tree.NodeList(null, makeLink([node]), null, ''); | |
174 } | |
175 | |
176 tree.NodeList makeList(String delimiter, Iterable<tree.Node> nodes, | |
177 {Token open, Token close}) { | |
178 return new tree.NodeList(open, makeLink(nodes), close, delimiter); | |
179 } | |
180 | |
181 tree.NodeList parenList(String delimiter, Iterable<tree.Node> nodes) { | |
182 return makeList(delimiter, nodes, open: openParen, close: closeParen); | |
183 } | |
184 | |
185 tree.NodeList bracketList(String delimiter, Iterable<tree.Node> nodes) { | |
186 return makeList(delimiter, nodes, open: openBracket, close: closeBracket); | |
187 } | |
188 | |
189 tree.NodeList braceList(String delimiter, Iterable<tree.Node> nodes) { | |
190 return makeList(delimiter, nodes, open: openBrace, close: closeBrace); | |
191 } | |
192 | |
193 tree.NodeList argList(Iterable<tree.Node> nodes) { | |
194 return parenList(',', nodes); | |
195 } | |
196 | |
197 tree.NodeList typeArgList(Iterable<tree.Node> nodes) { | |
198 return makeList(',', nodes, open: lt, close: gt); | |
199 } | |
200 | |
201 /// Converts a qualified name into nested Sends. | |
202 tree.Node makeName(String name) { | |
203 if (name == null) { | |
204 return null; | |
205 } | |
206 List<String> names = name.split('.').toList(growable: false); | |
207 tree.Node node = makeIdentifier(names[0]); | |
208 for (int i = 1; i < names.length; i++) { | |
209 node = new tree.Send(node, makeIdentifier(names[i])); | |
210 } | |
211 return node; | |
212 } | |
213 | |
214 static Token assignmentToken(String operatorName) { | |
215 switch (operatorName) { | |
216 case '=': | |
217 return new SymbolToken(EQ_INFO, -1); | |
218 case '+=': | |
219 return new SymbolToken(PLUS_EQ_INFO, -1); | |
220 case '-=': | |
221 return new SymbolToken(MINUS_EQ_INFO, -1); | |
222 case '*=': | |
223 return new SymbolToken(STAR_EQ_INFO, -1); | |
224 case '/=': | |
225 return new SymbolToken(SLASH_EQ_INFO, -1); | |
226 case '~/=': | |
227 return new SymbolToken(TILDE_SLASH_EQ_INFO, -1); | |
228 case '%=': | |
229 return new SymbolToken(PERCENT_EQ_INFO, -1); | |
230 case '&=': | |
231 return new SymbolToken(AMPERSAND_EQ_INFO, -1); | |
232 case '^=': | |
233 return new SymbolToken(CARET_EQ_INFO, -1); | |
234 case '|=': | |
235 return new SymbolToken(BAR_EQ_INFO, -1); | |
236 case '>>=': | |
237 return new SymbolToken(GT_GT_EQ_INFO, -1); | |
238 case '<<=': | |
239 return new SymbolToken(LT_LT_EQ_INFO, -1); | |
240 default: | |
241 throw "Unrecognized assignment operator: $operatorName"; | |
242 } | |
243 } | |
244 | |
245 static Token binopToken(String operatorName) { | |
246 switch (operatorName) { | |
247 case '+': | |
248 return new SymbolToken(PLUS_INFO, -1); | |
249 case '-': | |
250 return new SymbolToken(MINUS_INFO, -1); | |
251 case '*': | |
252 return new SymbolToken(STAR_INFO, -1); | |
253 case '/': | |
254 return new SymbolToken(SLASH_INFO, -1); | |
255 case '~/': | |
256 return new SymbolToken(TILDE_SLASH_INFO, -1); | |
257 case '%': | |
258 return new SymbolToken(PERCENT_INFO, -1); | |
259 case '&': | |
260 return new SymbolToken(AMPERSAND_INFO, -1); | |
261 case '^': | |
262 return new SymbolToken(CARET_INFO, -1); | |
263 case '|': | |
264 return new SymbolToken(BAR_INFO, -1); | |
265 case '>>': | |
266 return new SymbolToken(GT_GT_INFO, -1); | |
267 case '<<': | |
268 return new SymbolToken(LT_LT_INFO, -1); | |
269 case '==': | |
270 return new SymbolToken(EQ_EQ_INFO, -1); | |
271 case '!=': | |
272 return new SymbolToken(BANG_EQ_INFO, -1); | |
273 case '>': | |
274 return new SymbolToken(GT_INFO, -1); | |
275 case '>=': | |
276 return new SymbolToken(GT_EQ_INFO, -1); | |
277 case '<': | |
278 return new SymbolToken(LT_INFO, -1); | |
279 case '<=': | |
280 return new SymbolToken(LT_EQ_INFO, -1); | |
281 case '&&': | |
282 return new SymbolToken(AMPERSAND_AMPERSAND_INFO, -1); | |
283 case '||': | |
284 return new SymbolToken(BAR_BAR_INFO, -1); | |
285 default: | |
286 throw "Unrecognized binary operator: $operatorName"; | |
287 } | |
288 } | |
289 | |
290 static Token incrementToken(String operatorName) { | |
291 switch (operatorName) { | |
292 case '++': | |
293 return new SymbolToken(PLUS_PLUS_INFO, -1); | |
294 case '--': | |
295 return new SymbolToken(MINUS_MINUS_INFO, -1); | |
296 default: | |
297 throw "Unrecognized increment operator: $operatorName"; | |
298 } | |
299 } | |
300 | |
301 static Token typeOpToken(String operatorName) { | |
302 switch (operatorName) { | |
303 // "is!" is not an operator in the frontend AST. | |
304 case 'is': | |
305 return new SymbolToken(IS_INFO, -1); | |
306 case 'as': | |
307 return new SymbolToken(AS_INFO, -1); | |
308 default: | |
309 throw 'Unrecognized type operator: $operatorName'; | |
310 } | |
311 } | |
312 | |
313 Token unopToken(String operatorName) { | |
314 switch (operatorName) { | |
315 case '-': | |
316 return new SymbolToken(MINUS_INFO, -1); | |
317 case '~': | |
318 return new SymbolToken(TILDE_INFO, -1); | |
319 case '!': | |
320 return bang; | |
321 default: | |
322 throw "Unrecognized unary operator: $operatorName"; | |
323 } | |
324 } | |
325 | |
326 tree.Node makeStaticReceiver(elements.Element element) { | |
327 if (treeElements == null) return null; | |
328 if (element.isStatic) { | |
329 elements.ClassElement enclosingClass = element.enclosingClass; | |
330 tree.Send send = new tree.Send(null, makeIdentifier(enclosingClass.name)); | |
331 treeElements[send] = enclosingClass; | |
332 return send; | |
333 } else { | |
334 return null; | |
335 } | |
336 } | |
337 | |
338 tree.Node makeArgument(Argument arg) { | |
339 if (arg is Expression) { | |
340 return makeExpression(arg); | |
341 } else if (arg is NamedArgument) { | |
342 return new tree.NamedArgument( | |
343 makeIdentifier(arg.name), colon, makeExpression(arg.expression)); | |
344 } else { | |
345 throw "Unrecognized argument type: ${arg}"; | |
346 } | |
347 } | |
348 | |
349 tree.Node makeExpression(Expression exp) { | |
350 return makeExp(exp, EXPRESSION); | |
351 } | |
352 | |
353 /// Converts [exp] to a [tree.Node] that unparses to an expression with | |
354 /// a precedence level of at least [minPrecedence]. The expression will be | |
355 /// wrapped in a parenthesis if necessary. | |
356 tree.Node makeExp(Receiver exp, int minPrecedence, {bool beginStmt: false}) { | |
357 tree.Node result; | |
358 int precedence; | |
359 bool needParen = false; | |
360 if (exp is SuperReceiver) { | |
361 precedence = CALLEE; | |
362 result = makeIdentifier('super'); | |
363 } else if (exp is Assignment) { | |
364 Expression left = exp.left; | |
365 tree.Node receiver; | |
366 tree.Node selector; | |
367 tree.NodeList arguments; | |
368 elements.Element element; | |
369 if (left is Identifier) { | |
370 receiver = makeStaticReceiver(left.element); | |
371 selector = makeIdentifier(left.name); | |
372 arguments = singleton(makeExpression(exp.right)); | |
373 element = left.element; | |
374 } else if (left is FieldExpression) { | |
375 receiver = makeExp(left.object, PRIMARY, beginStmt: beginStmt); | |
376 selector = makeIdentifier(left.fieldName); | |
377 arguments = singleton(makeExpression(exp.right)); | |
378 } else if (left is IndexExpression) { | |
379 receiver = makeExp(left.object, PRIMARY, beginStmt: beginStmt); | |
380 selector = new tree.Operator(indexToken); | |
381 arguments = bracketList( | |
382 ',', [makeExpression(left.index), makeExpression(exp.right)]); | |
383 } else { | |
384 throw "Unexpected left-hand side of assignment: ${left}"; | |
385 } | |
386 tree.Operator op = new tree.Operator(assignmentToken(exp.operator)); | |
387 result = new tree.SendSet(receiver, selector, op, arguments); | |
388 if (left is Identifier) { | |
389 setElement(result, element, exp); | |
390 } | |
391 precedence = EXPRESSION; | |
392 } else if (exp is FieldInitializer) { | |
393 precedence = EXPRESSION; | |
394 tree.Node receiver = makeIdentifier('this'); | |
395 tree.Node selector = makeIdentifier(exp.element.name); | |
396 tree.Operator op = new tree.Operator(assignmentToken("=")); | |
397 // We pass CALLEE to ensure we write eg.: | |
398 // class B { var x; B() : x = (() {return a;}) {}} | |
399 // Not the invalid: | |
400 // class B { var x; B() : x = () {return a;} {}} | |
401 result = new tree.SendSet( | |
402 receiver, selector, op, singleton(makeExp(exp.body, CALLEE))); | |
403 setElement(result, exp.element, exp); | |
404 } else if (exp is SuperInitializer) { | |
405 precedence = EXPRESSION; | |
406 tree.Node receiver = makeIdentifier('super'); | |
407 tree.NodeList arguments = | |
408 argList(exp.arguments.map(makeArgument).toList()); | |
409 if (exp.target.name == "") { | |
410 result = new tree.Send(null, receiver, arguments); | |
411 } else { | |
412 result = | |
413 new tree.Send(receiver, makeIdentifier(exp.target.name), arguments); | |
414 } | |
415 setElement(result, exp.target, exp); | |
416 } else if (exp is BinaryOperator) { | |
417 precedence = BINARY_PRECEDENCE[exp.operator]; | |
418 int deltaLeft = isAssociativeBinaryOperator(precedence) ? 0 : 1; | |
419 result = new tree.Send( | |
420 makeExp(exp.left, precedence + deltaLeft, beginStmt: beginStmt), | |
421 new tree.Operator(binopToken(exp.operator)), | |
422 singleton(makeExp(exp.right, precedence + 1))); | |
423 } else if (exp is CallFunction) { | |
424 precedence = CALLEE; | |
425 tree.Node selector; | |
426 Expression callee = exp.callee; | |
427 elements.Element element; | |
428 tree.Node receiver; | |
429 if (callee is Identifier) { | |
430 receiver = makeStaticReceiver(callee.element); | |
431 selector = makeIdentifier(callee.name); | |
432 element = callee.element; | |
433 } else { | |
434 selector = makeExp(callee, CALLEE, beginStmt: beginStmt); | |
435 } | |
436 result = new tree.Send( | |
437 receiver, selector, argList(exp.arguments.map(makeArgument))); | |
438 if (callee is Identifier) { | |
439 setElement(result, element, exp); | |
440 } | |
441 } else if (exp is CallMethod) { | |
442 precedence = CALLEE; | |
443 // TODO(sra): Elide receiver when This, but only if not in a scope that | |
444 // shadows the method (e.g. constructor body). | |
445 tree.Node receiver = makeExp(exp.object, PRIMARY, beginStmt: beginStmt); | |
446 result = new tree.Send(receiver, makeIdentifier(exp.methodName), | |
447 argList(exp.arguments.map(makeArgument))); | |
448 } else if (exp is CallNew) { | |
449 precedence = CALLEE; | |
450 tree.Node selector = makeName(exp.type.name); | |
451 if (exp.type.typeArguments.length > 0) { | |
452 selector = new tree.TypeAnnotation( | |
453 selector, typeArgList(exp.type.typeArguments.map(makeType))); | |
454 setType(selector, exp.dartType, exp); | |
455 } | |
456 if (exp.constructorName != null) { | |
457 selector = new tree.Send(selector, makeIdentifier(exp.constructorName)); | |
458 } | |
459 tree.Send send = new tree.Send( | |
460 null, selector, argList(exp.arguments.map(makeArgument))); | |
461 result = | |
462 new tree.NewExpression(exp.isConst ? constToken : newToken, send); | |
463 setType(result, exp.dartType, exp); | |
464 setElement(send, exp.constructor, exp); | |
465 } else if (exp is CallStatic) { | |
466 precedence = CALLEE; | |
467 result = new tree.Send( | |
468 makeStaticReceiver(exp.element), | |
469 makeIdentifier(exp.methodName), | |
470 argList(exp.arguments.map(makeArgument))); | |
471 setElement(result, exp.element, exp); | |
472 } else if (exp is Conditional) { | |
473 precedence = CONDITIONAL; | |
474 result = new tree.Conditional( | |
475 makeExp(exp.condition, LOGICAL_OR, beginStmt: beginStmt), | |
476 makeExp(exp.thenExpression, EXPRESSION), | |
477 makeExp(exp.elseExpression, EXPRESSION), | |
478 question, | |
479 colon); | |
480 } else if (exp is FieldExpression) { | |
481 precedence = PRIMARY; | |
482 // TODO(sra): Elide receiver when This, but only if not in a scope that | |
483 // shadows the method (e.g. constructor body). | |
484 tree.Node receiver = makeExp(exp.object, PRIMARY, beginStmt: beginStmt); | |
485 result = new tree.Send(receiver, makeIdentifier(exp.fieldName)); | |
486 } else if (exp is ConstructorDefinition) { | |
487 precedence = EXPRESSION; | |
488 tree.NodeList parameters = makeParameters(exp.parameters); | |
489 tree.NodeList initializers = | |
490 exp.initializers == null || exp.initializers.isEmpty | |
491 ? null | |
492 : makeList(",", exp.initializers.map(makeExpression).toList()); | |
493 tree.Node body = exp.isConst || exp.body == null | |
494 ? new tree.EmptyStatement(semicolon) | |
495 : makeFunctionBody(exp.body); | |
496 result = new tree.FunctionExpression( | |
497 constructorName(exp), | |
498 // GENERIC_METHODS: In order to support generic methods fully, | |
499 // we must retrieve and pass the actual type variables here. | |
500 null, // typeVariables | |
501 parameters, | |
502 body, | |
503 null, // return type | |
504 makeFunctionModifiers(exp), | |
505 initializers, | |
506 null, // get/set | |
507 null); // async modifier | |
508 setElement(result, exp.element, exp); | |
509 } else if (exp is FunctionExpression) { | |
510 precedence = PRIMARY; | |
511 if (beginStmt && exp.name != null) { | |
512 needParen = true; // Do not mistake for function declaration. | |
513 } | |
514 Token getOrSet = exp.isGetter ? getToken : exp.isSetter ? setToken : null; | |
515 tree.NodeList parameters = | |
516 exp.isGetter ? makeList("", []) : makeParameters(exp.parameters); | |
517 tree.Node body = makeFunctionBody(exp.body); | |
518 result = new tree.FunctionExpression( | |
519 functionName(exp), | |
520 // GENERIC_METHODS: In order to support generic methods fully, | |
521 // we must retrieve and pass the actual type variables here. | |
522 null, // typeVariables | |
523 parameters, | |
524 body, | |
525 exp.returnType == null || exp.element.isConstructor | |
526 ? null | |
527 : makeType(exp.returnType), | |
528 makeFunctionModifiers(exp), | |
529 null, // initializers | |
530 getOrSet, // get/set | |
531 null); // async modifier | |
532 elements.Element element = exp.element; | |
533 if (element != null) setElement(result, element, exp); | |
534 } else if (exp is Identifier) { | |
535 precedence = CALLEE; | |
536 result = new tree.Send( | |
537 makeStaticReceiver(exp.element), makeIdentifier(exp.name)); | |
538 setElement(result, exp.element, exp); | |
539 } else if (exp is Increment) { | |
540 Expression lvalue = exp.expression; | |
541 tree.Node receiver; | |
542 tree.Node selector; | |
543 tree.Node argument; | |
544 bool innerBeginStmt = beginStmt && !exp.isPrefix; | |
545 if (lvalue is Identifier) { | |
546 receiver = makeStaticReceiver(lvalue.element); | |
547 selector = makeIdentifier(lvalue.name); | |
548 } else if (lvalue is FieldExpression) { | |
549 receiver = makeExp(lvalue.object, PRIMARY, beginStmt: innerBeginStmt); | |
550 selector = makeIdentifier(lvalue.fieldName); | |
551 } else if (lvalue is IndexExpression) { | |
552 receiver = makeExp(lvalue.object, PRIMARY, beginStmt: innerBeginStmt); | |
553 selector = new tree.Operator(indexToken); | |
554 argument = makeExpression(lvalue.index); | |
555 } else { | |
556 throw "Unrecognized left-hand side: ${lvalue}"; | |
557 } | |
558 tree.Operator op = new tree.Operator(incrementToken(exp.operator)); | |
559 if (exp.isPrefix) { | |
560 precedence = UNARY; | |
561 result = new tree.SendSet.prefix(receiver, selector, op, argument); | |
562 } else { | |
563 precedence = POSTFIX_INCREMENT; | |
564 result = new tree.SendSet.postfix(receiver, selector, op, argument); | |
565 } | |
566 if (lvalue is Identifier) { | |
567 setElement(result, lvalue.element, exp); | |
568 } | |
569 } else if (exp is IndexExpression) { | |
570 precedence = CALLEE; | |
571 result = new tree.Send( | |
572 makeExp(exp.object, PRIMARY, beginStmt: beginStmt), | |
573 new tree.Operator(indexToken), | |
574 bracketList(',', [makeExpression(exp.index)])); | |
575 } else if (exp is Literal) { | |
576 precedence = CALLEE; | |
577 values.PrimitiveConstantValue value = exp.value; | |
578 Token tok = new StringToken.fromString( | |
579 STRING_INFO, '${value.primitiveValue}', -1); | |
580 if (value.isString) { | |
581 result = unparseStringLiteral(exp); | |
582 } else if (value.isInt) { | |
583 result = new tree.LiteralInt(tok, null); | |
584 } else if (value.isDouble) { | |
585 if (value.primitiveValue == double.INFINITY) { | |
586 precedence = MULTIPLICATIVE; | |
587 tok = new StringToken.fromString(STRING_INFO, '1/0.0', -1); | |
588 } else if (value.primitiveValue == double.NEGATIVE_INFINITY) { | |
589 precedence = MULTIPLICATIVE; | |
590 tok = new StringToken.fromString(STRING_INFO, '-1/0.0', -1); | |
591 } else if (value.primitiveValue.isNaN) { | |
592 precedence = MULTIPLICATIVE; | |
593 tok = new StringToken.fromString(STRING_INFO, '0/0.0', -1); | |
594 } | |
595 result = new tree.LiteralDouble(tok, null); | |
596 } else if (value.isBool) { | |
597 result = new tree.LiteralBool(tok, null); | |
598 } else if (value.isNull) { | |
599 result = new tree.LiteralNull(tok); | |
600 } else { | |
601 throw "Unrecognized constant: ${value}"; | |
602 } | |
603 } else if (exp is LiteralList) { | |
604 precedence = PRIMARY; | |
605 tree.NodeList typeArgs = null; | |
606 if (exp.typeArgument != null) { | |
607 typeArgs = typeArgList([makeType(exp.typeArgument)]); | |
608 } | |
609 result = new tree.LiteralList( | |
610 typeArgs, | |
611 bracketList(',', exp.values.map(makeExpression)), | |
612 exp.isConst ? constToken : null); | |
613 } else if (exp is LiteralMap) { | |
614 precedence = PRIMARY; | |
615 if (beginStmt) { | |
616 // The opening brace can be confused with a block statement. | |
617 needParen = true; | |
618 } | |
619 tree.NodeList typeArgs = null; | |
620 if (exp.typeArguments != null && exp.typeArguments.length > 0) { | |
621 typeArgs = typeArgList(exp.typeArguments.map(makeType)); | |
622 } | |
623 result = new tree.LiteralMap( | |
624 typeArgs, | |
625 braceList(',', exp.entries.map(makeLiteralMapEntry)), | |
626 exp.isConst ? constToken : null); | |
627 } else if (exp is LiteralSymbol) { | |
628 precedence = PRIMARY; | |
629 result = new tree.LiteralSymbol( | |
630 hash, makeList('.', exp.id.split('.').map(makeIdentifier))); | |
631 } else if (exp is LiteralType) { | |
632 precedence = TYPE_LITERAL; | |
633 elements.Element optionalElement = exp.type.element; | |
634 result = new tree.Send( | |
635 optionalElement == null ? null : makeStaticReceiver(optionalElement), | |
636 makeIdentifier(exp.name)); | |
637 treeElements.setType(result, exp.type); | |
638 if (optionalElement != null) { | |
639 // dynamic does not have an element | |
640 setElement(result, optionalElement, exp); | |
641 } | |
642 } else if (exp is ReifyTypeVar) { | |
643 precedence = PRIMARY; | |
644 result = new tree.Send(null, makeIdentifier(exp.name)); | |
645 setElement(result, exp.element, exp); | |
646 setType(result, exp.element.type, exp); | |
647 } else if (exp is StringConcat) { | |
648 precedence = PRIMARY; | |
649 result = unparseStringLiteral(exp); | |
650 } else if (exp is This) { | |
651 precedence = CALLEE; | |
652 result = makeIdentifier('this'); | |
653 } else if (exp is Throw) { | |
654 precedence = EXPRESSION; // ??? | |
655 result = new tree.Throw(makeExpression(exp.expression), throwToken, | |
656 throwToken); // endToken not used by unparser | |
657 } else if (exp is TypeOperator) { | |
658 precedence = RELATIONAL; | |
659 tree.Operator operator; | |
660 tree.Node rightOperand = makeType(exp.type); | |
661 if (exp.operator == 'is!') { | |
662 operator = new tree.Operator(typeOpToken('is')); | |
663 rightOperand = | |
664 new tree.Send(rightOperand, new tree.Operator(bang), blankList()); | |
665 } else { | |
666 operator = new tree.Operator(typeOpToken(exp.operator)); | |
667 } | |
668 result = new tree.Send( | |
669 makeExp(exp.expression, BITWISE_OR, beginStmt: beginStmt), | |
670 operator, | |
671 singleton(rightOperand)); | |
672 } else if (exp is UnaryOperator) { | |
673 precedence = UNARY; | |
674 result = new tree.Send.prefix(makeExp(exp.operand, UNARY), | |
675 new tree.Operator(unopToken(exp.operatorName))); | |
676 } else { | |
677 throw "Unknown expression type: ${exp}"; | |
678 } | |
679 | |
680 needParen = needParen || precedence < minPrecedence; | |
681 if (needParen) { | |
682 result = parenthesize(result); | |
683 } | |
684 return result; | |
685 } | |
686 | |
687 /// Creates a LiteralString with [verbatim] as the value. | |
688 /// No (un)quoting or (un)escaping will be performed by this method. | |
689 /// The [DartString] inside the literal will be set to null because the | |
690 /// code emitter does not use it. | |
691 tree.LiteralString makeVerbatimStringLiteral(String verbatim) { | |
692 Token tok = new StringToken.fromString(STRING_INFO, verbatim, -1); | |
693 return new tree.LiteralString(tok, null); | |
694 } | |
695 | |
696 tree.LiteralMapEntry makeLiteralMapEntry(LiteralMapEntry en) { | |
697 return new tree.LiteralMapEntry( | |
698 makeExpression(en.key), colon, makeExpression(en.value)); | |
699 } | |
700 | |
701 /// A comment token to be inserted when [INSERT_NEW_BACKEND_COMMENT] is true. | |
702 final SymbolToken newBackendComment = new SymbolToken( | |
703 const PrecedenceInfo('/* new backend */ ', 0, OPEN_CURLY_BRACKET_TOKEN), | |
704 -1); | |
705 | |
706 tree.Node makeFunctionBody(Statement stmt) { | |
707 if (INSERT_NEW_BACKEND_COMMENT) { | |
708 return new tree.Block( | |
709 makeList('', [makeBlock(stmt)], open: newBackendComment)); | |
710 } else { | |
711 return makeBlock(stmt); | |
712 } | |
713 } | |
714 | |
715 /// Produces a statement in a context where only blocks are allowed. | |
716 tree.Node makeBlock(Statement stmt) { | |
717 if (stmt is Block || stmt is EmptyStatement) { | |
718 return makeStatement(stmt); | |
719 } else { | |
720 return new tree.Block(braceList('', [makeStatement(stmt)])); | |
721 } | |
722 } | |
723 | |
724 /// Adds the given statement to a block. If the statement itself is a block | |
725 /// it will be flattened (if this does not change lexical scoping). | |
726 void addBlockMember(Statement stmt, List<tree.Node> accumulator) { | |
727 if (stmt is Block && !stmt.statements.any(Unparser.definesVariable)) { | |
728 for (Statement innerStmt in stmt.statements) { | |
729 addBlockMember(innerStmt, accumulator); | |
730 } | |
731 } else if (stmt is EmptyStatement) { | |
732 // No need to include empty statements inside blocks | |
733 } else { | |
734 accumulator.add(makeStatement(stmt)); | |
735 } | |
736 } | |
737 | |
738 /// True if [stmt] is equivalent to an empty statement. | |
739 bool isEmptyStatement(Statement stmt) { | |
740 return stmt is EmptyStatement || | |
741 (stmt is Block && stmt.statements.every(isEmptyStatement)); | |
742 } | |
743 | |
744 tree.Node makeStatement(Statement stmt, {bool shortIf: true}) { | |
745 if (stmt is Block) { | |
746 List<tree.Node> body = <tree.Node>[]; | |
747 for (Statement innerStmt in stmt.statements) { | |
748 addBlockMember(innerStmt, body); | |
749 } | |
750 return new tree.Block(braceList('', body)); | |
751 } else if (stmt is Break) { | |
752 return new tree.BreakStatement( | |
753 stmt.label == null ? null : makeIdentifier(stmt.label), | |
754 breakToken, | |
755 semicolon); | |
756 } else if (stmt is Continue) { | |
757 return new tree.ContinueStatement( | |
758 stmt.label == null ? null : makeIdentifier(stmt.label), | |
759 continueToken, | |
760 semicolon); | |
761 } else if (stmt is DoWhile) { | |
762 return new tree.DoWhile( | |
763 makeStatement(stmt.body, shortIf: shortIf), | |
764 parenthesize(makeExpression(stmt.condition)), | |
765 doToken, | |
766 whileToken, | |
767 semicolon); | |
768 } else if (stmt is EmptyStatement) { | |
769 return new tree.EmptyStatement(semicolon); | |
770 } else if (stmt is ExpressionStatement) { | |
771 return new tree.ExpressionStatement( | |
772 makeExp(stmt.expression, EXPRESSION, beginStmt: true), semicolon); | |
773 } else if (stmt is For) { | |
774 tree.Node initializer; | |
775 if (stmt.initializer is VariableDeclarations) { | |
776 initializer = makeVariableDeclarations(stmt.initializer); | |
777 } else if (stmt.initializer is Expression) { | |
778 initializer = makeExpression(stmt.initializer); | |
779 } else { | |
780 initializer = null; | |
781 } | |
782 tree.Node condition; | |
783 if (stmt.condition != null) { | |
784 condition = new tree.ExpressionStatement( | |
785 makeExpression(stmt.condition), semicolon); | |
786 } else { | |
787 condition = new tree.EmptyStatement(semicolon); | |
788 } | |
789 return new tree.For( | |
790 initializer, | |
791 condition, | |
792 makeList(',', stmt.updates.map(makeExpression)), | |
793 makeStatement(stmt.body, shortIf: shortIf), | |
794 forToken); | |
795 } else if (stmt is ForIn) { | |
796 tree.Node left; | |
797 if (stmt.leftHandValue is Identifier) { | |
798 left = makeExpression(stmt.leftHandValue); | |
799 } else { | |
800 left = makeVariableDeclarations(stmt.leftHandValue); | |
801 } | |
802 return new tree.SyncForIn(left, makeExpression(stmt.expression), | |
803 makeStatement(stmt.body, shortIf: shortIf), forToken, inToken); | |
804 } else if (stmt is FunctionDeclaration) { | |
805 tree.FunctionExpression function = new tree.FunctionExpression( | |
806 stmt.name != null ? makeIdentifier(stmt.name) : null, | |
807 // GENERIC_METHODS: In order to support generic methods fully, | |
808 // we must retrieve and pass the actual type variables here. | |
809 null, // typeVariables | |
810 makeParameters(stmt.parameters), | |
811 makeFunctionBody(stmt.body), | |
812 stmt.returnType != null ? makeType(stmt.returnType) : null, | |
813 makeEmptyModifiers(), | |
814 null, // initializers | |
815 null, // get/set | |
816 null); // async modifier | |
817 setElement(function, stmt.function.element, stmt); | |
818 return new tree.FunctionDeclaration(function); | |
819 } else if (stmt is If) { | |
820 if (stmt.elseStatement == null || isEmptyStatement(stmt.elseStatement)) { | |
821 tree.Node node = new tree.If( | |
822 parenthesize(makeExpression(stmt.condition)), | |
823 makeStatement(stmt.thenStatement), | |
824 null, // else statement | |
825 ifToken, | |
826 null); // else token | |
827 if (shortIf) | |
828 return node; | |
829 else | |
830 return new tree.Block(braceList('', [node])); | |
831 } else { | |
832 return new tree.If( | |
833 parenthesize(makeExpression(stmt.condition)), | |
834 makeStatement(stmt.thenStatement, shortIf: false), | |
835 makeStatement(stmt.elseStatement, shortIf: shortIf), | |
836 ifToken, | |
837 elseToken); // else token | |
838 } | |
839 } else if (stmt is LabeledStatement) { | |
840 List<tree.Label> labels = []; | |
841 Statement inner = stmt; | |
842 while (inner is LabeledStatement) { | |
843 LabeledStatement lbl = inner as LabeledStatement; | |
844 labels.add(new tree.Label(makeIdentifier(lbl.label), colon)); | |
845 inner = lbl.statement; | |
846 } | |
847 return new tree.LabeledStatement( | |
848 makeList('', labels), makeStatement(inner, shortIf: shortIf)); | |
849 } else if (stmt is Rethrow) { | |
850 return new tree.Rethrow(rethrowToken, semicolon); | |
851 } else if (stmt is Return) { | |
852 return new tree.Return(returnToken, semicolon, | |
853 stmt.expression == null ? null : makeExpression(stmt.expression)); | |
854 } else if (stmt is Switch) { | |
855 return new tree.SwitchStatement( | |
856 parenthesize(makeExpression(stmt.expression)), | |
857 braceList('', stmt.cases.map(makeSwitchCase)), | |
858 switchToken); | |
859 } else if (stmt is Try) { | |
860 return new tree.TryStatement( | |
861 makeBlock(stmt.tryBlock), | |
862 makeList(null, stmt.catchBlocks.map(makeCatchBlock)), | |
863 stmt.finallyBlock == null ? null : makeBlock(stmt.finallyBlock), | |
864 tryToken, | |
865 stmt.finallyBlock == null ? null : finallyToken); | |
866 } else if (stmt is VariableDeclarations) { | |
867 return makeVariableDeclarations(stmt, useVar: true, endToken: semicolon); | |
868 } else if (stmt is While) { | |
869 return new tree.While(parenthesize(makeExpression(stmt.condition)), | |
870 makeStatement(stmt.body, shortIf: shortIf), whileToken); | |
871 } else { | |
872 throw "Unrecognized statement: ${stmt}"; | |
873 } | |
874 } | |
875 | |
876 tree.Node makeVariableDeclaration(VariableDeclaration vd) { | |
877 tree.Node id = makeIdentifier(vd.name); | |
878 setElement(id, vd.element, vd); | |
879 if (vd.initializer == null) { | |
880 return id; | |
881 } | |
882 tree.Node send = new tree.SendSet(null, id, new tree.Operator(eq), | |
883 singleton(makeExpression(vd.initializer))); | |
884 setElement(send, vd.element, vd); | |
885 return send; | |
886 } | |
887 | |
888 /// If [useVar] is true, the variable definition will use `var` as modifier | |
889 /// if no other modifiers are present. | |
890 /// [endToken] will be used to terminate the declaration list. | |
891 tree.Node makeVariableDeclarations(VariableDeclarations decl, | |
892 {bool useVar: false, Token endToken: null}) { | |
893 return new tree.VariableDefinitions( | |
894 decl.type == null ? null : makeType(decl.type), | |
895 makeVarModifiers( | |
896 isConst: decl.isConst, | |
897 isFinal: decl.isFinal, | |
898 useVar: useVar && decl.type == null), | |
899 makeList(',', decl.declarations.map(makeVariableDeclaration), | |
900 close: endToken)); | |
901 } | |
902 | |
903 tree.CatchBlock makeCatchBlock(CatchBlock block) { | |
904 List<tree.VariableDefinitions> formals = []; | |
905 if (block.exceptionVar != null) { | |
906 tree.Node exceptionName = makeIdentifier(block.exceptionVar.name); | |
907 setElement(exceptionName, block.exceptionVar.element, block.exceptionVar); | |
908 formals.add(new tree.VariableDefinitions( | |
909 null, makeEmptyModifiers(), singleton(exceptionName))); | |
910 } | |
911 if (block.stackVar != null) { | |
912 tree.Node stackTraceName = makeIdentifier(block.stackVar.name); | |
913 setElement(stackTraceName, block.stackVar.element, block.stackVar); | |
914 formals.add(new tree.VariableDefinitions( | |
915 null, makeEmptyModifiers(), singleton(stackTraceName))); | |
916 } | |
917 return new tree.CatchBlock( | |
918 block.onType == null ? null : makeType(block.onType), | |
919 block.exceptionVar == null ? null : argList(formals), | |
920 makeBlock(block.body), | |
921 block.onType == null ? null : onToken, | |
922 block.exceptionVar == null ? null : catchToken); | |
923 } | |
924 | |
925 tree.SwitchCase makeSwitchCase(SwitchCase caze) { | |
926 if (caze.isDefaultCase) { | |
927 return new tree.SwitchCase( | |
928 blankList(), | |
929 defaultToken, | |
930 makeList('', caze.statements.map(makeStatement)), | |
931 null); // startToken unused by unparser | |
932 } else { | |
933 return new tree.SwitchCase( | |
934 makeList('', caze.expressions.map(makeCaseMatch)), | |
935 null, // defaultKeyword, | |
936 makeList('', caze.statements.map(makeStatement)), | |
937 null); // startToken unused by unparser | |
938 } | |
939 } | |
940 | |
941 tree.CaseMatch makeCaseMatch(Expression exp) { | |
942 return new tree.CaseMatch(caseToken, makeExpression(exp), colon); | |
943 } | |
944 | |
945 tree.TypeAnnotation makeType(TypeAnnotation type) { | |
946 tree.NodeList typeArgs; | |
947 if (type.typeArguments.length > 0) { | |
948 typeArgs = typeArgList(type.typeArguments.map(makeType)); | |
949 } else { | |
950 typeArgs = null; | |
951 } | |
952 tree.TypeAnnotation result = | |
953 new tree.TypeAnnotation(makeIdentifier(type.name), typeArgs); | |
954 setType(result, type.dartType, type); | |
955 return result; | |
956 } | |
957 | |
958 tree.NodeList makeParameters(Parameters params) { | |
959 List<tree.Node> nodes = | |
960 params.requiredParameters.map(makeParameter).toList(); | |
961 if (params.hasOptionalParameters) { | |
962 Token assign = params.hasNamedParameters ? colon : eq; | |
963 Token open = params.hasNamedParameters ? openBrace : openBracket; | |
964 Token close = params.hasNamedParameters ? closeBrace : closeBracket; | |
965 Iterable<tree.Node> opt = | |
966 params.optionalParameters.map((p) => makeParameter(p, assign)); | |
967 nodes.add(new tree.NodeList(open, makeLink(opt), close, ',')); | |
968 } | |
969 return argList(nodes); | |
970 } | |
971 | |
972 /// [assignOperator] is used for writing the default value. | |
973 tree.Node makeParameter(Parameter param, [Token assignOperator]) { | |
974 if (param.isFunction) { | |
975 tree.Node definition = new tree.FunctionExpression( | |
976 makeIdentifier(param.name), | |
977 // GENERIC_METHODS: In order to support generic methods fully, | |
978 // we must retrieve and pass the actual type variables here. | |
979 null, // typeVariables | |
980 makeParameters(param.parameters), | |
981 null, // body | |
982 param.type == null ? null : makeType(param.type), | |
983 makeEmptyModifiers(), // TODO: Function parameter modifiers? | |
984 null, // initializers | |
985 null, // get/set | |
986 null); // async modifier | |
987 if (param.element != null) { | |
988 setElement(definition, param.element, param); | |
989 } | |
990 if (param.defaultValue != null) { | |
991 definition = new tree.SendSet( | |
992 null, | |
993 definition, | |
994 new tree.Operator(assignOperator), | |
995 singleton(makeExpression(param.defaultValue))); | |
996 } | |
997 return new tree.VariableDefinitions( | |
998 null, makeEmptyModifiers(), singleton(definition)); | |
999 } else { | |
1000 tree.Node definition; | |
1001 if (param.defaultValue != null) { | |
1002 definition = new tree.SendSet( | |
1003 null, | |
1004 makeIdentifier(param.name), | |
1005 new tree.Operator(assignOperator), | |
1006 singleton(makeExpression(param.defaultValue))); | |
1007 } else { | |
1008 definition = makeIdentifier(param.name); | |
1009 } | |
1010 if (param.element != null) { | |
1011 setElement(definition, param.element, param); | |
1012 } | |
1013 return new tree.VariableDefinitions( | |
1014 param.type == null ? null : makeType(param.type), | |
1015 makeEmptyModifiers(), // TODO: Parameter modifiers? | |
1016 singleton(definition)); | |
1017 } | |
1018 } | |
1019 | |
1020 tree.Modifiers makeEmptyModifiers() { | |
1021 return new tree.Modifiers(blankList()); | |
1022 } | |
1023 | |
1024 tree.Modifiers makeModifiers( | |
1025 {bool isExternal: false, | |
1026 bool isStatic: false, | |
1027 bool isAbstract: false, | |
1028 bool isFactory: false, | |
1029 bool isConst: false, | |
1030 bool isFinal: false, | |
1031 bool isVar: false}) { | |
1032 List<tree.Node> nodes = []; | |
1033 if (isExternal) { | |
1034 nodes.add(makeIdentifier('external')); | |
1035 } | |
1036 if (isStatic) { | |
1037 nodes.add(makeIdentifier('static')); | |
1038 } | |
1039 if (isAbstract) { | |
1040 nodes.add(makeIdentifier('abstract')); | |
1041 } | |
1042 if (isFactory) { | |
1043 nodes.add(makeIdentifier('factory')); | |
1044 } | |
1045 if (isConst) { | |
1046 nodes.add(makeIdentifier('const')); | |
1047 } | |
1048 if (isFinal) { | |
1049 nodes.add(makeIdentifier('final')); | |
1050 } | |
1051 if (isVar) { | |
1052 nodes.add(makeIdentifier('var')); | |
1053 } | |
1054 return new tree.Modifiers(makeList(' ', nodes)); | |
1055 } | |
1056 | |
1057 tree.Modifiers makeVarModifiers( | |
1058 {bool isConst: false, | |
1059 bool isFinal: false, | |
1060 bool useVar: false, | |
1061 bool isStatic: false}) { | |
1062 return makeModifiers( | |
1063 isStatic: isStatic, | |
1064 isConst: isConst, | |
1065 isFinal: isFinal, | |
1066 isVar: useVar && !(isConst || isFinal)); | |
1067 } | |
1068 | |
1069 tree.Modifiers makeFunctionModifiers(FunctionExpression exp) { | |
1070 if (exp.element == null) return makeEmptyModifiers(); | |
1071 return makeModifiers( | |
1072 isExternal: exp.element.isExternal, | |
1073 isStatic: exp.element.isStatic, | |
1074 isFactory: exp.element.isFactoryConstructor, | |
1075 isConst: exp.element.isConst); | |
1076 } | |
1077 | |
1078 tree.Node makeNodeForClassElement(elements.ClassElement cls) { | |
1079 if (cls.isMixinApplication) { | |
1080 return makeNamedMixinApplication(cls); | |
1081 } else if (cls.isEnumClass) { | |
1082 return makeEnum(cls); | |
1083 } else { | |
1084 return makeClassNode(cls); | |
1085 } | |
1086 } | |
1087 | |
1088 tree.Typedef makeTypedef(elements.TypedefElement typdef) { | |
1089 types.FunctionType functionType = typdef.alias; | |
1090 final tree.TypeAnnotation returnType = | |
1091 makeType(TypeGenerator.createType(functionType.returnType)); | |
1092 | |
1093 final tree.Identifier name = makeIdentifier(typdef.name); | |
1094 final tree.NodeList typeParameters = | |
1095 makeTypeParameters(typdef.typeVariables); | |
1096 final tree.NodeList formals = | |
1097 makeParameters(TypeGenerator.createParametersFromType(functionType)); | |
1098 | |
1099 final Token typedefKeyword = typedefToken; | |
1100 final Token endToken = semicolon; | |
1101 | |
1102 return new tree.Typedef( | |
1103 returnType, name, typeParameters, formals, typedefKeyword, endToken); | |
1104 } | |
1105 | |
1106 /// Create a [tree.NodeList] containing the type variable declarations in | |
1107 /// [typeVaraiables. | |
1108 tree.NodeList makeTypeParameters(List<types.DartType> typeVariables) { | |
1109 if (typeVariables.isEmpty) { | |
1110 return new tree.NodeList.empty(); | |
1111 } else { | |
1112 List<tree.Node> typeVariableList = <tree.Node>[]; | |
1113 for (types.TypeVariableType typeVariable in typeVariables) { | |
1114 tree.Node id = makeIdentifier(typeVariable.name); | |
1115 treeElements[id] = typeVariable.element; | |
1116 tree.Node bound; | |
1117 if (!typeVariable.element.bound.isObject) { | |
1118 bound = | |
1119 makeType(TypeGenerator.createType(typeVariable.element.bound)); | |
1120 } | |
1121 tree.TypeVariable node = new tree.TypeVariable(id, extendsToken, bound); | |
1122 treeElements.setType(node, typeVariable); | |
1123 typeVariableList.add(node); | |
1124 } | |
1125 return makeList(',', typeVariableList, open: lt, close: gt); | |
1126 } | |
1127 } | |
1128 | |
1129 /// Create a [tree.NodeList] containing the declared interfaces. | |
1130 /// | |
1131 /// [interfaces] is from [elements.ClassElement] in reverse declaration order | |
1132 /// and it contains mixins. To produce a list of the declared interfaces only, | |
1133 /// interfaces in [mixinTypes] are omitted. | |
1134 /// | |
1135 /// [forNamedMixinApplication] is because the structure of the [tree.NodeList] | |
1136 /// differs between [tree.NamedMixinApplication] and [tree.ClassNode]. | |
1137 // TODO(johnniwinther): Normalize interfaces on[tree.NamedMixinApplication] | |
1138 // and [tree.ClassNode]. | |
1139 tree.NodeList makeInterfaces( | |
1140 Link<types.DartType> interfaces, Set<types.DartType> mixinTypes, | |
1141 {bool forNamedMixinApplication: false}) { | |
1142 Link<tree.Node> typeAnnotations = const Link<tree.Node>(); | |
1143 for (Link<types.DartType> link = interfaces; | |
1144 !link.isEmpty; | |
1145 link = link.tail) { | |
1146 types.DartType interface = link.head; | |
1147 if (!mixinTypes.contains(interface)) { | |
1148 typeAnnotations = typeAnnotations | |
1149 .prepend(makeType(TypeGenerator.createType(interface))); | |
1150 } | |
1151 } | |
1152 if (typeAnnotations.isEmpty) { | |
1153 return forNamedMixinApplication ? null : new tree.NodeList.empty(); | |
1154 } else { | |
1155 return new tree.NodeList( | |
1156 forNamedMixinApplication ? null : implementsToken, | |
1157 typeAnnotations, | |
1158 null, | |
1159 ','); | |
1160 } | |
1161 } | |
1162 | |
1163 /// Creates a [tree.NamedMixinApplication] node for [cls]. | |
1164 // TODO(johnniwinther): Unify creation of mixin lists between | |
1165 // [NamedMixinApplicationElement] and [ClassElement]. | |
1166 tree.NamedMixinApplication makeNamedMixinApplication( | |
1167 elements.MixinApplicationElement cls) { | |
1168 assert(invariant(cls, !cls.isUnnamedMixinApplication, | |
1169 message: "Cannot create ClassNode for unnamed mixin application " | |
1170 "$cls.")); | |
1171 tree.Modifiers modifiers = makeModifiers(isAbstract: cls.isAbstract); | |
1172 tree.Identifier name = makeIdentifier(cls.name); | |
1173 tree.NodeList typeParameters = makeTypeParameters(cls.typeVariables); | |
1174 | |
1175 Set<types.DartType> mixinTypes = new Set<types.DartType>(); | |
1176 Link<tree.Node> mixins = const Link<tree.Node>(); | |
1177 | |
1178 void addMixin(types.DartType mixinType) { | |
1179 mixinTypes.add(mixinType); | |
1180 mixins = mixins.prepend(makeType(TypeGenerator.createType(mixinType))); | |
1181 } | |
1182 | |
1183 addMixin(cls.mixinType); | |
1184 | |
1185 tree.Node superclass; | |
1186 types.InterfaceType supertype = cls.supertype; | |
1187 while (supertype.element.isUnnamedMixinApplication) { | |
1188 elements.MixinApplicationElement mixinApplication = supertype.element; | |
1189 addMixin(cls.asInstanceOf(mixinApplication.mixin)); | |
1190 supertype = mixinApplication.supertype; | |
1191 } | |
1192 superclass = | |
1193 makeType(TypeGenerator.createType(cls.asInstanceOf(supertype.element))); | |
1194 tree.Node supernode = new tree.MixinApplication( | |
1195 superclass, new tree.NodeList(null, mixins, null, ',')); | |
1196 | |
1197 tree.NodeList interfaces = makeInterfaces(cls.interfaces, mixinTypes, | |
1198 forNamedMixinApplication: true); | |
1199 | |
1200 return new tree.NamedMixinApplication(name, typeParameters, modifiers, | |
1201 supernode, interfaces, classToken, semicolon); | |
1202 } | |
1203 | |
1204 tree.Enum makeEnum(elements.EnumClassElement cls) { | |
1205 return new tree.Enum( | |
1206 enumToken, | |
1207 makeIdentifier(cls.name), | |
1208 makeList(',', cls.enumValues.map((e) => makeIdentifier(e.name)), | |
1209 open: openBrace, close: closeBrace)); | |
1210 } | |
1211 | |
1212 /// Creates a [tree.ClassNode] node for [cls]. | |
1213 tree.ClassNode makeClassNode(elements.ClassElement cls) { | |
1214 assert(invariant(cls, !cls.isUnnamedMixinApplication, | |
1215 message: "Cannot create ClassNode for unnamed mixin application " | |
1216 "$cls.")); | |
1217 tree.Modifiers modifiers = makeModifiers(isAbstract: cls.isAbstract); | |
1218 tree.Identifier name = makeIdentifier(cls.name); | |
1219 tree.NodeList typeParameters = makeTypeParameters(cls.typeVariables); | |
1220 tree.Node supernode; | |
1221 types.InterfaceType supertype = cls.supertype; | |
1222 Set<types.DartType> mixinTypes = new Set<types.DartType>(); | |
1223 Link<tree.Node> mixins = const Link<tree.Node>(); | |
1224 | |
1225 void addMixin(types.DartType mixinType) { | |
1226 mixinTypes.add(mixinType); | |
1227 mixins = mixins.prepend(makeType(TypeGenerator.createType(mixinType))); | |
1228 } | |
1229 | |
1230 if (supertype != null) { | |
1231 if (supertype.element.isUnnamedMixinApplication) { | |
1232 while (supertype.element.isUnnamedMixinApplication) { | |
1233 elements.MixinApplicationElement mixinApplication = supertype.element; | |
1234 addMixin(cls.asInstanceOf(mixinApplication.mixin)); | |
1235 supertype = mixinApplication.supertype; | |
1236 } | |
1237 tree.Node superclass = makeType( | |
1238 TypeGenerator.createType(cls.asInstanceOf(supertype.element))); | |
1239 supernode = new tree.MixinApplication( | |
1240 superclass, new tree.NodeList(null, mixins, null, ',')); | |
1241 } else if (!supertype.isObject) { | |
1242 supernode = makeType(TypeGenerator.createType(supertype)); | |
1243 } | |
1244 } | |
1245 tree.NodeList interfaces = makeInterfaces(cls.interfaces, mixinTypes); | |
1246 | |
1247 Token extendsKeyword = supernode != null ? extendsToken : null; | |
1248 return new tree.ClassNode( | |
1249 modifiers, | |
1250 name, | |
1251 typeParameters, | |
1252 supernode, | |
1253 interfaces, | |
1254 openBrace, | |
1255 extendsKeyword, | |
1256 null, // No body. | |
1257 closeBrace); | |
1258 } | |
1259 | |
1260 tree.Node constructorName(ConstructorDefinition exp) { | |
1261 String name = exp.name; | |
1262 tree.Identifier className = makeIdentifier(exp.element.enclosingClass.name); | |
1263 tree.Node result = | |
1264 name == "" ? className : new tree.Send(className, makeIdentifier(name)); | |
1265 setElement(result, exp.element, exp); | |
1266 return result; | |
1267 } | |
1268 | |
1269 tree.Node functionName(FunctionExpression exp) { | |
1270 String name = exp.name; | |
1271 if (name == null) return null; | |
1272 if (isUserDefinableOperator(name)) { | |
1273 return makeOperator("operator$name"); | |
1274 } else if (name == "unary-") { | |
1275 return makeOperator("operator-"); | |
1276 } | |
1277 return makeIdentifier(name); | |
1278 } | |
1279 | |
1280 tree.Node parenthesize(tree.Node node) { | |
1281 return new tree.ParenthesizedExpression(node, openParen); | |
1282 } | |
1283 | |
1284 tree.Node unparseStringLiteral(Expression exp) { | |
1285 StringLiteralOutput output = Unparser.analyzeStringLiteral(exp); | |
1286 List parts = output.parts; | |
1287 tree.Node printStringChunk(StringChunk chunk) { | |
1288 bool raw = chunk.quoting.raw; | |
1289 int quoteCode = chunk.quoting.quote; | |
1290 | |
1291 List<tree.StringInterpolationPart> literalParts = []; | |
1292 tree.LiteralString firstLiteral; | |
1293 tree.Node currentInterpolation; | |
1294 | |
1295 // sb contains the current unfinished LiteralString | |
1296 StringBuffer sb = new StringBuffer(); | |
1297 if (raw) { | |
1298 sb.write('r'); | |
1299 } | |
1300 for (int i = 0; i < chunk.quoting.leftQuoteCharCount; i++) { | |
1301 sb.write(chunk.quoting.quoteChar); | |
1302 } | |
1303 | |
1304 // Print every character and string interpolation | |
1305 int startIndex = chunk.previous != null ? chunk.previous.endIndex : 0; | |
1306 for (int i = startIndex; i < chunk.endIndex; i++) { | |
1307 var part = parts[i]; | |
1308 if (part is Expression) { | |
1309 // Finish the previous string interpolation, if there is one. | |
1310 tree.LiteralString lit = makeVerbatimStringLiteral(sb.toString()); | |
1311 if (currentInterpolation != null) { | |
1312 literalParts.add( | |
1313 new tree.StringInterpolationPart(currentInterpolation, lit)); | |
1314 } else { | |
1315 firstLiteral = lit; | |
1316 } | |
1317 sb.clear(); | |
1318 currentInterpolation = makeExpression(part); | |
1319 } else { | |
1320 int char = part; | |
1321 sb.write(Unparser.getEscapedCharacter(char, quoteCode, raw)); | |
1322 } | |
1323 } | |
1324 | |
1325 // Print ending quotes | |
1326 for (int i = 0; i < chunk.quoting.rightQuoteLength; i++) { | |
1327 sb.write(chunk.quoting.quoteChar); | |
1328 } | |
1329 | |
1330 // Finish the previous string interpolation, if there is one. | |
1331 // Then wrap everything in a StringInterpolation, if relevant. | |
1332 tree.LiteralString lit = makeVerbatimStringLiteral(sb.toString()); | |
1333 tree.Node node; | |
1334 if (firstLiteral == null) { | |
1335 node = lit; | |
1336 } else { | |
1337 literalParts | |
1338 .add(new tree.StringInterpolationPart(currentInterpolation, lit)); | |
1339 node = new tree.StringInterpolation( | |
1340 firstLiteral, makeList('', literalParts)); | |
1341 } | |
1342 | |
1343 // Juxtapose with the previous string chunks, if any. | |
1344 if (chunk.previous != null) { | |
1345 return new tree.StringJuxtaposition( | |
1346 printStringChunk(chunk.previous), node); | |
1347 } else { | |
1348 return node; | |
1349 } | |
1350 } | |
1351 return printStringChunk(output.chunk); | |
1352 } | |
1353 } | |
OLD | NEW |