Index: sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart |
index b9f33613bac553f2eddd0fa523d6a63932859d21..c1f19ebcb66a481725eda7c28010bc54041f9d31 100644 |
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart |
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart |
@@ -206,6 +206,9 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
final List<ConstDeclaration> localConstants; |
+ FunctionElement currentFunction; |
+ final DetectClosureVariables closureLocals; |
+ |
/// Construct a top-level visitor. |
IrBuilder(TreeElements elements, Compiler compiler, this.sourceFile) |
: returnContinuation = new ir.Continuation.retrn(), |
@@ -215,6 +218,7 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
assignedVars = <ir.Primitive>[], |
index2variable = <Element>[], |
localConstants = <ConstDeclaration>[], |
+ closureLocals = new DetectClosureVariables(elements), |
super(elements, compiler) { |
constantBuilder = new ConstExpBuilder(this); |
} |
@@ -233,6 +237,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
index2variable = new List<Element>.from(parent.index2variable), |
constantBuilder = parent.constantBuilder, |
localConstants = parent.localConstants, |
+ currentFunction = parent.currentFunction, |
+ closureLocals = parent.closureLocals, |
super(parent.elements, parent.compiler); |
/** |
@@ -246,26 +252,37 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
ir.FunctionDefinition buildFunctionInternal(FunctionElement element) { |
assert(invariant(element, element.isImplementation)); |
+ currentFunction = element; |
ast.FunctionExpression function = element.node; |
assert(function != null); |
assert(!function.modifiers.isExternal); |
assert(elements[function] != null); |
+ closureLocals.visit(function); |
+ |
root = current = null; |
FunctionSignature signature = element.functionSignature; |
- signature.orderedForEachParameter((parameterElement) { |
+ signature.orderedForEachParameter((ParameterElement parameterElement) { |
+ // TODO(asgerf): Handle parameters with default values. (Use a ConstExp) |
+ if (parameterElement.initializer != null) { |
+ giveup(parameterElement.node, 'Function parameter with default value'); |
+ } |
ir.Parameter parameter = new ir.Parameter(parameterElement); |
parameters.add(parameter); |
- variableIndex[parameterElement] = assignedVars.length; |
- assignedVars.add(parameter); |
- index2variable.add(parameterElement); |
+ if (isClosureVariable(parameterElement)) { |
+ add(new ir.SetClosureVariable(parameterElement, parameter)); |
+ } else { |
+ variableIndex[parameterElement] = assignedVars.length; |
+ assignedVars.add(parameter); |
+ index2variable.add(parameterElement); |
+ } |
}); |
visit(function.body); |
ensureReturn(function); |
- return new ir.FunctionDefinition(returnContinuation, parameters, root, |
- localConstants); |
+ return new ir.FunctionDefinition(element, returnContinuation, parameters, |
+ root, localConstants); |
} |
ConstantSystem get constantSystem => compiler.backend.constantSystem; |
@@ -515,6 +532,17 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
ir.Primitive visitFor(ast.For node) { |
assert(isOpen); |
+ // TODO(asgerf): Handle closure variables declared in a for-loop. |
+ if (node.initializer is ast.VariableDefinitions) { |
+ ast.VariableDefinitions definitions = node.initializer; |
+ for (ast.Node definition in definitions.definitions.nodes) { |
+ Element element = elements[definition]; |
+ if (isClosureVariable(element)) { |
+ return giveup(definition, 'Closure variable in for loop initializer'); |
+ } |
+ } |
+ } |
+ |
// For loops use three named continuations: the entry to the condition, |
// the entry to the body, and the loop exit (break). The CPS translation |
// of [[for (initializer; condition; update) body; successor]] is: |
@@ -731,27 +759,29 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
} else { |
for (ast.Node definition in node.definitions.nodes) { |
Element element = elements[definition]; |
+ ir.Primitive initialValue; |
// Definitions are either SendSets if there is an initializer, or |
// Identifiers if there is no initializer. |
if (definition is ast.SendSet) { |
assert(!definition.arguments.isEmpty); |
assert(definition.arguments.tail.isEmpty); |
- ir.Primitive initialValue = visit(definition.arguments.head); |
- // In case a primitive was introduced for the initializer expression, |
- // use this variable element to help derive a good name for it. |
- initialValue.useElementAsHint(element); |
- variableIndex[element] = assignedVars.length; |
- assignedVars.add(initialValue); |
- index2variable.add(element); |
+ initialValue = visit(definition.arguments.head); |
} else { |
assert(definition is ast.Identifier); |
// The initial value is null. |
// TODO(kmillikin): Consider pooling constants. |
- ir.Constant constant = makePrimConst(constantSystem.createNull()); |
- constant.useElementAsHint(element); |
- add(new ir.LetPrim(constant)); |
+ initialValue = makePrimConst(constantSystem.createNull()); |
+ add(new ir.LetPrim(initialValue)); |
+ } |
+ if (isClosureVariable(element)) { |
+ add(new ir.SetClosureVariable(element, initialValue, |
+ isDeclaration: true)); |
+ } else { |
+ // In case a primitive was introduced for the initializer expression, |
+ // use this variable element to help derive a good name for it. |
+ initialValue.useElementAsHint(element); |
variableIndex[element] = assignedVars.length; |
- assignedVars.add(constant); |
+ assignedVars.add(initialValue); |
index2variable.add(element); |
} |
} |
@@ -959,6 +989,14 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
return visit(node.expression); |
} |
+ ir.Primitive continueWithExpression(ir.Expression build(ir.Continuation k)) { |
+ ir.Parameter v = new ir.Parameter(null); |
+ ir.Continuation k = new ir.Continuation([v]); |
+ ir.Expression expression = build(k); |
+ add(new ir.LetCont(k, expression)); |
+ return v; |
+ } |
+ |
ir.Primitive translateClosureCall(ir.Primitive receiver, |
Selector closureSelector, |
ast.NodeList arguments) { |
@@ -968,12 +1006,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
closureSelector.argumentCount, |
closureSelector.namedArguments); |
List<ir.Primitive> args = arguments.nodes.mapToList(visit, growable:false); |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.Expression invoke = |
- new ir.InvokeMethod(receiver, namedCallSelector, k, args); |
- add(new ir.LetCont(k, invoke)); |
- return v; |
+ return continueWithExpression( |
+ (k) => new ir.InvokeMethod(receiver, namedCallSelector, k, args)); |
} |
ir.Primitive visitClosureSend(ast.Send node) { |
@@ -982,6 +1016,9 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
ir.Primitive closureTarget; |
if (element == null) { |
closureTarget = visit(node.selector); |
+ } else if (isClosureVariable(element)) { |
+ closureTarget = new ir.GetClosureVariable(element); |
+ add(new ir.LetPrim(closureTarget)); |
} else { |
assert(Elements.isLocal(element)); |
closureTarget = lookupLocal(element); |
@@ -1020,12 +1057,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
for (ast.Node n in node.arguments) { |
arguments.add(visit(n)); |
} |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.Expression invoke = |
- createDynamicInvoke(node, selector, receiver, k, arguments); |
- add(new ir.LetCont(k, invoke)); |
- return v; |
+ return continueWithExpression( |
+ (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); |
} |
_GetterElements translateGetter(ast.Send node, Selector selector) { |
@@ -1035,17 +1068,17 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
ir.Primitive index; |
if (Elements.isErroneousElement(element)) { |
- giveup(node, 'Erroneous element on GetterSend'); |
- return null; |
+ return giveup(node, 'Erroneous element on GetterSend'); |
} |
if (element != null && element.isConst) { |
// Reference to constant local, top-level or static field |
- |
result = translateConstant(node); |
+ } else if (isClosureVariable(element)) { |
+ result = new ir.GetClosureVariable(element); |
+ add(new ir.LetPrim(result)); |
} else if (Elements.isLocal(element)) { |
// Reference to local variable |
- |
result = lookupLocal(element); |
} else if (element == null || |
Elements.isInstanceField(element) || |
@@ -1064,31 +1097,20 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
arguments.add(index); |
} |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
assert(selector.kind == SelectorKind.GETTER || |
selector.kind == SelectorKind.INDEX); |
- ir.Expression invoke = |
- createDynamicInvoke(node, selector, receiver, k, arguments); |
- add(new ir.LetCont(k, invoke)); |
- result = v; |
- } else if (element.isField || element.isGetter || |
- // Access to a static field or getter (non-static case handled above). |
- // Even if there is only a setter, we compile as if it was a getter, |
- // so the vm can fail at runtime. |
- |
- element.isSetter) { |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
+ result = continueWithExpression( |
+ (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); |
+ } else if (element.isField || element.isGetter || element.isSetter) { |
+ // Access to a static field or getter (non-static case handled above). |
+ // Even if there is only a setter, we compile as if it was a getter, |
+ // so the vm can fail at runtime. |
assert(selector.kind == SelectorKind.GETTER || |
selector.kind == SelectorKind.SETTER); |
- ir.Expression invoke = |
- new ir.InvokeStatic(element, selector, k, []); |
- add(new ir.LetCont(k, invoke)); |
- result = v; |
+ result = continueWithExpression( |
+ (k) => new ir.InvokeStatic(element, selector, k, [])); |
} else if (Elements.isStaticOrTopLevelFunction(element)) { |
// Convert a top-level or static function to a function object. |
- |
result = translateConstant(node); |
} else { |
throw "Unexpected SendSet getter: $node, $element"; |
@@ -1254,11 +1276,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); |
if (type.isMalformed) return giveup(node, "Malformed type for as"); |
ir.Primitive receiver = visit(node.receiver); |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.AsCast asCast = new ir.AsCast(receiver, type, k); |
- add(new ir.LetCont(k, asCast)); |
- return v; |
+ return continueWithExpression( |
+ (k) => new ir.AsCast(receiver, type, k)); |
} |
return giveup(node); |
} |
@@ -1285,12 +1304,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
// TODO(lry): support default arguments, need support for locals. |
List<ir.Definition> arguments = node.arguments.mapToList(visit, |
growable:false); |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.Expression invoke = |
- new ir.InvokeStatic(element, selector, k, arguments); |
- add(new ir.LetCont(k, invoke)); |
- return v; |
+ return continueWithExpression( |
+ (k) => new ir.InvokeStatic(element, selector, k, arguments)); |
} |
ir.Primitive visitSuperSend(ast.Send node) { |
@@ -1311,8 +1326,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
// If the user is trying to invoke the type literal or variable, |
// it must be treated as a function call. |
if (node.argumentsNode != null) { |
- // TODO(sigurdm): Change this to match proposed semantics of issue #19725. |
- return visitDynamicSend(node); |
+ // TODO(sigurdm): Handle this to match proposed semantics of issue #19725. |
+ return giveup(node, 'Type literal invoked as function'); |
} |
DartType type = elements.getTypeLiteralType(node); |
@@ -1325,6 +1340,13 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
} |
} |
+ /// True if [element] is a local variable, local function, or parameter that |
+ /// is accessed from an inner function. Recursive self-references in a local |
+ /// function count as closure accesses. |
+ bool isClosureVariable(Element element) { |
+ return closureLocals.isClosureVariable(element); |
+ } |
+ |
ir.Primitive visitSendSet(ast.SendSet node) { |
assert(isOpen); |
Element element = elements[node]; |
@@ -1386,33 +1408,29 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
} |
// Set the value |
- if (Elements.isLocal(element)) { |
+ if (isClosureVariable(element)) { |
+ add(new ir.SetClosureVariable(element, valueToStore)); |
+ } else if (Elements.isLocal(element)) { |
valueToStore.useElementAsHint(element); |
assignedVars[variableIndex[element]] = valueToStore; |
} else if (Elements.isStaticOrTopLevel(element)) { |
assert(element.isField || element.isSetter); |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
Selector selector = elements.getSelector(node); |
- ir.InvokeStatic invoke = |
- new ir.InvokeStatic(element, selector, k, [valueToStore]); |
- add(new ir.LetCont(k, invoke)); |
+ continueWithExpression( |
+ (k) => new ir.InvokeStatic(element, selector, k, [valueToStore])); |
} else { |
if (element != null && Elements.isUnresolved(element)) { |
return giveup(node, 'SendSet: non-local, non-static, unresolved'); |
} |
// Setter or index-setter invocation |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
Selector selector = elements.getSelector(node); |
assert(selector.kind == SelectorKind.SETTER || |
selector.kind == SelectorKind.INDEX); |
List<ir.Definition> arguments = selector.isIndexSet |
? [index, valueToStore] |
: [valueToStore]; |
- ir.Expression invoke = |
- createDynamicInvoke(node, selector, receiver, k, arguments); |
- add(new ir.LetCont(k, invoke)); |
+ continueWithExpression( |
+ (k) => createDynamicInvoke(node, selector, receiver, k, arguments)); |
} |
if (node.isPostfix) { |
@@ -1437,24 +1455,16 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
GenericType type = elements.getType(node); |
List<ir.Primitive> args = |
node.send.arguments.mapToList(visit, growable:false); |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.InvokeConstructor invoke = |
- new ir.InvokeConstructor(type, element,selector, k, args); |
- add(new ir.LetCont(k, invoke)); |
- return v; |
+ return continueWithExpression( |
+ (k) => new ir.InvokeConstructor(type, element,selector, k, args)); |
} |
ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { |
assert(isOpen); |
ir.Primitive first = visit(node.first); |
ir.Primitive second = visit(node.second); |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.ConcatenateStrings concat = |
- new ir.ConcatenateStrings(k, [first, second]); |
- add(new ir.LetCont(k, concat)); |
- return v; |
+ return continueWithExpression( |
+ (k) => new ir.ConcatenateStrings(k, [first, second])); |
} |
ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { |
@@ -1467,11 +1477,8 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
arguments.add(visit(part.expression)); |
arguments.add(visitLiteralString(part.string)); |
} |
- ir.Parameter v = new ir.Parameter(null); |
- ir.Continuation k = new ir.Continuation([v]); |
- ir.ConcatenateStrings concat = new ir.ConcatenateStrings(k, arguments); |
- add(new ir.LetCont(k, concat)); |
- return v; |
+ return continueWithExpression( |
+ (k) => new ir.ConcatenateStrings(k, arguments)); |
} |
ir.Primitive translateConstant(ast.Node node, [Constant value]) { |
@@ -1484,9 +1491,38 @@ class IrBuilder extends ResolvedVisitor<ir.Primitive> { |
return primitive; |
} |
+ ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) { |
+ return new IrBuilder(elements, compiler, sourceFile) |
+ .buildFunctionInternal(elements[node]); |
+ } |
+ |
+ ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { |
+ FunctionElement element = elements[node]; |
+ ir.FunctionDefinition inner = makeSubFunction(node); |
+ ir.CreateFunction prim = new ir.CreateFunction(inner); |
+ add(new ir.LetPrim(prim)); |
+ return prim; |
+ } |
+ |
+ ir.Primitive visitFunctionDeclaration(ast.FunctionDeclaration node) { |
+ FunctionElement element = elements[node.function]; |
+ ir.FunctionDefinition inner = makeSubFunction(node.function); |
+ if (isClosureVariable(element)) { |
+ add(new ir.DeclareFunction(element, inner)); |
+ } else { |
+ ir.CreateFunction prim = new ir.CreateFunction(inner); |
+ add(new ir.LetPrim(prim)); |
+ variableIndex[element] = assignedVars.length; |
+ assignedVars.add(prim); |
+ index2variable.add(element); |
+ prim.useElementAsHint(element); |
+ } |
+ return null; |
+ } |
+ |
static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; |
- ir.Primitive giveup(ast.Node node, [String reason]) { |
+ dynamic giveup(ast.Node node, [String reason]) { |
throw ABORT_IRNODE_BUILDER; |
} |
@@ -1662,3 +1698,45 @@ class ConstExpBuilder extends ast.Visitor<ConstExp> { |
} |
} |
+ |
+/// Classifies local variables and local functions as 'closure variables'. |
+/// A closure variable is one that is accessed from an inner function nested |
+/// one or more levels inside the one that declares it. |
+class DetectClosureVariables extends ast.Visitor { |
+ final TreeElements elements; |
+ DetectClosureVariables(this.elements); |
+ |
+ FunctionElement currentFunction; |
+ Set<Element> usedFromClosure = new Set<Element>(); |
+ Set<FunctionElement> recursiveFunctions = new Set<FunctionElement>(); |
+ |
+ bool isClosureVariable(Element element) => usedFromClosure.contains(element); |
+ |
+ void markAsClosureVariable(Element element) { |
+ usedFromClosure.add(element); |
+ } |
+ |
+ visit(ast.Node node) => node.accept(this); |
+ |
+ visitNode(ast.Node node) { |
+ node.visitChildren(this); |
+ } |
+ |
+ visitSend(ast.Send node) { |
+ Element element = elements[node]; |
+ if (Elements.isLocal(element) && |
+ !element.isConst && |
+ element.enclosingElement != currentFunction) { |
+ markAsClosureVariable(element); |
+ } |
+ node.visitChildren(this); |
+ } |
+ |
+ visitFunctionExpression(ast.FunctionExpression node) { |
+ FunctionElement oldFunction = currentFunction; |
+ currentFunction = elements[node]; |
+ visit(node.body); |
+ currentFunction = oldFunction; |
+ } |
+ |
+} |