| // 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_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. |
| Expression emit(tree.FunctionDefinition definition) { |
| return new ASTEmitter().emit(definition); |
| } |
| |
| /// 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.Visitor<dynamic, Expression> { |
| /// Variables to be hoisted at the top of the current function. |
| List<VariableDeclaration> variables = <VariableDeclaration>[]; |
| |
| /// Maps variables to their name. |
| Map<tree.Variable, String> variableNames = <tree.Variable, String>{}; |
| |
| /// Maps local constants to their name. |
| Map<VariableElement, String> constantNames = <VariableElement, String>{}; |
| |
| /// Variables that have had their declaration created. |
| Set<tree.Variable> declaredVariables = new Set<tree.Variable>(); |
| |
| /// Variable names that have already been used. Used to avoid name clashes. |
| Set<String> usedVariableNames; |
| |
| /// Statements emitted by the most recent call to [visitStatement]. |
| List<Statement> statementBuffer = <Statement>[]; |
| |
| /// The function currently being emitted. |
| FunctionElement functionElement; |
| |
| /// Bookkeeping object needed to synthesize a variable declaration. |
| 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. |
| Set<tree.Label> usedLabels = new Set<tree.Label>(); |
| |
| /// The first dart_tree statement that is not converted to a variable |
| /// initializer. |
| tree.Statement firstStatement; |
| |
| /// Emitter for the enclosing function, or null if the current function is |
| /// not a local function. |
| ASTEmitter parent; |
| |
| ASTEmitter() : usedVariableNames = new Set<String>(); |
| |
| ASTEmitter.inner(ASTEmitter parent) |
| : this.parent = parent, |
| usedVariableNames = parent.usedVariableNames; |
| |
| FunctionExpression emit(tree.FunctionDefinition definition) { |
| functionElement = definition.element; |
| |
| Parameters parameters = emitRootParameters(definition); |
| |
| // Declare parameters. |
| for (tree.Variable param in definition.parameters) { |
| variableNames[param] = param.element.name; |
| usedVariableNames.add(param.element.name); |
| declaredVariables.add(param); |
| } |
| |
| Statement body; |
| if (definition.isAbstract) { |
| body = new EmptyStatement(); |
| } else { |
| firstStatement = definition.body; |
| visitStatement(definition.body); |
| removeTrailingReturn(); |
| |
| // 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 variableNames.keys) { |
| if (!declaredVariables.contains(variable)) { |
| addDeclaration(variable); |
| } |
| } |
| |
| // Add constant declarations. |
| List<VariableDeclaration> constants = <VariableDeclaration>[]; |
| for (ConstDeclaration constDecl in definition.localConstants) { |
| if (!constantNames.containsKey(constDecl.element)) |
| continue; // Discard unused constants declarations. |
| String name = getConstantName(constDecl.element); |
| Expression value = emitConstant(constDecl.expression); |
| VariableDeclaration decl = new VariableDeclaration(name, value); |
| decl.element = constDecl.element; |
| constants.add(decl); |
| } |
| |
| List<Statement> bodyParts = []; |
| if (constants.length > 0) { |
| bodyParts.add(new VariableDeclarations(constants, isConst: true)); |
| } |
| if (variables.length > 0) { |
| bodyParts.add(new VariableDeclarations(variables)); |
| } |
| bodyParts.addAll(statementBuffer); |
| |
| body = new Block(bodyParts); |
| } |
| FunctionType functionType = functionElement.type; |
| |
| return new FunctionExpression( |
| parameters, |
| body, |
| name: functionElement.name, |
| returnType: emitOptionalType(functionType.returnType), |
| isGetter: functionElement.isGetter, |
| isSetter: functionElement.isSetter) |
| ..element = functionElement; |
| } |
| |
| 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); |
| } |
| |
| /// Removes a trailing "return null" from [statementBuffer]. |
| void removeTrailingReturn() { |
| if (statementBuffer.isEmpty) return; |
| if (statementBuffer.last is! Return) return; |
| Return ret = statementBuffer.last; |
| Expression expr = ret.expression; |
| if (expr is Literal && expr.value.isNull) { |
| statementBuffer.removeLast(); |
| } |
| } |
| |
| /// TODO(johnniwinther): Remove this when issue 21283 has been resolved. |
| int pseudoNameCounter = 0; |
| |
| Parameter emitParameter(DartType type, |
| {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 = emitOptionalType(functionType.returnType); |
| Parameters innerParameters = emitParametersFromType(functionType); |
| parameter = new Parameter.function(name, returnType, innerParameters); |
| } else { |
| TypeAnnotation typeAnnotation = emitOptionalType(type); |
| parameter = new Parameter(name, type: typeAnnotation); |
| } |
| parameter.element = element; |
| if (defaultValue != null && !defaultValue.value.isNull) { |
| parameter.defaultValue = emitConstant(defaultValue); |
| } |
| return parameter; |
| } |
| |
| Parameters emitParametersFromType(FunctionType functionType) { |
| if (functionType.namedParameters.isEmpty) { |
| return new Parameters( |
| emitParameters(functionType.parameterTypes), |
| emitParameters(functionType.optionalParameterTypes), |
| false); |
| } else { |
| return new Parameters( |
| emitParameters(functionType.parameterTypes), |
| emitParameters(functionType.namedParameterTypes, |
| names: functionType.namedParameters), |
| true); |
| } |
| } |
| |
| List<Parameter> emitParameters( |
| Iterable<DartType> parameterTypes, |
| {Iterable<String> names: const <String>[], |
| Iterable<ConstantExpression> defaultValues: const <ConstantExpression>[], |
| Iterable<Element> elements: const <Element>[]}) { |
| Iterator<String> name = names.iterator; |
| Iterator<ConstantExpression> defaultValue = defaultValues.iterator; |
| Iterator<Element> element = elements.iterator; |
| return parameterTypes.map((DartType type) { |
| name.moveNext(); |
| defaultValue.moveNext(); |
| element.moveNext(); |
| return emitParameter(type, |
| name: name.current, |
| defaultValue: defaultValue.current, |
| element: element.current); |
| }).toList(); |
| } |
| |
| /// Emits parameters that are not nested inside other parameters. |
| /// Root parameters can have default values, while inner parameters cannot. |
| Parameters emitRootParameters(tree.FunctionDefinition function) { |
| FunctionType functionType = function.element.type; |
| List<Parameter> required = emitParameters( |
| functionType.parameterTypes, |
| elements: function.parameters.map((p) => p.element)); |
| bool optionalParametersAreNamed = !functionType.namedParameters.isEmpty; |
| List<Parameter> optional = emitParameters( |
| optionalParametersAreNamed |
| ? functionType.namedParameterTypes |
| : functionType.optionalParameterTypes, |
| defaultValues: function.defaultParameterValues, |
| 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)) { |
| Expression 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); |
| } |
| |
| void visitExpressionStatement(tree.ExpressionStatement stmt) { |
| Expression e = visitExpression(stmt.expression); |
| statementBuffer.add(new ExpressionStatement(e)); |
| visitStatement(stmt.next); |
| } |
| |
| void visitLabeledStatement(tree.LabeledStatement stmt) { |
| List<Statement> savedBuffer = statementBuffer; |
| tree.Statement savedFallthrough = fallthrough; |
| statementBuffer = <Statement>[]; |
| fallthrough = stmt.next; |
| visitStatement(stmt.body); |
| if (usedLabels.remove(stmt.label)) { |
| savedBuffer.add(new LabeledStatement(stmt.label.name, |
| new Block(statementBuffer))); |
| } else { |
| savedBuffer.add(new Block(statementBuffer)); |
| } |
| fallthrough = savedFallthrough; |
| statementBuffer = savedBuffer; |
| visitStatement(stmt.next); |
| } |
| |
| /// 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 (variable.host.element != functionElement) { |
| 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, |
| functionElement, |
| variableList); |
| } |
| return name; |
| } |
| |
| String getConstantName(VariableElement element) { |
| assert(element.kind == ElementKind.VARIABLE); |
| if (element.enclosingElement != functionElement) { |
| 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; |
| } |
| |
| bool isNullLiteral(Expression exp) => exp is Literal && exp.value.isNull; |
| |
| void visitAssign(tree.Assign stmt) { |
| // 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.definition is tree.FunctionExpression && |
| !declaredVariables.contains(stmt.variable)) { |
| tree.FunctionExpression functionExp = stmt.definition; |
| FunctionExpression function = makeSubFunction(functionExp.definition); |
| FunctionDeclaration decl = new FunctionDeclaration(function); |
| statementBuffer.add(decl); |
| declaredVariables.add(stmt.variable); |
| visitStatement(stmt.next); |
| return; |
| } |
| |
| bool isFirstOccurrence = (variableNames[stmt.variable] == null); |
| bool isDeclaredHere = stmt.variable.host.element == functionElement; |
| String name = getVariableName(stmt.variable); |
| Expression definition = visitExpression(stmt.definition); |
| |
| // Try to pull into initializer. |
| if (firstStatement == stmt && isFirstOccurrence && isDeclaredHere) { |
| if (isNullLiteral(definition)) definition = null; |
| addDeclaration(stmt.variable, definition); |
| firstStatement = stmt.next; |
| visitStatement(stmt.next); |
| return; |
| } |
| |
| // Emit a variable declaration if we are required to do so. |
| // This is to ensure that a fresh closure 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; |
| declaredVariables.add(stmt.variable); |
| statementBuffer.add(new VariableDeclarations([decl])); |
| visitStatement(stmt.next); |
| return; |
| } |
| |
| statementBuffer.add(new ExpressionStatement(makeAssignment( |
| visitVariable(stmt.variable), |
| definition))); |
| visitStatement(stmt.next); |
| } |
| |
| void visitReturn(tree.Return stmt) { |
| Expression inner = visitExpression(stmt.value); |
| statementBuffer.add(new Return(inner)); |
| } |
| |
| void visitBreak(tree.Break stmt) { |
| tree.Statement fall = 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 { |
| usedLabels.add(stmt.target); |
| statementBuffer.add(new Break(stmt.target.name)); |
| } |
| } |
| |
| void visitContinue(tree.Continue stmt) { |
| tree.Statement fall = 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 { |
| usedLabels.add(stmt.target); |
| statementBuffer.add(new Continue(stmt.target.name)); |
| } |
| } |
| |
| void visitIf(tree.If stmt) { |
| Expression condition = visitExpression(stmt.condition); |
| List<Statement> savedBuffer = statementBuffer; |
| List<Statement> thenBuffer = statementBuffer = <Statement>[]; |
| visitStatement(stmt.thenStatement); |
| List<Statement> elseBuffer = statementBuffer = <Statement>[]; |
| visitStatement(stmt.elseStatement); |
| savedBuffer.add( |
| new If(condition, new Block(thenBuffer), new Block(elseBuffer))); |
| statementBuffer = savedBuffer; |
| } |
| |
| void visitWhileTrue(tree.WhileTrue stmt) { |
| List<Statement> savedBuffer = statementBuffer; |
| tree.Statement savedFallthrough = fallthrough; |
| statementBuffer = <Statement>[]; |
| fallthrough = stmt; |
| |
| visitStatement(stmt.body); |
| Statement body = new Block(statementBuffer); |
| Statement statement = new While(new Literal(new TrueConstantValue()), |
| body); |
| if (usedLabels.remove(stmt.label)) { |
| statement = new LabeledStatement(stmt.label.name, statement); |
| } |
| savedBuffer.add(statement); |
| |
| statementBuffer = savedBuffer; |
| fallthrough = savedFallthrough; |
| } |
| |
| void visitWhileCondition(tree.WhileCondition stmt) { |
| Expression condition = visitExpression(stmt.condition); |
| |
| List<Statement> savedBuffer = statementBuffer; |
| tree.Statement savedFallthrough = fallthrough; |
| statementBuffer = <Statement>[]; |
| fallthrough = stmt; |
| |
| visitStatement(stmt.body); |
| Statement body = new Block(statementBuffer); |
| Statement statement; |
| statement = new While(condition, body); |
| if (usedLabels.remove(stmt.label)) { |
| statement = new LabeledStatement(stmt.label.name, statement); |
| } |
| savedBuffer.add(statement); |
| |
| statementBuffer = savedBuffer; |
| fallthrough = savedFallthrough; |
| |
| visitStatement(stmt.next); |
| } |
| |
| Expression visitConstant(tree.Constant exp) { |
| return emitConstant(exp.expression); |
| } |
| |
| Expression visitThis(tree.This exp) { |
| return new This(); |
| } |
| |
| Expression visitReifyTypeVar(tree.ReifyTypeVar exp) { |
| return new ReifyTypeVar(exp.typeVariable.name) |
| ..element = exp.typeVariable; |
| } |
| |
| Expression visitLiteralList(tree.LiteralList exp) { |
| return new LiteralList( |
| exp.values.map(visitExpression).toList(growable: false), |
| typeArgument: emitOptionalType(exp.type.typeArguments.single)); |
| } |
| |
| Expression visitLiteralMap(tree.LiteralMap exp) { |
| List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate( |
| exp.entries.length, |
| (i) => new LiteralMapEntry(visitExpression(exp.entries[i].key), |
| visitExpression(exp.entries[i].value))); |
| List<TypeAnnotation> typeArguments = exp.type.treatAsRaw |
| ? null |
| : exp.type.typeArguments.map(createTypeAnnotation) |
| .toList(growable: false); |
| return new LiteralMap(entries, typeArguments: typeArguments); |
| } |
| |
| Expression visitTypeOperator(tree.TypeOperator exp) { |
| return new TypeOperator(visitExpression(exp.receiver), |
| exp.operator, |
| createTypeAnnotation(exp.type)); |
| } |
| |
| List<Argument> emitArguments(tree.Invoke exp) { |
| List<tree.Expression> args = exp.arguments; |
| int positionalArgumentCount = exp.selector.positionalArgumentCount; |
| List<Argument> result = new List<Argument>.generate(positionalArgumentCount, |
| (i) => visitExpression(exp.arguments[i])); |
| for (int i = 0; i < exp.selector.namedArgumentCount; ++i) { |
| result.add(new NamedArgument(exp.selector.namedArguments[i], |
| visitExpression(exp.arguments[positionalArgumentCount + i]))); |
| } |
| return result; |
| } |
| |
| Expression visitInvokeStatic(tree.InvokeStatic exp) { |
| 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])); |
| |
| case SelectorKind.CALL: |
| return new CallStatic(null, exp.target.name, emitArguments(exp)) |
| ..element = exp.target; |
| |
| default: |
| throw "Unexpected selector kind: ${exp.selector.kind}"; |
| } |
| } |
| |
| Expression emitMethodCall(tree.Invoke exp, Receiver receiver) { |
| List<Argument> args = emitArguments(exp); |
| 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}"; |
| } |
| } |
| |
| Expression visitInvokeMethod(tree.InvokeMethod exp) { |
| Expression receiver = visitExpression(exp.receiver); |
| return emitMethodCall(exp, receiver); |
| } |
| |
| Expression visitInvokeSuperMethod(tree.InvokeSuperMethod exp) { |
| return emitMethodCall(exp, new SuperReceiver()); |
| } |
| |
| Expression visitInvokeConstructor(tree.InvokeConstructor exp) { |
| List args = emitArguments(exp); |
| FunctionElement constructor = exp.target; |
| String name = constructor.name.isEmpty ? null : constructor.name; |
| return new CallNew(createTypeAnnotation(exp.type), |
| args, |
| constructorName: name, |
| isConst: exp.constant != null) |
| ..constructor = constructor |
| ..dartType = exp.type; |
| } |
| |
| Expression visitConcatenateStrings(tree.ConcatenateStrings exp) { |
| List args = exp.arguments.map(visitExpression).toList(growable:false); |
| return new StringConcat(args); |
| } |
| |
| Expression visitConditional(tree.Conditional exp) { |
| return new Conditional( |
| visitExpression(exp.condition), |
| visitExpression(exp.thenExpression), |
| visitExpression(exp.elseExpression)); |
| } |
| |
| Expression visitLogicalOperator(tree.LogicalOperator exp) { |
| return new BinaryOperator(visitExpression(exp.left), |
| exp.operator, |
| visitExpression(exp.right)); |
| } |
| |
| Expression visitNot(tree.Not exp) { |
| return new UnaryOperator('!', visitExpression(exp.operand)); |
| } |
| |
| Expression visitVariable(tree.Variable exp) { |
| return new Identifier(getVariableName(exp)) |
| ..element = exp.element; |
| } |
| |
| FunctionExpression makeSubFunction(tree.FunctionDefinition function) { |
| return new ASTEmitter.inner(this).emit(function); |
| } |
| |
| Expression visitFunctionExpression(tree.FunctionExpression exp) { |
| return makeSubFunction(exp.definition)..name = null; |
| } |
| |
| void visitFunctionDeclaration(tree.FunctionDeclaration node) { |
| assert(variableNames[node.variable] == null); |
| String name = getVariableName(node.variable); |
| FunctionExpression inner = makeSubFunction(node.definition); |
| inner.name = name; |
| FunctionDeclaration decl = new FunctionDeclaration(inner); |
| declaredVariables.add(node.variable); |
| statementBuffer.add(decl); |
| visitStatement(node.next); |
| } |
| |
| /// Like [createTypeAnnotation] except the dynamic type is converted to null. |
| TypeAnnotation emitOptionalType(DartType type) { |
| if (type.treatAsDynamic) { |
| return null; |
| } else { |
| return createTypeAnnotation(type); |
| } |
| } |
| |
| Expression emitConstant(ConstantExpression exp) { |
| return new ConstantEmitter(this).visit(exp); |
| } |
| } |
| |
| TypeAnnotation createTypeAnnotation(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(createTypeAnnotation).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<Null, Expression> { |
| ASTEmitter parent; |
| ConstantEmitter(this.parent); |
| |
| 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); |
| } |
| |
| @override |
| Expression visitPrimitive(PrimitiveConstantExpression exp, [_]) { |
| 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, [_]) { |
| return new LiteralList( |
| exp.values.map(visit).toList(growable: false), |
| isConst: true, |
| typeArgument: parent.emitOptionalType(exp.type.typeArguments.single)); |
| } |
| |
| @override |
| Expression visitMap(MapConstantExpression exp, [_]) { |
| List<LiteralMapEntry> entries = new List<LiteralMapEntry>.generate( |
| exp.values.length, |
| (i) => new LiteralMapEntry(visit(exp.keys[i]), |
| visit(exp.values[i]))); |
| List<TypeAnnotation> typeArguments = exp.type.treatAsRaw |
| ? null |
| : exp.type.typeArguments.map(createTypeAnnotation).toList(); |
| return new LiteralMap(entries, isConst: true, typeArguments: typeArguments); |
| } |
| |
| @override |
| Expression visitConstructed(ConstructedConstantExpresssion exp, [_]) { |
| int positionalArgumentCount = exp.selector.positionalArgumentCount; |
| List<Argument> args = new List<Argument>.generate( |
| positionalArgumentCount, |
| (i) => visit(exp.arguments[i])); |
| for (int i = 0; i < exp.selector.namedArgumentCount; ++i) { |
| args.add(new NamedArgument(exp.selector.namedArguments[i], |
| visit(exp.arguments[positionalArgumentCount + i]))); |
| } |
| |
| FunctionElement constructor = exp.target; |
| String name = constructor.name.isEmpty ? null : constructor.name; |
| return new CallNew(createTypeAnnotation(exp.type), |
| args, |
| constructorName: name, |
| isConst: true) |
| ..constructor = constructor |
| ..dartType = exp.type; |
| } |
| |
| @override |
| Expression visitConcatenate(ConcatenateConstantExpression exp, [_]) { |
| return new StringConcat(exp.arguments.map(visit).toList(growable: false)); |
| } |
| |
| @override |
| Expression visitSymbol(SymbolConstantExpression exp, [_]) { |
| return new LiteralSymbol(exp.name); |
| } |
| |
| @override |
| Expression visitType(TypeConstantExpression exp, [_]) { |
| DartType type = exp.type; |
| return new LiteralType(type.name) |
| ..type = type; |
| } |
| |
| @override |
| Expression visitVariable(VariableConstantExpression exp, [_]) { |
| Element element = exp.element; |
| if (element.kind != ElementKind.VARIABLE) { |
| return new Identifier(element.name)..element = element; |
| } |
| String name = parent.getConstantName(element); |
| return new Identifier(name) |
| ..element = element; |
| } |
| |
| @override |
| Expression visitFunction(FunctionConstantExpression exp, [_]) { |
| return new Identifier(exp.element.name) |
| ..element = exp.element; |
| } |
| |
| @override |
| Expression visitBinary(BinaryConstantExpression exp, [_]) { |
| return handlePrimitiveConstant(exp.value); |
| } |
| |
| @override |
| Expression visitConditional(ConditionalConstantExpression exp, [_]) { |
| if (exp.condition.value.isTrue) { |
| return exp.trueExp.accept(this); |
| } else { |
| return exp.falseExp.accept(this); |
| } |
| } |
| |
| @override |
| Expression visitUnary(UnaryConstantExpression exp, [_]) { |
| 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<String, tree.Variable> environment = <String, tree.Variable>{}; |
| |
| /// Parameters that are currently shadowed by another parameter. |
| Set<tree.Variable> shadowedParameters = new Set<tree.Variable>(); |
| |
| /// Parameters that are used in a context where it is shadowed. |
| Set<tree.Variable> hasShadowedUse = new Set<tree.Variable>(); |
| |
| void unshadow(tree.FunctionDefinition definition) { |
| if (definition.isAbstract) return; |
| |
| visitFunctionDefinition(definition); |
| } |
| |
| visitFunctionDefinition(tree.FunctionDefinition definition) { |
| var oldShadow = shadowedParameters; |
| var oldEnvironment = environment; |
| environment = new Map<String, tree.Variable>.from(environment); |
| shadowedParameters = new Set<tree.Variable>.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; |
| } |
| visitStatement(definition.body); |
| environment = oldEnvironment; |
| shadowedParameters = oldShadow; |
| |
| for (int i=0; i<definition.parameters.length; i++) { |
| tree.Variable param = definition.parameters[i]; |
| if (hasShadowedUse.remove(param)) { |
| tree.Variable newParam = new tree.Variable(definition, param.element); |
| definition.parameters[i] = newParam; |
| definition.body = new tree.Assign(param, newParam, definition.body); |
| newParam.writeCount = 1; // Being a parameter counts as a write. |
| } |
| } |
| } |
| |
| visitVariable(tree.Variable variable) { |
| if (shadowedParameters.contains(variable)) { |
| hasShadowedUse.add(variable); |
| } |
| } |
| |
| } |
| |
| // TODO(johnniwinther): Remove this when the dart `backend_ast` does not need |
| // [Element] for entities. |
| class _SyntheticLocalVariableElement extends modelx.VariableElementX |
| implements LocalVariableElement { |
| |
| _SyntheticLocalVariableElement(String name, |
| ExecutableElement enclosingElement, |
| modelx.VariableList variables) |
| : super(name, ElementKind.VARIABLE, enclosingElement, variables, null); |
| |
| ExecutableElement get executableContext => enclosingElement; |
| |
| ExecutableElement get memberContext => executableContext.memberContext; |
| |
| bool get isLocal => true; |
| |
| LibraryElement get implementationLibrary => enclosingElement.library; |
| } |