Linter Demo Errors: 2Warnings: 104File: /home/fstrocco/Dart/dart/benchmark/compiler/lib/src/dart_backend/backend_ast_emitter.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 backend_ast_emitter; import '../tree_ir/tree_ir_nodes.dart' as tree; import 'backend_ast_nodes.dart'; import '../constants/expressions.dart'; import '../constants/values.dart'; import '../dart_types.dart'; import '../elements/elements.dart'; import '../elements/modelx.dart' as modelx; import '../universe/universe.dart'; import '../tree/tree.dart' as tree show Modifiers; /// Translates the dart_tree IR to Dart backend AST. RootNode emit(tree.RootNode root) { return new ASTEmitter().emit(root); } // TODO(johnniwinther): Split into function/block state. class BuilderContext { /// Builder context for the enclosing function, or null if the current /// function is not a local function. BuilderContext _parent; /// Variables to be hoisted at the top of the current function. final List variables = []; /// Maps variables to their name. final Map variableNames; /// Maps local constants to their name. final Map constantNames = {}; /// Variables that have had their declaration created. final Set declaredVariables = new Set(); /// Variables that are used as catch handler parameters. final Set handlerVariables = new Set(); /// Variable names that have already been used. Used to avoid name clashes. final Set usedVariableNames; /// Statements emitted by the most recent call to [visitStatement]. List _statementBuffer = []; /// The element currently being emitted. ExecutableElement currentElement; /// Bookkeeping object needed to synthesize a variable declaration. final modelx.VariableList variableList = new modelx.VariableList(tree.Modifiers.EMPTY); /// Input to [visitStatement]. Denotes the statement that will execute next /// if the statements produced by [visitStatement] complete normally. /// Set to null if control will fall over the end of the method. tree.Statement fallthrough = null; /// Labels that could not be eliminated using fallthrough. final Set _usedLabels = new Set(); final bool inInitializer; /// The first dart_tree statement that is not converted to a variable /// initializer. tree.Statement firstStatement; BuilderContext() : usedVariableNames = new Set(), inInitializer = false, variableNames = {}; BuilderContext.inner(BuilderContext parent) : this._parent = parent, usedVariableNames = parent.usedVariableNames, inInitializer = false, variableNames = {}; BuilderContext.initializer(BuilderContext parent) : this._parent = parent, usedVariableNames = parent.usedVariableNames, inInitializer = true, variableNames = new Map.from(parent.variableNames); // TODO(johnniwinther): Fully encapsulate handling of parameter, variable // and local function declarations. void addDeclaration(tree.Variable variable, [Expression initializer]) { assert(!declaredVariables.contains(variable)); String name = getVariableName(variable); VariableDeclaration decl = new VariableDeclaration(name, initializer); decl.element = variable.element; declaredVariables.add(variable); variables.add(decl); } /// Creates an [Identifier] referring to the given variable. Expression makeVariableAccess(tree.Variable variable) { return new Identifier(getVariableName(variable)) ..element = variable.element; } /// Generates a name for the given variable and synthesizes an element for it, /// if necessary. String getVariableName(tree.Variable variable) { // If the variable belongs to an enclosing function, ask the parent emitter // for the variable name. if (!inInitializer && variable.host != currentElement) { return _parent.getVariableName(variable); } // Get the name if we already have one. String name = variableNames[variable]; if (name != null) { return name; } // Synthesize a variable name that isn't used elsewhere. // The [usedVariableNames] set is shared between nested emitters, // so this also prevents clash with variables in an enclosing/inner scope. // The renaming phase after codegen will further prefix local variables // so they cannot clash with top-level variables or fields. String prefix = variable.element == null ? 'v' : variable.element.name; int counter = 0; name = variable.element == null ? '$prefix$counter' : variable.element.name; while (!usedVariableNames.add(name)) { ++counter; name = '$prefix$counter'; } variableNames[variable] = name; // Synthesize an element for the variable if (variable.element == null || name != variable.element.name) { // TODO(johnniwinther): Replace by synthetic [Entity]. variable.element = new _SyntheticLocalVariableElement( name, currentElement, variableList); } return name; } String getConstantName(VariableElement element) { assert(element.kind == ElementKind.VARIABLE); if (element.enclosingElement != currentElement) { return _parent.getConstantName(element); } String name = constantNames[element]; if (name != null) { return name; } String prefix = element.name; int counter = 0; name = element.name; while (!usedVariableNames.add(name)) { ++counter; name = '$prefix$counter'; } constantNames[element] = name; return name; } List inSubcontext(f(BuilderContext subcontext), {tree.Statement fallthrough}) { List savedBuffer = this._statementBuffer; tree.Statement savedFallthrough = this.fallthrough; List buffer = this._statementBuffer = []; if (fallthrough != null) { this.fallthrough = fallthrough; } f(this); this.fallthrough = savedFallthrough; this._statementBuffer = savedBuffer; return buffer; } /// Removes a trailing "return null" from the current block. void removeTrailingReturn(bool isReturnNull(T statement)) { if (_statementBuffer.isEmpty) return; if (isReturnNull(_statementBuffer.last)) { _statementBuffer.removeLast(); } } /// Register [label] as used. void useLabel(tree.Label label) { _usedLabels.add(label); } /// Remove [label] and return `true` if it was used. bool removeUsedLabel(tree.Label label) { return _usedLabels.remove(label); } /// Add [statement] to the current block. void addStatement(T statement) { _statementBuffer.add(statement); } /// The statements in the current block. Iterable get statements => _statementBuffer; } /// Translates the dart_tree IR to Dart backend AST. /// An instance of this class should only be used once; a fresh emitter /// must be created for each function to be emitted. class ASTEmitter extends tree.StatementVisitor1> with tree.ExpressionVisitor1>, tree.RootVisitor1>, tree.InitializerVisitor1> { RootNode emit(tree.RootNode node) { return visitRootNode(node, new BuilderContext()); } @override FieldDefinition visitFieldDefinition(tree.FieldDefinition definition, BuilderContext context) { context.currentElement = definition.element; Expression initializer; if (!definition.isEmpty) { visitStatement(definition.body, context); List bodyParts; for (tree.Variable variable in context.variableNames.keys) { if (!context.declaredVariables.contains(variable)) { context.addDeclaration(variable); } } if (context.variables.length > 0) { bodyParts = new List(); bodyParts.add(new VariableDeclarations(context.variables)); bodyParts.addAll(context.statements); } else { bodyParts = context.statements; } initializer = ensureExpression(bodyParts); } return new FieldDefinition(definition.element, initializer); } /// Returns an expression that will evaluate all of [bodyParts]. /// If [bodyParts] is a single [Return] return its value. /// Otherwise wrap the body-parts in an immediately invoked closure. Expression ensureExpression(List bodyParts) { if (bodyParts.length == 1) { Statement onlyStatement = bodyParts.single; if (onlyStatement is Return) { return onlyStatement.expression; } } Statement body = new Block(bodyParts); FunctionExpression function = new FunctionExpression(new Parameters([]), body); function.element = null; return new CallFunction(function, []); } bool _recognizeTrailingReturn(Statement statement) { if (statement is Return) { Expression expr = statement.expression; if (expr == null || expr is Literal && expr.value.isNull) { return true; } } return false; } @override FunctionExpression visitConstructorDefinition( tree.ConstructorDefinition definition, BuilderContext context) { context.currentElement = definition.element; Parameters parameters = emitRootParameters( definition, definition.defaultParameterValues, context); // Declare parameters. for (tree.Variable param in definition.parameters) { context.variableNames[param] = param.element.name; context.usedVariableNames.add(param.element.name); context.declaredVariables.add(param); } List initializers; Statement body; if (!definition.isEmpty) { initializers = definition.initializers.map((tree.Initializer initializer) { return visitInitializer(initializer, context); }).toList(); context.firstStatement = definition.body; visitStatement(definition.body, context); context.removeTrailingReturn(_recognizeTrailingReturn); // Some of the variable declarations have already been added // if their first assignment could be pulled into the initializer. // Add the remaining variable declarations now. for (tree.Variable variable in context.variableNames.keys) { if (!context.declaredVariables.contains(variable)) { context.addDeclaration(variable); } } // Add constant declarations. List constants = []; for (ConstDeclaration constDecl in definition.localConstants) { if (!context.constantNames.containsKey(constDecl.element)) { continue; // Discard unused constants declarations. } String name = context.getConstantName(constDecl.element); Expression value = ConstantEmitter.createExpression(constDecl.expression, context); VariableDeclaration decl = new VariableDeclaration(name, value); decl.element = constDecl.element; constants.add(decl); } List bodyParts = []; if (constants.length > 0) { bodyParts.add(new VariableDeclarations(constants, isConst: true)); } if (context.variables.length > 0) { bodyParts.add(new VariableDeclarations(context.variables)); } bodyParts.addAll(context.statements); body = new Block(bodyParts); } FunctionType functionType = context.currentElement.type; return new ConstructorDefinition( parameters, body, initializers, context.currentElement.name, definition.element.isConst) ..element = context.currentElement; } @override FunctionExpression visitFunctionDefinition( tree.FunctionDefinition definition, BuilderContext context) { context.currentElement = definition.element; Parameters parameters = emitRootParameters( definition, definition.defaultParameterValues, context); // Declare parameters. for (tree.Variable param in definition.parameters) { context.variableNames[param] = param.element.name; context.usedVariableNames.add(param.element.name); context.declaredVariables.add(param); } Statement body; if (definition.isEmpty) { body = new EmptyStatement(); } else { context.firstStatement = definition.body; visitStatement(definition.body, context); context.removeTrailingReturn(_recognizeTrailingReturn); // Some of the variable declarations have already been added // if their first assignment could be pulled into the initializer. // Add the remaining variable declarations now. for (tree.Variable variable in context.variableNames.keys) { if (!context.declaredVariables.contains(variable) && !context.handlerVariables.contains(variable)) { context.addDeclaration(variable); } } // Add constant declarations. List constants = []; for (ConstDeclaration constDecl in definition.localConstants) { if (!context.constantNames.containsKey(constDecl.element)) { continue; // Discard unused constants declarations. } String name = context.getConstantName(constDecl.element); Expression value = ConstantEmitter.createExpression(constDecl.expression, context); VariableDeclaration decl = new VariableDeclaration(name, value); decl.element = constDecl.element; constants.add(decl); } List bodyParts = []; if (constants.length > 0) { bodyParts.add(new VariableDeclarations(constants, isConst: true)); } if (context.variables.length > 0) { bodyParts.add(new VariableDeclarations(context.variables)); } bodyParts.addAll(context.statements); body = new Block(bodyParts); } FunctionType functionType = context.currentElement.type; return new FunctionExpression( parameters, body, name: context.currentElement.name, returnType: TypeGenerator.createOptionalType(functionType.returnType), isGetter: context.currentElement.isGetter, isSetter: context.currentElement.isSetter) ..element = context.currentElement; } /// Emits parameters that are not nested inside other parameters. /// Root parameters can have default values, while inner parameters cannot. Parameters emitRootParameters(tree.RootNode function, List defaults, BuilderContext context) { FunctionType functionType = function.element.type; List required = TypeGenerator.createParameters( functionType.parameterTypes, context: context, elements: function.parameters.map((p) => p.element)); bool optionalParametersAreNamed = !functionType.namedParameters.isEmpty; List optional = TypeGenerator.createParameters( optionalParametersAreNamed ? functionType.namedParameterTypes : functionType.optionalParameterTypes, context: context, defaultValues: defaults, elements: function.parameters.skip(required.length) .map((p) => p.element)); return new Parameters(required, optional, optionalParametersAreNamed); } /// True if the two expressions are a reference to the same variable. bool isSameVariable(Receiver e1, Receiver e2) { return e1 is Identifier && e2 is Identifier && e1.element is VariableElement && e1.element == e2.element; } Expression makeAssignment(Expression target, Expression value) { // Try to print as compound assignment or increment if (value is BinaryOperator && isCompoundableOperator(value.operator)) { Receiver leftOperand = value.left; Expression rightOperand = value.right; bool valid = false; if (isSameVariable(target, leftOperand)) { valid = true; } else if (target is FieldExpression && leftOperand is FieldExpression && isSameVariable(target.object, leftOperand.object) && target.fieldName == leftOperand.fieldName) { valid = true; } else if (target is IndexExpression && leftOperand is IndexExpression && isSameVariable(target.object, leftOperand.object) && isSameVariable(target.index, leftOperand.index)) { valid = true; } if (valid) { if (rightOperand is Literal && rightOperand.value.isOne && (value.operator == '+' || value.operator == '-')) { return new Increment.prefix(target, value.operator + value.operator); } else { return new Assignment(target, value.operator + '=', rightOperand); } } } // Fall back to regular assignment return new Assignment(target, '=', value); } Block visitInSubContext(tree.Statement statement, BuilderContext context, {tree.Statement fallthrough}) { return new Block(context.inSubcontext( (BuilderContext subcontext) { visitStatement(statement, subcontext); }, fallthrough: fallthrough)); } void addLabeledStatement(tree.Label label, Statement statement, BuilderContext context) { if (context.removeUsedLabel(label)) { context.addStatement(new LabeledStatement(label.name, statement)); } else { context.addStatement(statement); } } @override void visitExpressionStatement(tree.ExpressionStatement stmt, BuilderContext context) { Expression e = visitExpression(stmt.expression, context); context.addStatement(new ExpressionStatement(e)); visitStatement(stmt.next, context); } @override void visitLabeledStatement(tree.LabeledStatement stmt, BuilderContext context) { Block block = visitInSubContext(stmt.body, context, fallthrough: stmt.next); addLabeledStatement(stmt.label, block, context); visitStatement(stmt.next, context); } bool isNullLiteral(Expression exp) => exp is Literal && exp.value.isNull; @override void visitAssign(tree.Assign stmt, BuilderContext context) { // Try to emit a local function declaration. This is useful for functions // that may occur in expression context, but could not be inlined anywhere. if (stmt.variable.element is FunctionElement && stmt.value is tree.FunctionExpression && !context.declaredVariables.contains(stmt.variable) && stmt.variable.writeCount == 1) { tree.FunctionExpression functionExp = stmt.value; FunctionExpression function = makeSubFunction(functionExp.definition, context); FunctionDeclaration decl = new FunctionDeclaration(function); context.addStatement(decl); context.declaredVariables.add(stmt.variable); visitStatement(stmt.next, context); return; } bool isFirstOccurrence = (context.variableNames[stmt.variable] == null); bool isDeclaredHere = stmt.variable.host == context.currentElement; String name = context.getVariableName(stmt.variable); Expression definition = visitExpression(stmt.value, context); // Try to pull into initializer. if (context.firstStatement == stmt && isFirstOccurrence && isDeclaredHere) { if (isNullLiteral(definition)) definition = null; context.addDeclaration(stmt.variable, definition); context.firstStatement = stmt.next; visitStatement(stmt.next, context); return; } // Emit a variable declaration if we are required to do so. // For captured variables, this ensures that a fresh variable is created. if (stmt.isDeclaration) { assert(isFirstOccurrence); assert(isDeclaredHere); if (isNullLiteral(definition)) definition = null; VariableDeclaration decl = new VariableDeclaration(name, definition) ..element = stmt.variable.element; context.declaredVariables.add(stmt.variable); context.addStatement(new VariableDeclarations([decl])); visitStatement(stmt.next, context); return; } context.addStatement(new ExpressionStatement(makeAssignment( context.makeVariableAccess(stmt.variable), definition))); visitStatement(stmt.next, context); } @override void visitReturn(tree.Return stmt, BuilderContext context) { if (context.currentElement.isGenerativeConstructor && !context.inInitializer) { assert(() { tree.Expression value = stmt.value; return value is tree.Constant && value.value.isNull; }); context.addStatement(new Return(null)); } else { Expression inner = visitExpression(stmt.value, context); context.addStatement(new Return(inner)); } } @override void visitBreak(tree.Break stmt, BuilderContext context) { tree.Statement fall = context.fallthrough; if (stmt.target.binding.next == fall) { // Fall through to break target } else if (fall is tree.Break && fall.target == stmt.target) { // Fall through to equivalent break } else { context.useLabel(stmt.target); context.addStatement(new Break(stmt.target.name)); } } @override void visitContinue(tree.Continue stmt, BuilderContext context) { tree.Statement fall = context.fallthrough; if (stmt.target.binding == fall) { // Fall through to continue target } else if (fall is tree.Continue && fall.target == stmt.target) { // Fall through to equivalent continue } else { context.useLabel(stmt.target); context.addStatement(new Continue(stmt.target.name)); } } @override void visitIf(tree.If stmt, BuilderContext context) { Expression condition = visitExpression(stmt.condition, context); Block thenBlock = visitInSubContext(stmt.thenStatement, context); Block elseBlock= visitInSubContext(stmt.elseStatement, context); context.addStatement(new If(condition, thenBlock, elseBlock)); } @override void visitWhileTrue(tree.WhileTrue stmt, BuilderContext context) { Block body = visitInSubContext(stmt.body, context, fallthrough: stmt); Statement statement = new While(new Literal(new TrueConstantValue()), body); addLabeledStatement(stmt.label, statement, context); } @override void visitWhileCondition(tree.WhileCondition stmt, BuilderContext context) { Expression condition = visitExpression(stmt.condition, context); Block body = visitInSubContext(stmt.body, context, fallthrough: stmt); Statement statement = new While(condition, body); addLabeledStatement(stmt.label, statement, context); visitStatement(stmt.next, context); } @override void visitTry(tree.Try stmt, BuilderContext context) { Block tryBody = visitInSubContext(stmt.tryBody, context); Block catchBody = visitInSubContext(stmt.catchBody, context); CatchBlock catchBlock; tree.Variable exceptionVariable = stmt.catchParameters[0]; context.handlerVariables.add(exceptionVariable); VariableDeclaration exceptionParameter = new VariableDeclaration(context.getVariableName(exceptionVariable)); exceptionParameter.element = exceptionVariable.element; if (stmt.catchParameters.length == 2) { tree.Variable stackTraceVariable = stmt.catchParameters[1]; context.handlerVariables.add(stackTraceVariable); VariableDeclaration stackTraceParameter = new VariableDeclaration(context.getVariableName(stackTraceVariable)); stackTraceParameter.element = stackTraceVariable.element; catchBlock = new CatchBlock(catchBody, exceptionVar: exceptionParameter, stackVar: stackTraceParameter); } else { assert(stmt.catchParameters.length == 1); catchBlock = new CatchBlock(catchBody, exceptionVar: exceptionParameter); } context.addStatement(new Try(tryBody, [catchBlock], null)); } @override Expression visitConstant(tree.Constant exp, BuilderContext context) { return ConstantEmitter.createExpression(exp.expression, context); } @override Expression visitThis(tree.This exp, BuilderContext context) { return new This(); } @override Expression visitReifyTypeVar(tree.ReifyTypeVar exp, BuilderContext context) { return new ReifyTypeVar(exp.typeVariable.name) ..element = exp.typeVariable; } List visitExpressions(List expressions, BuilderContext context) { return expressions.map((expression) => visitExpression(expression, context)) .toList(growable: false); } @override Expression visitLiteralList(tree.LiteralList exp, BuilderContext context) { return new LiteralList(visitExpressions(exp.values, context), typeArgument: TypeGenerator.createOptionalType(exp.type.typeArguments.single)); } @override Expression visitLiteralMap(tree.LiteralMap exp, BuilderContext context) { List entries = new List.generate( exp.entries.length, (i) => new LiteralMapEntry( visitExpression(exp.entries[i].key, context), visitExpression(exp.entries[i].value, context))); List typeArguments = exp.type.treatAsRaw ? null : exp.type.typeArguments.map(TypeGenerator.createType) .toList(growable: false); return new LiteralMap(entries, typeArguments: typeArguments); } @override Expression visitTypeOperator(tree.TypeOperator exp, BuilderContext context) { return new TypeOperator(visitExpression(exp.receiver, context), exp.operator, TypeGenerator.createType(exp.type)); } List emitArguments(List arguments, Selector selector) { int positionalArgumentCount = selector.positionalArgumentCount; List result = new List.generate(positionalArgumentCount, (i) => arguments[i]); for (int i = 0; i < selector.namedArgumentCount; ++i) { result.add(new NamedArgument(selector.namedArguments[i], arguments[positionalArgumentCount + i])); } return result; } List visitArgumentList(List arguments, BuilderContext context) { return arguments .map((tree.Expression argument) => visitExpression(argument, context)) .toList(); } @override Expression visitInvokeStatic(tree.InvokeStatic exp, BuilderContext context) { switch (exp.selector.kind) { case SelectorKind.GETTER: return new Identifier(exp.target.name)..element = exp.target; case SelectorKind.SETTER: return new Assignment( new Identifier(exp.target.name)..element = exp.target, '=', visitExpression(exp.arguments[0], context)); case SelectorKind.CALL: return new CallStatic( null, exp.target.name, emitArguments(visitArgumentList(exp.arguments, context), exp.selector)) ..element = exp.target; default: throw "Unexpected selector kind: ${exp.selector.kind}"; } } Expression emitMethodCall(tree.Invoke exp, Receiver receiver, BuilderContext context) { List args = emitArguments(visitArgumentList(exp.arguments, context), exp.selector); switch (exp.selector.kind) { case SelectorKind.CALL: if (exp.selector.name == "call") { return new CallFunction(receiver, args); } return new CallMethod(receiver, exp.selector.name, args); case SelectorKind.OPERATOR: if (args.length == 0) { String name = exp.selector.name; if (name == 'unary-') { name = '-'; } return new UnaryOperator(name, receiver); } return new BinaryOperator(receiver, exp.selector.name, args[0]); case SelectorKind.GETTER: return new FieldExpression(receiver, exp.selector.name); case SelectorKind.SETTER: return makeAssignment( new FieldExpression(receiver, exp.selector.name), args[0]); case SelectorKind.INDEX: Expression e = new IndexExpression(receiver, args[0]); if (args.length == 2) { e = makeAssignment(e, args[1]); } return e; default: throw "Unexpected selector in InvokeMethod: ${exp.selector.kind}"; } } @override Expression visitInvokeMethod(tree.InvokeMethod exp, BuilderContext context) { Expression receiver = visitExpression(exp.receiver, context); return emitMethodCall(exp, receiver, context); } @override Expression visitInvokeMethodDirectly(tree.InvokeMethodDirectly exp, BuilderContext context) { // When targeting Dart, InvokeMethodDirectly is only used for super calls. // The receiver is known to be `this`, and the target method is a method // on the super class. So we just translate it as a method call with the // super receiver. return emitMethodCall(exp, new SuperReceiver(), context); } @override Expression visitInvokeConstructor(tree.InvokeConstructor exp, BuilderContext context) { List args = emitArguments(visitArgumentList(exp.arguments, context), exp.selector); FunctionElement constructor = exp.target; String name = constructor.name.isEmpty ? null : constructor.name; return new CallNew(TypeGenerator.createType(exp.type), args, constructorName: name, isConst: exp.constant != null) ..constructor = constructor ..dartType = exp.type; } @override Expression visitConcatenateStrings(tree.ConcatenateStrings exp, BuilderContext context) { return new StringConcat(visitExpressions(exp.arguments, context)); } @override Expression visitConditional(tree.Conditional exp, BuilderContext context) { return new Conditional( visitExpression(exp.condition, context), visitExpression(exp.thenExpression, context), visitExpression(exp.elseExpression, context)); } @override Expression visitLogicalOperator(tree.LogicalOperator exp, BuilderContext context) { return new BinaryOperator(visitExpression(exp.left, context), exp.operator, visitExpression(exp.right, context)); } @override Expression visitNot(tree.Not exp, BuilderContext context) { return new UnaryOperator('!', visitExpression(exp.operand, context)); } @override Expression visitVariableUse(tree.VariableUse exp, BuilderContext context) { return context.makeVariableAccess(exp.variable); } FunctionExpression makeSubFunction(tree.FunctionDefinition function, BuilderContext context) { return visitFunctionDefinition(function, new BuilderContext.inner(context)); } @override Expression visitFunctionExpression(tree.FunctionExpression exp, BuilderContext context) { return makeSubFunction(exp.definition, context)..name = null; } @override void visitFunctionDeclaration(tree.FunctionDeclaration node, BuilderContext context) { assert(context.variableNames[node.variable] == null); String name = context.getVariableName(node.variable); FunctionExpression inner = makeSubFunction(node.definition, context); inner.name = name; FunctionDeclaration decl = new FunctionDeclaration(inner); context.declaredVariables.add(node.variable); context.addStatement(decl); visitStatement(node.next, context); } List buildInInitializerContext(tree.Statement root, BuilderContext context) { BuilderContext inner = new BuilderContext.initializer(context); inner.currentElement = context.currentElement; visitStatement(root, inner); List bodyParts; for (tree.Variable variable in inner.variableNames.keys) { if (!context.declaredVariables.contains(variable)) { inner.addDeclaration(variable); } } if (inner.variables.length > 0) { bodyParts = new List(); bodyParts.add(new VariableDeclarations(inner.variables)); bodyParts.addAll(inner.statements); } else { bodyParts = inner.statements; } return bodyParts; } @override Initializer visitFieldInitializer(tree.FieldInitializer node, BuilderContext context) { return new FieldInitializer(node.element, ensureExpression(buildInInitializerContext(node.body, context))); } @override Initializer visitSuperInitializer(tree.SuperInitializer node, BuilderContext context) { List arguments = node.arguments.map((tree.Statement argument) { return ensureExpression(buildInInitializerContext(argument, context)); }).toList(); return new SuperInitializer(node.target, emitArguments(arguments, node.selector)); } @override Expression visitTypeExpression(tree.TypeExpression node, arg) { throw '$node not supported by dart backend'; } @override visitGetField(tree.GetField node, arg) => errorUnsupportedNode(node); @override visitSetField(tree.SetField node, arg) => errorUnsupportedNode(node); @override visitCreateBox(tree.CreateBox node, arg) => errorUnsupportedNode(node); @override visitCreateInstance(tree.CreateInstance node, arg) { return errorUnsupportedNode(node); } @override Expression visitReadTypeVariable(tree.ReadTypeVariable node, arg) { return errorUnsupportedNode(node); } @override Expression visitReifyRuntimeType(tree.ReifyRuntimeType node, arg) { return errorUnsupportedNode(node); } errorUnsupportedNode(tree.JsSpecificNode node) { throw '$node not supported by dart backend'; } } class TypeGenerator { /// TODO(johnniwinther): Remove this when issue 21283 has been resolved. static int pseudoNameCounter = 0; static Parameter emitParameter(DartType type, BuilderContext context, {String name, Element element, ConstantExpression defaultValue}) { if (name == null && element != null) { name = element.name; } if (name == null) { name = '_${pseudoNameCounter++}'; } Parameter parameter; if (type.isFunctionType) { FunctionType functionType = type; TypeAnnotation returnType = createOptionalType(functionType.returnType); Parameters innerParameters = createParametersFromType(functionType); parameter = new Parameter.function(name, returnType, innerParameters); } else { TypeAnnotation typeAnnotation = createOptionalType(type); parameter = new Parameter(name, type: typeAnnotation); } parameter.element = element; if (defaultValue != null && !defaultValue.value.isNull) { parameter.defaultValue = ConstantEmitter.createExpression(defaultValue, context); } return parameter; } static Parameters createParametersFromType(FunctionType functionType) { pseudoNameCounter = 0; if (functionType.namedParameters.isEmpty) { return new Parameters( createParameters(functionType.parameterTypes), createParameters(functionType.optionalParameterTypes), false); } else { return new Parameters( createParameters(functionType.parameterTypes), createParameters(functionType.namedParameterTypes, names: functionType.namedParameters), true); } } static List createParameters( Iterable parameterTypes, {BuilderContext context, Iterable names: const [], Iterable defaultValues: const [], Iterable elements: const []}) { Iterator name = names.iterator; Iterator defaultValue = defaultValues.iterator; Iterator element = elements.iterator; return parameterTypes.map((DartType type) { name.moveNext(); defaultValue.moveNext(); element.moveNext(); return emitParameter(type, context, name: name.current, defaultValue: defaultValue.current, element: element.current); }).toList(); } /// Like [createTypeAnnotation] except the dynamic type is converted to null. static TypeAnnotation createOptionalType(DartType type) { if (type.treatAsDynamic) { return null; } else { return createType(type); } } /// Creates the [TypeAnnotation] for a [type] that is not function type. static TypeAnnotation createType(DartType type) { if (type is GenericType) { if (type.treatAsRaw) { return new TypeAnnotation(type.element.name)..dartType = type; } return new TypeAnnotation( type.element.name, type.typeArguments.map(createType).toList(growable:false)) ..dartType = type; } else if (type is VoidType) { return new TypeAnnotation('void') ..dartType = type; } else if (type is TypeVariableType) { return new TypeAnnotation(type.name) ..dartType = type; } else if (type is DynamicType) { return new TypeAnnotation("dynamic") ..dartType = type; } else if (type is MalformedType) { return new TypeAnnotation(type.name) ..dartType = type; } else { throw "Unsupported type annotation: $type"; } } } class ConstantEmitter extends ConstantExpressionVisitor, Expression> { const ConstantEmitter(); /// Creates the [Expression] for the constant [exp]. static Expression createExpression(ConstantExpression exp, BuilderContext context) { return const ConstantEmitter().visit(exp, context); } Expression handlePrimitiveConstant(PrimitiveConstantValue value) { // Num constants may be negative, while literals must be non-negative: // Literals are non-negative in the specification, and a negated literal // parses as a call to unary `-`. The AST unparser assumes literals are // non-negative and relies on this to avoid incorrectly generating `--`, // the predecrement operator. // Translate such constants into their positive value wrapped by // the unary minus operator. if (value.isNum) { NumConstantValue numConstant = value; if (numConstant.primitiveValue.isNegative) { return negatedLiteral(numConstant); } } return new Literal(value); } List visitExpressions(List expressions, BuilderContext context) { return expressions.map((expression) => visit(expression, context)) .toList(growable: false); } @override Expression visitPrimitive(PrimitiveConstantExpression exp, BuilderContext context) { return handlePrimitiveConstant(exp.value); } /// Given a negative num constant, returns the corresponding positive /// literal wrapped by a unary minus operator. Expression negatedLiteral(NumConstantValue constant) { assert(constant.primitiveValue.isNegative); NumConstantValue positiveConstant; if (constant.isInt) { positiveConstant = new IntConstantValue(-constant.primitiveValue); } else if (constant.isDouble) { positiveConstant = new DoubleConstantValue(-constant.primitiveValue); } else { throw "Unexpected type of NumConstant: $constant"; } return new UnaryOperator('-', new Literal(positiveConstant)); } @override Expression visitList(ListConstantExpression exp, BuilderContext context) { return new LiteralList( visitExpressions(exp.values, context), isConst: true, typeArgument: TypeGenerator.createOptionalType(exp.type.typeArguments.single)); } @override Expression visitMap(MapConstantExpression exp, BuilderContext context) { List entries = new List.generate( exp.values.length, (i) => new LiteralMapEntry(visit(exp.keys[i], context), visit(exp.values[i], context))); List typeArguments = exp.type.treatAsRaw ? null : exp.type.typeArguments.map(TypeGenerator.createType).toList(); return new LiteralMap(entries, isConst: true, typeArguments: typeArguments); } @override Expression visitConstructed(ConstructedConstantExpression exp, BuilderContext context) { int positionalArgumentCount = exp.callStructure.positionalArgumentCount; List args = new List.generate( positionalArgumentCount, (i) => visit(exp.arguments[i], context)); for (int i = 0; i < exp.callStructure.namedArgumentCount; ++i) { args.add(new NamedArgument(exp.callStructure.namedArguments[i], visit(exp.arguments[positionalArgumentCount + i], context))); } FunctionElement constructor = exp.target; String name = constructor.name.isEmpty ? null : constructor.name; return new CallNew(TypeGenerator.createType(exp.type), args, constructorName: name, isConst: true) ..constructor = constructor ..dartType = exp.type; } @override Expression visitConcatenate(ConcatenateConstantExpression exp, BuilderContext context) { return new StringConcat(visitExpressions(exp.arguments, context)); } @override Expression visitSymbol(SymbolConstantExpression exp, BuilderContext context) { return new LiteralSymbol(exp.name); } @override Expression visitType(TypeConstantExpression exp, BuilderContext context) { DartType type = exp.type; return new LiteralType(type.name) ..type = type; } @override Expression visitVariable(VariableConstantExpression exp, BuilderContext context) { Element element = exp.element; if (element.kind != ElementKind.VARIABLE) { return new Identifier(element.name)..element = element; } String name = context.getConstantName(element); return new Identifier(name) ..element = element; } @override Expression visitFunction(FunctionConstantExpression exp, BuilderContext context) { return new Identifier(exp.element.name) ..element = exp.element; } @override Expression visitBinary(BinaryConstantExpression exp, BuilderContext context) { return handlePrimitiveConstant(exp.value); } @override Expression visitConditional(ConditionalConstantExpression exp, BuilderContext context) { if (exp.condition.value.isTrue) { return exp.trueExp.accept(this); } else { return exp.falseExp.accept(this); } } @override Expression visitUnary(UnaryConstantExpression exp, BuilderContext context) { return handlePrimitiveConstant(exp.value); } } /// Moves function parameters into a separate variable if one of its uses is /// shadowed by an inner function parameter. /// This artifact is necessary because function parameters cannot be renamed. class UnshadowParameters extends tree.RecursiveVisitor { /// Maps parameter names to their bindings. Map environment = {}; /// Parameters that are currently shadowed by another parameter. Set shadowedParameters = new Set(); /// Parameters that are used in a context where it is shadowed. Set hasShadowedUse = new Set(); void unshadow(tree.RootNode definition) { if (definition.isEmpty) return; unshadowFunction(definition); } void unshadowFunction(tree.RootNode definition) { var oldShadow = shadowedParameters; var oldEnvironment = environment; environment = new Map.from(environment); shadowedParameters = new Set.from(shadowedParameters); for (tree.Variable param in definition.parameters) { tree.Variable oldVariable = environment[param.element.name]; if (oldVariable != null) { shadowedParameters.add(oldVariable); } environment[param.element.name] = param; } definition.forEachBody(visitStatement); environment = oldEnvironment; shadowedParameters = oldShadow; for (int i=0; i enclosingElement; ExecutableElement get memberContext => executableContext.memberContext; bool get isLocal => true; LibraryElement get implementationLibrary => enclosingElement.library; }