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