Linter Demo Errors: 2Warnings: 104File: /home/fstrocco/Dart/dart/benchmark/compiler/lib/src/dart_backend/backend_ast_to_frontend_ast.dart // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. library dart_tree_printer; import '../constants/values.dart' as values; import '../dart_types.dart' as types; import '../dart2jslib.dart' as dart2js; import '../elements/elements.dart' as elements; import '../tree/tree.dart' as tree; import '../scanner/scannerlib.dart'; import '../util/util.dart'; import 'backend_ast_nodes.dart'; import 'backend_ast_emitter.dart' show TypeGenerator; /// Translates the backend AST to Dart frontend AST. tree.Node emit(dart2js.TreeElementMapping treeElements, RootNode root) { return new TreePrinter(treeElements).makeDefinition(root); } /// If true, the unparser will insert a coment in front of every function /// it emits. This helps indicate which functions were translated by the new /// backend. bool INSERT_NEW_BACKEND_COMMENT = const bool.fromEnvironment('USE_NEW_BACKEND', defaultValue: false); /// Converts backend ASTs to frontend ASTs. class TreePrinter { dart2js.TreeElementMapping treeElements; TreePrinter([this.treeElements]); tree.Node makeDefinition(RootNode node) { if (node is FieldDefinition) { tree.Node definition; if (node.initializer == null) { definition = makeIdentifier(node.element.name); } else { definition = new tree.SendSet( null, makeIdentifier(node.element.name), new tree.Operator(assignmentToken("=")), singleton(makeExpression(node.initializer))); } setElement(definition, node.element, node); return new tree.VariableDefinitions( null, // TODO(sigurdm): Type makeVarModifiers(useVar: true, isFinal: node.element.isFinal, isStatic: node.element.isStatic, isConst: node.element.isConst), makeList(null, [definition], close: semicolon)); } else if (node is FunctionExpression) { return makeExpression(node); } else { assert(false); return null; } } void setElement(tree.Node node, elements.Element element, source) { if (treeElements != null) { if (element == null) { throw "Missing element from ${source}"; } treeElements[node] = element; } } void setType(tree.Node node, types.DartType type, source) { if (treeElements != null) { if (type == null) { throw "Missing type from ${source}"; } treeElements.setType(node, type); } } // Group tokens: () [] {} <> static BeginGroupToken makeGroup(PrecedenceInfo open, PrecedenceInfo close) { BeginGroupToken openTok = new BeginGroupToken(open, -1); openTok.endGroup = new SymbolToken(close, -1); return openTok; } final BeginGroupToken openParen = makeGroup(OPEN_PAREN_INFO, CLOSE_PAREN_INFO); final BeginGroupToken openBrace = makeGroup(OPEN_CURLY_BRACKET_INFO, CLOSE_CURLY_BRACKET_INFO); final BeginGroupToken openBracket = makeGroup(OPEN_SQUARE_BRACKET_INFO, CLOSE_SQUARE_BRACKET_INFO); final BeginGroupToken lt = makeGroup(LT_INFO, GT_INFO); Token get closeParen => openParen.endGroup; Token get closeBrace => openBrace.endGroup; Token get closeBracket => openBracket.endGroup; Token get gt => lt.endGroup; // Symbol tokens final Token semicolon = new SymbolToken(SEMICOLON_INFO, -1); final Token indexToken = new SymbolToken(INDEX_INFO, -1); // "[]" final Token question = new SymbolToken(QUESTION_INFO, -1); final Token colon = new SymbolToken(COLON_INFO, -1); final Token hash = new SymbolToken(HASH_INFO, -1); final Token bang = new SymbolToken(BANG_INFO, -1); final Token eq = new SymbolToken(EQ_INFO, -1); // Keyword tokens static Token makeIdToken(String text) { return new StringToken.fromString(IDENTIFIER_INFO, text, -1); } final Token newToken = makeIdToken('new'); final Token constToken = makeIdToken('const'); final Token throwToken = makeIdToken('throw'); final Token rethrowToken = makeIdToken('rethrow'); final Token breakToken = makeIdToken('break'); final Token continueToken = makeIdToken('continue'); final Token doToken = makeIdToken('do'); final Token whileToken = makeIdToken('while'); final Token ifToken = makeIdToken('if'); final Token elseToken = makeIdToken('else'); final Token awaitToken = makeIdToken('await'); final Token forToken = makeIdToken('for'); final Token inToken = makeIdToken('in'); final Token returnToken = makeIdToken('return'); final Token switchToken = makeIdToken('switch'); final Token caseToken = makeIdToken('case'); final Token defaultToken = makeIdToken('default'); final Token tryToken = makeIdToken('try'); final Token catchToken = makeIdToken('catch'); final Token onToken = makeIdToken('on'); final Token finallyToken = makeIdToken('finally'); final Token getToken = makeIdToken('get'); final Token setToken = makeIdToken('set'); final Token classToken = makeIdToken('class'); final Token extendsToken = makeIdToken('extends'); final Token withToken = makeIdToken('with'); final Token implementsToken = makeIdToken('implements'); final Token typedefToken = makeIdToken('typedef'); final Token enumToken = makeIdToken('enum'); static tree.Identifier makeIdentifier(String name) { return new tree.Identifier( new StringToken.fromString(IDENTIFIER_INFO, name, -1)); } static tree.Operator makeOperator(String name) { return new tree.Operator( new StringToken.fromString(IDENTIFIER_INFO, name, -1)); } // Utilities for creating NodeLists Link makeLink(Iterable nodes) { LinkBuilder builder = new LinkBuilder(); for (tree.Node node in nodes) { builder.addLast(node); } return builder.toLink(); } tree.NodeList blankList() { return new tree.NodeList(null, makeLink([]), null, ''); } tree.NodeList singleton(tree.Node node) { return new tree.NodeList(null, makeLink([node]), null, ''); } tree.NodeList makeList(String delimiter, Iterable nodes, { Token open, Token close }) { return new tree.NodeList(open, makeLink(nodes), close, delimiter); } tree.NodeList parenList(String delimiter, Iterable nodes) { return makeList(delimiter, nodes, open: openParen, close: closeParen); } tree.NodeList bracketList(String delimiter, Iterable nodes) { return makeList(delimiter, nodes, open: openBracket, close: closeBracket); } tree.NodeList braceList(String delimiter, Iterable nodes) { return makeList(delimiter, nodes, open: openBrace, close: closeBrace); } tree.NodeList argList(Iterable nodes) { return parenList(',', nodes); } tree.NodeList typeArgList(Iterable nodes) { return makeList(',', nodes, open: lt, close: gt); } /// Converts a qualified name into nested Sends. tree.Node makeName(String name) { if (name == null) { return null; } List names = name.split('.').toList(growable:false); tree.Node node = makeIdentifier(names[0]); for (int i = 1; i < names.length; i++) { node = new tree.Send(node, makeIdentifier(names[i])); } return node; } static Token assignmentToken(String operatorName) { switch (operatorName) { case '=': return new SymbolToken(EQ_INFO, -1); case '+=': return new SymbolToken(PLUS_EQ_INFO, -1); case '-=': return new SymbolToken(MINUS_EQ_INFO, -1); case '*=': return new SymbolToken(STAR_EQ_INFO, -1); case '/=': return new SymbolToken(SLASH_EQ_INFO, -1); case '~/=': return new SymbolToken(TILDE_SLASH_EQ_INFO, -1); case '%=': return new SymbolToken(PERCENT_EQ_INFO, -1); case '&=': return new SymbolToken(AMPERSAND_EQ_INFO, -1); case '^=': return new SymbolToken(CARET_EQ_INFO, -1); case '|=': return new SymbolToken(BAR_EQ_INFO, -1); case '>>=': return new SymbolToken(GT_GT_EQ_INFO, -1); case '<<=': return new SymbolToken(LT_LT_EQ_INFO, -1); default: throw "Unrecognized assignment operator: $operatorName"; } } static Token binopToken(String operatorName) { switch (operatorName) { case '+': return new SymbolToken(PLUS_INFO, -1); case '-': return new SymbolToken(MINUS_INFO, -1); case '*': return new SymbolToken(STAR_INFO, -1); case '/': return new SymbolToken(SLASH_INFO, -1); case '~/': return new SymbolToken(TILDE_SLASH_INFO, -1); case '%': return new SymbolToken(PERCENT_INFO, -1); case '&': return new SymbolToken(AMPERSAND_INFO, -1); case '^': return new SymbolToken(CARET_INFO, -1); case '|': return new SymbolToken(BAR_INFO, -1); case '>>': return new SymbolToken(GT_GT_INFO, -1); case '<<': return new SymbolToken(LT_LT_INFO, -1); case '==': return new SymbolToken(EQ_EQ_INFO, -1); case '!=': return new SymbolToken(BANG_EQ_INFO, -1); case '>': return new SymbolToken(GT_INFO, -1); case '>=': return new SymbolToken(GT_EQ_INFO, -1); case '<': return new SymbolToken(LT_INFO, -1); case '<=': return new SymbolToken(LT_EQ_INFO, -1); case '&&': return new SymbolToken(AMPERSAND_AMPERSAND_INFO, -1); case '||': return new SymbolToken(BAR_BAR_INFO, -1); default: throw "Unrecognized binary operator: $operatorName"; } } static Token incrementToken(String operatorName) { switch (operatorName) { case '++': return new SymbolToken(PLUS_PLUS_INFO, -1); case '--': return new SymbolToken(MINUS_MINUS_INFO, -1); default: throw "Unrecognized increment operator: $operatorName"; } } static Token typeOpToken(String operatorName) { switch (operatorName) { // "is!" is not an operator in the frontend AST. case 'is': return new SymbolToken(IS_INFO, -1); case 'as': return new SymbolToken(AS_INFO, -1); default: throw 'Unrecognized type operator: $operatorName'; } } Token unopToken(String operatorName) { switch (operatorName) { case '-': return new SymbolToken(MINUS_INFO, -1); case '~': return new SymbolToken(TILDE_INFO, -1); case '!': return bang; default: throw "Unrecognized unary operator: $operatorName"; } } tree.Node makeStaticReceiver(elements.Element element) { if (treeElements == null) return null; if (element.isStatic) { elements.ClassElement enclosingClass = element.enclosingClass; tree.Send send = new tree.Send( null, makeIdentifier(enclosingClass.name)); treeElements[send] = enclosingClass; return send; } else { return null; } } tree.Node makeArgument(Argument arg) { if (arg is Expression) { return makeExpression(arg); } else if (arg is NamedArgument) { return new tree.NamedArgument( makeIdentifier(arg.name), colon, makeExpression(arg.expression)); } else { throw "Unrecognized argument type: ${arg}"; } } tree.Node makeExpression(Expression exp) { return makeExp(exp, EXPRESSION); } /// Converts [exp] to a [tree.Node] that unparses to an expression with /// a precedence level of at least [minPrecedence]. The expression will be /// wrapped in a parenthesis if necessary. tree.Node makeExp(Receiver exp, int minPrecedence, {bool beginStmt: false}) { tree.Node result; int precedence; bool needParen = false; if (exp is SuperReceiver) { precedence = CALLEE; result = makeIdentifier('super'); } else if (exp is Assignment) { Expression left = exp.left; tree.Node receiver; tree.Node selector; tree.NodeList arguments; elements.Element element; if (left is Identifier) { receiver = makeStaticReceiver(left.element); selector = makeIdentifier(left.name); arguments = singleton(makeExpression(exp.right)); element = left.element; } else if (left is FieldExpression) { receiver = makeExp(left.object, PRIMARY, beginStmt: beginStmt); selector = makeIdentifier(left.fieldName); arguments = singleton(makeExpression(exp.right)); } else if (left is IndexExpression) { receiver = makeExp(left.object, PRIMARY, beginStmt: beginStmt); selector = new tree.Operator(indexToken); arguments = bracketList(',', [makeExpression(left.index), makeExpression(exp.right)]); } else { throw "Unexpected left-hand side of assignment: ${left}"; } tree.Operator op = new tree.Operator(assignmentToken(exp.operator)); result = new tree.SendSet(receiver, selector, op, arguments); if (left is Identifier) { setElement(result, element, exp); } precedence = EXPRESSION; } else if (exp is FieldInitializer) { precedence = EXPRESSION; tree.Node receiver = makeIdentifier('this'); tree.Node selector = makeIdentifier(exp.element.name); tree.Operator op = new tree.Operator(assignmentToken("=")); // We pass CALLEE to ensure we write eg.: // class B { var x; B() : x = (() {return a;}) {}} // Not the invalid: // class B { var x; B() : x = () {return a;} {}} result = new tree.SendSet(receiver, selector, op, singleton(makeExp(exp.body, CALLEE))); setElement(result, exp.element, exp); } else if (exp is SuperInitializer) { precedence = EXPRESSION; tree.Node receiver = makeIdentifier('super'); tree.NodeList arguments = argList(exp.arguments.map(makeArgument).toList()); if (exp.target.name == "") { result = new tree.Send(null, receiver, arguments); } else { result = new tree.Send(receiver, makeIdentifier(exp.target.name), arguments); } setElement(result, exp.target, exp); } else if (exp is BinaryOperator) { precedence = BINARY_PRECEDENCE[exp.operator]; int deltaLeft = isAssociativeBinaryOperator(precedence) ? 0 : 1; result = new tree.Send( makeExp(exp.left, precedence + deltaLeft, beginStmt: beginStmt), new tree.Operator(binopToken(exp.operator)), singleton(makeExp(exp.right, precedence + 1))); } else if (exp is CallFunction) { precedence = CALLEE; tree.Node selector; Expression callee = exp.callee; elements.Element element; tree.Node receiver; if (callee is Identifier) { receiver = makeStaticReceiver(callee.element); selector = makeIdentifier(callee.name); element = callee.element; } else { selector = makeExp(callee, CALLEE, beginStmt: beginStmt); } result = new tree.Send( receiver, selector, argList(exp.arguments.map(makeArgument))); if (callee is Identifier) { setElement(result, element, exp); } } else if (exp is CallMethod) { precedence = CALLEE; // TODO(sra): Elide receiver when This, but only if not in a scope that // shadows the method (e.g. constructor body). tree.Node receiver = makeExp(exp.object, PRIMARY, beginStmt: beginStmt); result = new tree.Send( receiver, makeIdentifier(exp.methodName), argList(exp.arguments.map(makeArgument))); } else if (exp is CallNew) { precedence = CALLEE; tree.Node selector = makeName(exp.type.name); if (exp.type.typeArguments.length > 0) { selector = new tree.TypeAnnotation( selector, typeArgList(exp.type.typeArguments.map(makeType))); setType(selector, exp.dartType, exp); } if (exp.constructorName != null) { selector = new tree.Send( selector, makeIdentifier(exp.constructorName)); } tree.Send send = new tree.Send( null, selector, argList(exp.arguments.map(makeArgument))); result = new tree.NewExpression( exp.isConst ? constToken : newToken, send); setType(result, exp.dartType, exp); setElement(send, exp.constructor, exp); } else if (exp is CallStatic) { precedence = CALLEE; result = new tree.Send( makeStaticReceiver(exp.element), makeIdentifier(exp.methodName), argList(exp.arguments.map(makeArgument))); setElement(result, exp.element, exp); } else if (exp is Conditional) { precedence = CONDITIONAL; result = new tree.Conditional( makeExp(exp.condition, LOGICAL_OR, beginStmt: beginStmt), makeExp(exp.thenExpression, EXPRESSION), makeExp(exp.elseExpression, EXPRESSION), question, colon); } else if (exp is FieldExpression) { precedence = PRIMARY; // TODO(sra): Elide receiver when This, but only if not in a scope that // shadows the method (e.g. constructor body). tree.Node receiver = makeExp(exp.object, PRIMARY, beginStmt: beginStmt); result = new tree.Send(receiver, makeIdentifier(exp.fieldName)); } else if (exp is ConstructorDefinition) { precedence = EXPRESSION; tree.NodeList parameters = makeParameters(exp.parameters); tree.NodeList initializers = exp.initializers == null || exp.initializers.isEmpty ? null : makeList(",", exp.initializers.map(makeExpression).toList()); tree.Node body = exp.isConst || exp.body == null ? new tree.EmptyStatement(semicolon) : makeFunctionBody(exp.body); result = new tree.FunctionExpression(constructorName(exp), parameters, body, null, // return type makeFunctionModifiers(exp), initializers, null, // get/set null); // async modifier setElement(result, exp.element, exp); } else if (exp is FunctionExpression) { precedence = PRIMARY; if (beginStmt && exp.name != null) { needParen = true; // Do not mistake for function declaration. } Token getOrSet = exp.isGetter ? getToken : exp.isSetter ? setToken : null; tree.NodeList parameters = exp.isGetter ? makeList("", []) : makeParameters(exp.parameters); tree.Node body = makeFunctionBody(exp.body); result = new tree.FunctionExpression( functionName(exp), parameters, body, exp.returnType == null || exp.element.isConstructor ? null : makeType(exp.returnType), makeFunctionModifiers(exp), null, // initializers getOrSet, // get/set null); // async modifier elements.Element element = exp.element; if (element != null) setElement(result, element, exp); } else if (exp is Identifier) { precedence = CALLEE; result = new tree.Send( makeStaticReceiver(exp.element), makeIdentifier(exp.name)); setElement(result, exp.element, exp); } else if (exp is Increment) { Expression lvalue = exp.expression; tree.Node receiver; tree.Node selector; tree.Node argument; bool innerBeginStmt = beginStmt && !exp.isPrefix; if (lvalue is Identifier) { receiver = makeStaticReceiver(lvalue.element); selector = makeIdentifier(lvalue.name); } else if (lvalue is FieldExpression) { receiver = makeExp(lvalue.object, PRIMARY, beginStmt: innerBeginStmt); selector = makeIdentifier(lvalue.fieldName); } else if (lvalue is IndexExpression) { receiver = makeExp(lvalue.object, PRIMARY, beginStmt: innerBeginStmt); selector = new tree.Operator(indexToken); argument = makeExpression(lvalue.index); } else { throw "Unrecognized left-hand side: ${lvalue}"; } tree.Operator op = new tree.Operator(incrementToken(exp.operator)); if (exp.isPrefix) { precedence = UNARY; result = new tree.SendSet.prefix(receiver, selector, op, argument); } else { precedence = POSTFIX_INCREMENT; result = new tree.SendSet.postfix(receiver, selector, op, argument); } if (lvalue is Identifier) { setElement(result, lvalue.element, exp); } } else if (exp is IndexExpression) { precedence = CALLEE; result = new tree.Send( makeExp(exp.object, PRIMARY, beginStmt: beginStmt), new tree.Operator(indexToken), bracketList(',', [makeExpression(exp.index)])); } else if (exp is Literal) { precedence = CALLEE; values.PrimitiveConstantValue value = exp.value; Token tok = new StringToken.fromString( STRING_INFO, '${value.primitiveValue}', -1); if (value.isString) { result = unparseStringLiteral(exp); } else if (value.isInt) { result = new tree.LiteralInt(tok, null); } else if (value.isDouble) { if (value.primitiveValue == double.INFINITY) { precedence = MULTIPLICATIVE; tok = new StringToken.fromString(STRING_INFO, '1/0.0', -1); } else if (value.primitiveValue == double.NEGATIVE_INFINITY) { precedence = MULTIPLICATIVE; tok = new StringToken.fromString(STRING_INFO, '-1/0.0', -1); } else if (value.primitiveValue.isNaN) { precedence = MULTIPLICATIVE; tok = new StringToken.fromString(STRING_INFO, '0/0.0', -1); } result = new tree.LiteralDouble(tok, null); } else if (value.isBool) { result = new tree.LiteralBool(tok, null); } else if (value.isNull) { result = new tree.LiteralNull(tok); } else { throw "Unrecognized constant: ${value}"; } } else if (exp is LiteralList) { precedence = PRIMARY; tree.NodeList typeArgs = null; if (exp.typeArgument != null) { typeArgs = typeArgList([makeType(exp.typeArgument)]); } result = new tree.LiteralList( typeArgs, bracketList(',', exp.values.map(makeExpression)), exp.isConst ? constToken : null); } else if (exp is LiteralMap) { precedence = PRIMARY; if (beginStmt) { // The opening brace can be confused with a block statement. needParen = true; } tree.NodeList typeArgs = null; if (exp.typeArguments != null && exp.typeArguments.length > 0) { typeArgs = typeArgList(exp.typeArguments.map(makeType)); } result = new tree.LiteralMap( typeArgs, braceList(',', exp.entries.map(makeLiteralMapEntry)), exp.isConst ? constToken : null); } else if (exp is LiteralSymbol) { precedence = PRIMARY; result = new tree.LiteralSymbol( hash, makeList('.', exp.id.split('.').map(makeIdentifier))); } else if (exp is LiteralType) { precedence = TYPE_LITERAL; elements.Element optionalElement = exp.type.element; result = new tree.Send( optionalElement == null ? null : makeStaticReceiver(optionalElement), makeIdentifier(exp.name)); treeElements.setType(result, exp.type); if (optionalElement != null) { // dynamic does not have an element setElement(result, optionalElement, exp); } } else if (exp is ReifyTypeVar) { precedence = PRIMARY; result = new tree.Send( null, makeIdentifier(exp.name)); setElement(result, exp.element, exp); setType(result, exp.element.type, exp); } else if (exp is StringConcat) { precedence = PRIMARY; result = unparseStringLiteral(exp); } else if (exp is This) { precedence = CALLEE; result = makeIdentifier('this'); } else if (exp is Throw) { precedence = EXPRESSION; // ??? result = new tree.Throw( makeExpression(exp.expression), throwToken, throwToken); // endToken not used by unparser } else if (exp is TypeOperator) { precedence = RELATIONAL; tree.Operator operator; tree.Node rightOperand = makeType(exp.type); if (exp.operator == 'is!') { operator = new tree.Operator(typeOpToken('is')); rightOperand = new tree.Send( rightOperand, new tree.Operator(bang), blankList()); } else { operator = new tree.Operator(typeOpToken(exp.operator)); } result = new tree.Send( makeExp(exp.expression, BITWISE_OR, beginStmt: beginStmt), operator, singleton(rightOperand)); } else if (exp is UnaryOperator) { precedence = UNARY; result = new tree.Send.prefix( makeExp(exp.operand, UNARY), new tree.Operator(unopToken(exp.operatorName))); } else { throw "Unknown expression type: ${exp}"; } needParen = needParen || precedence < minPrecedence; if (needParen) { result = parenthesize(result); } return result; } /// Creates a LiteralString with [verbatim] as the value. /// No (un)quoting or (un)escaping will be performed by this method. /// The [DartString] inside the literal will be set to null because the /// code emitter does not use it. tree.LiteralString makeVerbatimStringLiteral(String verbatim) { Token tok = new StringToken.fromString(STRING_INFO, verbatim, -1); return new tree.LiteralString(tok, null); } tree.LiteralMapEntry makeLiteralMapEntry(LiteralMapEntry en) { return new tree.LiteralMapEntry( makeExpression(en.key), colon, makeExpression(en.value)); } /// A comment token to be inserted when [INSERT_NEW_BACKEND_COMMENT] is true. final SymbolToken newBackendComment = new SymbolToken( const PrecedenceInfo('/* new backend */ ', 0, OPEN_CURLY_BRACKET_TOKEN), -1); tree.Node makeFunctionBody(Statement stmt) { if (INSERT_NEW_BACKEND_COMMENT) { return new tree.Block(makeList('', [makeBlock(stmt)], open: newBackendComment)); } else { return makeBlock(stmt); } } /// Produces a statement in a context where only blocks are allowed. tree.Node makeBlock(Statement stmt) { if (stmt is Block || stmt is EmptyStatement) { return makeStatement(stmt); } else { return new tree.Block(braceList('', [makeStatement(stmt)])); } } /// Adds the given statement to a block. If the statement itself is a block /// it will be flattened (if this does not change lexical scoping). void addBlockMember(Statement stmt, List accumulator) { if (stmt is Block && !stmt.statements.any(Unparser.definesVariable)) { for (Statement innerStmt in stmt.statements) { addBlockMember(innerStmt, accumulator); } } else if (stmt is EmptyStatement) { // No need to include empty statements inside blocks } else { accumulator.add(makeStatement(stmt)); } } /// True if [stmt] is equivalent to an empty statement. bool isEmptyStatement(Statement stmt) { return stmt is EmptyStatement || (stmt is Block && stmt.statements.every(isEmptyStatement)); } tree.Node makeStatement(Statement stmt, {bool shortIf: true}) { if (stmt is Block) { List body = []; for (Statement innerStmt in stmt.statements) { addBlockMember(innerStmt, body); } return new tree.Block(braceList('', body)); } else if (stmt is Break) { return new tree.BreakStatement( stmt.label == null ? null : makeIdentifier(stmt.label), breakToken, semicolon); } else if (stmt is Continue) { return new tree.ContinueStatement( stmt.label == null ? null : makeIdentifier(stmt.label), continueToken, semicolon); } else if (stmt is DoWhile) { return new tree.DoWhile( makeStatement(stmt.body, shortIf: shortIf), parenthesize(makeExpression(stmt.condition)), doToken, whileToken, semicolon); } else if (stmt is EmptyStatement) { return new tree.EmptyStatement(semicolon); } else if (stmt is ExpressionStatement) { return new tree.ExpressionStatement( makeExp(stmt.expression, EXPRESSION, beginStmt: true), semicolon); } else if (stmt is For) { tree.Node initializer; if (stmt.initializer is VariableDeclarations) { initializer = makeVariableDeclarations(stmt.initializer); } else if (stmt.initializer is Expression) { initializer = makeExpression(stmt.initializer); } else { initializer = null; } tree.Node condition; if (stmt.condition != null) { condition = new tree.ExpressionStatement( makeExpression(stmt.condition), semicolon); } else { condition = new tree.EmptyStatement(semicolon); } return new tree.For( initializer, condition, makeList(',', stmt.updates.map(makeExpression)), makeStatement(stmt.body, shortIf: shortIf), forToken); } else if (stmt is ForIn) { tree.Node left; if (stmt.leftHandValue is Identifier) { left = makeExpression(stmt.leftHandValue); } else { left = makeVariableDeclarations(stmt.leftHandValue); } return new tree.ForIn( left, makeExpression(stmt.expression), makeStatement(stmt.body, shortIf: shortIf), awaitToken, forToken, inToken); } else if (stmt is FunctionDeclaration) { tree.FunctionExpression function = new tree.FunctionExpression( stmt.name != null ? makeIdentifier(stmt.name) : null, makeParameters(stmt.parameters), makeFunctionBody(stmt.body), stmt.returnType != null ? makeType(stmt.returnType) : null, makeEmptyModifiers(), null, // initializers null, // get/set null); // async modifier setElement(function, stmt.function.element, stmt); return new tree.FunctionDeclaration(function); } else if (stmt is If) { if (stmt.elseStatement == null || isEmptyStatement(stmt.elseStatement)) { tree.Node node = new tree.If( parenthesize(makeExpression(stmt.condition)), makeStatement(stmt.thenStatement), null, // else statement ifToken, null); // else token if (shortIf) return node; else return new tree.Block(braceList('', [node])); } else { return new tree.If( parenthesize(makeExpression(stmt.condition)), makeStatement(stmt.thenStatement, shortIf: false), makeStatement(stmt.elseStatement, shortIf: shortIf), ifToken, elseToken); // else token } } else if (stmt is LabeledStatement) { List labels = []; Statement inner = stmt; while (inner is LabeledStatement) { LabeledStatement lbl = inner as LabeledStatement; labels.add(new tree.Label(makeIdentifier(lbl.label), colon)); inner = lbl.statement; } return new tree.LabeledStatement( makeList('', labels), makeStatement(inner, shortIf: shortIf)); } else if (stmt is Rethrow) { return new tree.Rethrow(rethrowToken, semicolon); } else if (stmt is Return) { return new tree.Return( returnToken, semicolon, stmt.expression == null ? null : makeExpression(stmt.expression)); } else if (stmt is Switch) { return new tree.SwitchStatement( parenthesize(makeExpression(stmt.expression)), braceList('', stmt.cases.map(makeSwitchCase)), switchToken); } else if (stmt is Try) { return new tree.TryStatement( makeBlock(stmt.tryBlock), makeList(null, stmt.catchBlocks.map(makeCatchBlock)), stmt.finallyBlock == null ? null : makeBlock(stmt.finallyBlock), tryToken, stmt.finallyBlock == null ? null : finallyToken); } else if (stmt is VariableDeclarations) { return makeVariableDeclarations(stmt, useVar: true, endToken: semicolon); } else if (stmt is While) { return new tree.While( parenthesize(makeExpression(stmt.condition)), makeStatement(stmt.body, shortIf: shortIf), whileToken); } else { throw "Unrecognized statement: ${stmt}"; } } tree.Node makeVariableDeclaration(VariableDeclaration vd) { tree.Node id = makeIdentifier(vd.name); setElement(id, vd.element, vd); if (vd.initializer == null) { return id; } tree.Node send = new tree.SendSet( null, id, new tree.Operator(eq), singleton(makeExpression(vd.initializer))); setElement(send, vd.element, vd); return send; } /// If [useVar] is true, the variable definition will use `var` as modifier /// if no other modifiers are present. /// [endToken] will be used to terminate the declaration list. tree.Node makeVariableDeclarations(VariableDeclarations decl, { bool useVar: false, Token endToken: null }) { return new tree.VariableDefinitions( decl.type == null ? null : makeType(decl.type), makeVarModifiers(isConst: decl.isConst, isFinal: decl.isFinal, useVar: useVar && decl.type == null), makeList(',', decl.declarations.map(makeVariableDeclaration), close: endToken)); } tree.CatchBlock makeCatchBlock(CatchBlock block) { List formals = []; if (block.exceptionVar != null) { tree.Node exceptionName = makeIdentifier(block.exceptionVar.name); setElement(exceptionName, block.exceptionVar.element, block.exceptionVar); formals.add(new tree.VariableDefinitions( null, makeEmptyModifiers(), singleton(exceptionName))); } if (block.stackVar != null) { tree.Node stackTraceName = makeIdentifier(block.stackVar.name); setElement(stackTraceName, block.stackVar.element, block.stackVar); formals.add(new tree.VariableDefinitions( null, makeEmptyModifiers(), singleton(stackTraceName))); } return new tree.CatchBlock( block.onType == null ? null : makeType(block.onType), block.exceptionVar == null ? null : argList(formals), makeBlock(block.body), block.onType == null ? null : onToken, block.exceptionVar == null ? null : catchToken); } tree.SwitchCase makeSwitchCase(SwitchCase caze) { if (caze.isDefaultCase) { return new tree.SwitchCase( blankList(), defaultToken, makeList('', caze.statements.map(makeStatement)), null); // startToken unused by unparser } else { return new tree.SwitchCase( makeList('', caze.expressions.map(makeCaseMatch)), null, // defaultKeyword, makeList('', caze.statements.map(makeStatement)), null); // startToken unused by unparser } } tree.CaseMatch makeCaseMatch(Expression exp) { return new tree.CaseMatch(caseToken, makeExpression(exp), colon); } tree.TypeAnnotation makeType(TypeAnnotation type) { tree.NodeList typeArgs; if (type.typeArguments.length > 0) { typeArgs = typeArgList(type.typeArguments.map(makeType)); } else { typeArgs = null; } tree.TypeAnnotation result = new tree.TypeAnnotation(makeIdentifier(type.name), typeArgs); setType(result, type.dartType, type); return result; } tree.NodeList makeParameters(Parameters params) { List nodes = params.requiredParameters.map(makeParameter).toList(); if (params.hasOptionalParameters) { Token assign = params.hasNamedParameters ? colon : eq; Token open = params.hasNamedParameters ? openBrace : openBracket; Token close = params.hasNamedParameters ? closeBrace : closeBracket; Iterable opt = params.optionalParameters.map((p) => makeParameter(p,assign)); nodes.add(new tree.NodeList(open, makeLink(opt), close, ',')); } return argList(nodes); } /// [assignOperator] is used for writing the default value. tree.Node makeParameter(Parameter param, [Token assignOperator]) { if (param.isFunction) { tree.Node definition = new tree.FunctionExpression( makeIdentifier(param.name), makeParameters(param.parameters), null, // body param.type == null ? null : makeType(param.type), makeEmptyModifiers(), // TODO: Function parameter modifiers? null, // initializers null, // get/set null); // async modifier if (param.element != null) { setElement(definition, param.element, param); } if (param.defaultValue != null) { definition = new tree.SendSet( null, definition, new tree.Operator(assignOperator), singleton(makeExpression(param.defaultValue))); } return new tree.VariableDefinitions( null, makeEmptyModifiers(), singleton(definition)); } else { tree.Node definition; if (param.defaultValue != null) { definition = new tree.SendSet( null, makeIdentifier(param.name), new tree.Operator(assignOperator), singleton(makeExpression(param.defaultValue))); } else { definition = makeIdentifier(param.name); } if (param.element != null) { setElement(definition, param.element, param); } return new tree.VariableDefinitions( param.type == null ? null : makeType(param.type), makeEmptyModifiers(), // TODO: Parameter modifiers? singleton(definition)); } } tree.Modifiers makeEmptyModifiers() { return new tree.Modifiers(blankList()); } tree.Modifiers makeModifiers({bool isExternal: false, bool isStatic: false, bool isAbstract: false, bool isFactory: false, bool isConst: false, bool isFinal: false, bool isVar: false}) { List nodes = []; if (isExternal) { nodes.add(makeIdentifier('external')); } if (isStatic) { nodes.add(makeIdentifier('static')); } if (isAbstract) { nodes.add(makeIdentifier('abstract')); } if (isFactory) { nodes.add(makeIdentifier('factory')); } if (isConst) { nodes.add(makeIdentifier('const')); } if (isFinal) { nodes.add(makeIdentifier('final')); } if (isVar) { nodes.add(makeIdentifier('var')); } return new tree.Modifiers(makeList(' ', nodes)); } tree.Modifiers makeVarModifiers({bool isConst: false, bool isFinal: false, bool useVar: false, bool isStatic: false}) { return makeModifiers(isStatic: isStatic, isConst: isConst, isFinal: isFinal, isVar: useVar && !(isConst || isFinal)); } tree.Modifiers makeFunctionModifiers(FunctionExpression exp) { if (exp.element == null) return makeEmptyModifiers(); return makeModifiers(isExternal: exp.element.isExternal, isStatic: exp.element.isStatic, isFactory: exp.element.isFactoryConstructor, isConst: exp.element.isConst); } tree.Node makeNodeForClassElement(elements.ClassElement cls) { if (cls.isMixinApplication) { return makeNamedMixinApplication(cls); } else if (cls.isEnumClass) { return makeEnum(cls); } else { return makeClassNode(cls); } } tree.Typedef makeTypedef(elements.TypedefElement typdef) { types.FunctionType functionType = typdef.alias; final tree.TypeAnnotation returnType = makeType(TypeGenerator.createType(functionType.returnType)); final tree.Identifier name = makeIdentifier(typdef.name); final tree.NodeList typeParameters = makeTypeParameters(typdef.typeVariables); final tree.NodeList formals = makeParameters(TypeGenerator.createParametersFromType(functionType)); final Token typedefKeyword = typedefToken; final Token endToken = semicolon; return new tree.Typedef(returnType, name, typeParameters, formals, typedefKeyword, endToken); } /// Create a [tree.NodeList] containing the type variable declarations in /// [typeVaraiables. tree.NodeList makeTypeParameters(List typeVariables) { if (typeVariables.isEmpty) { return new tree.NodeList.empty(); } else { List typeVariableList = []; for (types.TypeVariableType typeVariable in typeVariables) { tree.Node id = makeIdentifier(typeVariable.name); treeElements[id] = typeVariable.element; tree.Node bound; if (!typeVariable.element.bound.isObject) { bound = makeType(TypeGenerator.createType(typeVariable.element.bound)); } tree.TypeVariable node = new tree.TypeVariable(id, bound); treeElements.setType(node, typeVariable); typeVariableList.add(node); } return makeList(',', typeVariableList, open: lt, close: gt); } } /// Create a [tree.NodeList] containing the declared interfaces. /// /// [interfaces] is from [elements.ClassElement] in reverse declaration order /// and it contains mixins. To produce a list of the declared interfaces only, /// interfaces in [mixinTypes] are omitted. /// /// [forNamedMixinApplication] is because the structure of the [tree.NodeList] /// differs between [tree.NamedMixinApplication] and [tree.ClassNode]. // TODO(johnniwinther): Normalize interfaces on[tree.NamedMixinApplication] // and [tree.ClassNode]. tree.NodeList makeInterfaces(Link interfaces, Set mixinTypes, {bool forNamedMixinApplication: false}) { Link typeAnnotations = const Link(); for (Link link = interfaces; !link.isEmpty; link = link.tail) { types.DartType interface = link.head; if (!mixinTypes.contains(interface)) { typeAnnotations = typeAnnotations.prepend( makeType(TypeGenerator.createType(interface))); } } if (typeAnnotations.isEmpty) { return forNamedMixinApplication ? null : new tree.NodeList.empty(); } else { return new tree.NodeList( forNamedMixinApplication ? null : implementsToken, typeAnnotations, null, ','); } } /// Creates a [tree.NamedMixinApplication] node for [cls]. // TODO(johnniwinther): Unify creation of mixin lists between // [NamedMixinApplicationElement] and [ClassElement]. tree.NamedMixinApplication makeNamedMixinApplication( elements.MixinApplicationElement cls) { assert(dart2js.invariant(cls, !cls.isUnnamedMixinApplication, message: "Cannot create ClassNode for unnamed mixin application " "$cls.")); tree.Modifiers modifiers = makeModifiers(isAbstract: cls.isAbstract); tree.Identifier name = makeIdentifier(cls.name); tree.NodeList typeParameters = makeTypeParameters(cls.typeVariables); Set mixinTypes = new Set(); Link mixins = const Link(); void addMixin(types.DartType mixinType) { mixinTypes.add(mixinType); mixins = mixins.prepend(makeType(TypeGenerator.createType(mixinType))); } addMixin(cls.mixinType); tree.Node superclass; types.InterfaceType supertype = cls.supertype; while (supertype.element.isUnnamedMixinApplication) { elements.MixinApplicationElement mixinApplication = supertype.element; addMixin(cls.asInstanceOf(mixinApplication.mixin)); supertype = mixinApplication.supertype; } superclass = makeType(TypeGenerator.createType(cls.asInstanceOf(supertype.element))); tree.Node supernode = new tree.MixinApplication( superclass, new tree.NodeList(null, mixins, null, ',')); tree.NodeList interfaces = makeInterfaces( cls.interfaces, mixinTypes, forNamedMixinApplication: true); return new tree.NamedMixinApplication( name, typeParameters, modifiers, supernode, interfaces, classToken, semicolon); } tree.Enum makeEnum(elements.EnumClassElement cls) { return new tree.Enum( enumToken, makeIdentifier(cls.name), makeList(',', cls.enumValues.map((e) => makeIdentifier(e.name)), open: openBrace, close: closeBrace)); } /// Creates a [tree.ClassNode] node for [cls]. tree.ClassNode makeClassNode(elements.ClassElement cls) { assert(dart2js.invariant(cls, !cls.isUnnamedMixinApplication, message: "Cannot create ClassNode for unnamed mixin application " "$cls.")); tree.Modifiers modifiers = makeModifiers(isAbstract: cls.isAbstract); tree.Identifier name = makeIdentifier(cls.name); tree.NodeList typeParameters = makeTypeParameters(cls.typeVariables); tree.Node supernode; types.InterfaceType supertype = cls.supertype; Set mixinTypes = new Set(); Link mixins = const Link(); void addMixin(types.DartType mixinType) { mixinTypes.add(mixinType); mixins = mixins.prepend(makeType(TypeGenerator.createType(mixinType))); } if (supertype != null) { tree.Node superclass; if (supertype.element.isUnnamedMixinApplication) { while (supertype.element.isUnnamedMixinApplication) { elements.MixinApplicationElement mixinApplication = supertype.element; addMixin(cls.asInstanceOf(mixinApplication.mixin)); supertype = mixinApplication.supertype; } tree.Node superclass = makeType( TypeGenerator.createType(cls.asInstanceOf(supertype.element))); supernode = new tree.MixinApplication( superclass, new tree.NodeList(null, mixins, null, ',')); } else if (!supertype.isObject) { supernode = makeType(TypeGenerator.createType(supertype)); } } tree.NodeList interfaces = makeInterfaces( cls.interfaces, mixinTypes); Token extendsKeyword = supernode != null ? extendsToken : null; return new tree.ClassNode( modifiers, name, typeParameters, supernode, interfaces, openBrace, extendsKeyword, null, // No body. closeBrace); } tree.Node constructorName(ConstructorDefinition exp) { String name = exp.name; tree.Identifier className = makeIdentifier(exp.element.enclosingClass.name); tree.Node result = name == "" ? className : new tree.Send(className, makeIdentifier(name)); setElement(result, exp.element, exp); return result; } tree.Node functionName(FunctionExpression exp) { String name = exp.name; if (name == null) return null; if (isUserDefinableOperator(name)) { return makeOperator("operator$name"); } else if (name == "unary-") { return makeOperator("operator-"); } return makeIdentifier(name); } tree.Node parenthesize(tree.Node node) { return new tree.ParenthesizedExpression(node, openParen); } tree.Node unparseStringLiteral(Expression exp) { StringLiteralOutput output = Unparser.analyzeStringLiteral(exp); List parts = output.parts; tree.Node printStringChunk(StringChunk chunk) { bool raw = chunk.quoting.raw; int quoteCode = chunk.quoting.quote; List literalParts = []; tree.LiteralString firstLiteral; tree.Node currentInterpolation; // sb contains the current unfinished LiteralString StringBuffer sb = new StringBuffer(); if (raw) { sb.write('r'); } for (int i = 0; i < chunk.quoting.leftQuoteCharCount; i++) { sb.write(chunk.quoting.quoteChar); } // Print every character and string interpolation int startIndex = chunk.previous != null ? chunk.previous.endIndex : 0; for (int i = startIndex; i < chunk.endIndex; i++) { var part = parts[i]; if (part is Expression) { // Finish the previous string interpolation, if there is one. tree.LiteralString lit = makeVerbatimStringLiteral(sb.toString()); if (currentInterpolation != null) { literalParts.add(new tree.StringInterpolationPart( currentInterpolation, lit)); } else { firstLiteral = lit; } sb.clear(); currentInterpolation = makeExpression(part); } else { int char = part; sb.write(Unparser.getEscapedCharacter(char, quoteCode, raw)); } } // Print ending quotes for (int i = 0; i < chunk.quoting.rightQuoteLength; i++) { sb.write(chunk.quoting.quoteChar); } // Finish the previous string interpolation, if there is one. // Then wrap everything in a StringInterpolation, if relevant. tree.LiteralString lit = makeVerbatimStringLiteral(sb.toString()); tree.Node node; if (firstLiteral == null) { node = lit; } else { literalParts.add(new tree.StringInterpolationPart( currentInterpolation, lit)); node = new tree.StringInterpolation( firstLiteral, makeList('', literalParts)); } // Juxtapose with the previous string chunks, if any. if (chunk.previous != null) { return new tree.StringJuxtaposition( printStringChunk(chunk.previous), node); } else { return node; } } return printStringChunk(output.chunk); } }