blob: 7ceefa66b585b348a223d7065953f67102d0efb0 [file] [log] [blame]
// 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<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 visitConstructor(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;
}