Index: sdk/lib/_internal/compiler/implementation/dart_backend/dart_codegen.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/dart_codegen.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/dart_codegen.dart |
index f3177aedc6503c335c13bd0eb25aefccaff071aa..848b8bff15878a4b22df087947270d2048fae1fa 100644 |
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/dart_codegen.dart |
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/dart_codegen.dart |
@@ -20,7 +20,7 @@ import '../ir/const_expression.dart'; |
frontend.FunctionExpression emit(FunctionElement element, |
dart2js.TreeElementMapping treeElements, |
tree.FunctionDefinition definition) { |
- FunctionExpression fn = new ASTEmitter().emit(element, definition); |
+ FunctionExpression fn = new ASTEmitter().emit(definition); |
return new TreePrinter(treeElements).makeExpression(fn); |
} |
@@ -37,12 +37,11 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
/// Maps local constants to their name. |
Map<VariableElement, String> constantNames = <VariableElement, String>{}; |
- /// Maps variables to their declarations. |
- Map<tree.Variable, VariableDeclaration> variableDeclarations = |
- <tree.Variable, VariableDeclaration>{}; |
+ /// 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 = new Set<String>(); |
+ Set<String> usedVariableNames; |
/// Statements emitted by the most recent call to [visitStatement]. |
List<Statement> statementBuffer = <Statement>[]; |
@@ -66,23 +65,39 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
/// initializer. |
tree.Statement firstStatement; |
- FunctionExpression emit(FunctionElement element, |
- tree.FunctionDefinition definition) { |
- functionElement = element; |
+ /// 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 = emitParameters(definition.element.functionSignature); |
+ |
+ // Declare parameters. |
+ for (tree.Variable param in definition.parameters) { |
+ variableNames[param] = param.element.name; |
+ usedVariableNames.add(param.element.name); |
+ declaredVariables.add(param); |
+ } |
- Parameters parameters = emitParameters(definition.parameters); |
firstStatement = definition.body; |
visitStatement(definition.body); |
removeTrailingReturn(); |
- Statement body = new Block(statementBuffer); |
// 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 (variable.element is ParameterElement) continue; |
- if (variableDeclarations.containsKey(variable)) continue; |
- addDeclaration(variable); |
+ if (!declaredVariables.contains(variable)) { |
+ addDeclaration(variable); |
+ } |
} |
// Add constant declarations. |
@@ -104,24 +119,24 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
if (variables.length > 0) { |
bodyParts.add(new VariableDeclarations(variables)); |
} |
- bodyParts.add(body); |
+ bodyParts.addAll(statementBuffer); |
- FunctionType functionType = element.type; |
+ FunctionType functionType = functionElement.type; |
return new FunctionExpression( |
parameters, |
new Block(bodyParts), |
- name: element.name, |
+ name: functionElement.name, |
returnType: emitOptionalType(functionType.returnType)) |
- ..element = element; |
+ ..element = functionElement; |
} |
void addDeclaration(tree.Variable variable, [Expression initializer]) { |
- assert(!variableDeclarations.containsKey(variable)); |
+ assert(!declaredVariables.contains(variable)); |
String name = getVariableName(variable); |
VariableDeclaration decl = new VariableDeclaration(name, initializer); |
decl.element = variable.element; |
- variableDeclarations[variable] = decl; |
+ declaredVariables.add(variable); |
variables.add(decl); |
} |
@@ -143,10 +158,7 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
if (element.functionSignature != null) { |
FunctionSignature signature = element.functionSignature; |
TypeAnnotation returnType = emitOptionalType(signature.type.returnType); |
- Parameters innerParameters = new Parameters( |
- signature.requiredParameters.mapToList(emitParameterFromElement), |
- signature.optionalParameters.mapToList(emitParameterFromElement), |
- signature.optionalParametersAreNamed); |
+ Parameters innerParameters = emitParameters(signature); |
return new Parameter.function(name, returnType, innerParameters) |
..element = element; |
} else { |
@@ -156,12 +168,11 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
} |
} |
- Parameter emitParameter(tree.Variable param) { |
- return emitParameterFromElement(param.element, getVariableName(param)); |
- } |
- |
- Parameters emitParameters(List<tree.Variable> params) { |
- return new Parameters(params.map(emitParameter).toList(growable:false)); |
+ Parameters emitParameters(FunctionSignature signature) { |
+ return new Parameters( |
+ signature.requiredParameters.mapToList(emitParameterFromElement), |
+ signature.optionalParameters.mapToList(emitParameterFromElement), |
+ signature.optionalParametersAreNamed); |
} |
/// True if the two expressions are a reference to the same variable. |
@@ -233,10 +244,23 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
/// 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; |
@@ -259,6 +283,10 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
} |
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; |
@@ -274,18 +302,54 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
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); |
- if (firstStatement == stmt && isFirstOccurrence) { |
+ |
+ // Try to pull into initializer. |
+ if (firstStatement == stmt && isFirstOccurrence && isDeclaredHere) { |
+ if (isNullLiteral(definition)) definition = null; |
addDeclaration(stmt.variable, definition); |
firstStatement = stmt.next; |
- } else { |
- statementBuffer.add(new ExpressionStatement(makeAssignment( |
- visitVariable(stmt.variable), |
- definition))); |
+ 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); |
} |
@@ -331,10 +395,6 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
} |
void visitWhileTrue(tree.WhileTrue stmt) { |
- List<Expression> updates = stmt.updates.reversed |
- .map(visitExpression) |
- .toList(growable:false); |
- |
List<Statement> savedBuffer = statementBuffer; |
tree.Statement savedFallthrough = fallthrough; |
statementBuffer = <Statement>[]; |
@@ -342,7 +402,8 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
visitStatement(stmt.body); |
Statement body = new Block(statementBuffer); |
- Statement statement = new For(null, null, updates, body); |
+ Statement statement = new While(new Literal(new dart2js.TrueConstant()), |
+ body); |
if (usedLabels.remove(stmt.label.name)) { |
statement = new LabeledStatement(stmt.label.name, statement); |
} |
@@ -354,9 +415,6 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
void visitWhileCondition(tree.WhileCondition stmt) { |
Expression condition = visitExpression(stmt.condition); |
- List<Expression> updates = stmt.updates.reversed |
- .map(visitExpression) |
- .toList(growable:false); |
List<Statement> savedBuffer = statementBuffer; |
tree.Statement savedFallthrough = fallthrough; |
@@ -366,12 +424,7 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
visitStatement(stmt.body); |
Statement body = new Block(statementBuffer); |
Statement statement; |
- if (updates.isEmpty) { |
- // while(E) is the same as for(;E;), but the former is nicer |
- statement = new While(condition, body); |
- } else { |
- statement = new For(null, condition, updates, body); |
- } |
+ statement = new While(condition, body); |
if (usedLabels.remove(stmt.label.name)) { |
statement = new LabeledStatement(stmt.label.name, statement); |
} |
@@ -392,8 +445,8 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
} |
Expression visitReifyTypeVar(tree.ReifyTypeVar exp) { |
- return new ReifyTypeVar(exp.element.name) |
- ..element = exp.element; |
+ return new ReifyTypeVar(exp.typeVariable.name) |
+ ..element = exp.typeVariable; |
} |
Expression visitLiteralList(tree.LiteralList exp) { |
@@ -538,6 +591,25 @@ class ASTEmitter extends tree.Visitor<dynamic, Expression> { |
..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); |
+ } |
+ |
TypeAnnotation emitType(DartType type) { |
if (type is GenericType) { |
return new TypeAnnotation( |
@@ -636,9 +708,13 @@ class ConstantEmitter extends ConstExpVisitor<Expression> { |
} |
Expression visitVariable(VariableConstExp exp) { |
- String name = parent.getConstantName(exp.element); |
+ 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 = exp.element; |
+ ..element = element; |
} |
Expression visitFunction(FunctionConstExp exp) { |
@@ -647,3 +723,56 @@ class ConstantEmitter extends ConstExpVisitor<Expression> { |
} |
} |
+ |
+/// 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) { |
+ 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); |
+ } |
+ } |
+ |
+} |