Support for-in in analyzer2dart.

BUG=
R=paulberry@google.com, sigurdm@google.com

Review URL: https://codereview.chromium.org//702453002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@41491 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
johnniwinther@google.com 2014-11-04 14:53:18 +00:00
parent 0928369fc9
commit dadff8c117
5 changed files with 338 additions and 122 deletions

View file

@ -31,6 +31,8 @@ class CpsGeneratingVisitor extends SemanticVisitor<ir.Node>
Source get currentSource => element.source;
analyzer.LibraryElement get currentLibrary => element.library;
ir.Node visit(AstNode node) => node.accept(this);
@override
@ -344,6 +346,46 @@ class CpsGeneratingVisitor extends SemanticVisitor<ir.Node>
buildBody: subbuild(node.body));
}
@override
visitDeclaredIdentifier(DeclaredIdentifier node) {
giveUp(node, "Unexpected node: DeclaredIdentifier");
}
@override
visitForEachStatement(ForEachStatement node) {
SubbuildFunction buildVariableDeclaration;
dart2js.Element variableElement;
Selector variableSelector;
if (node.identifier != null) {
AccessSemantics accessSemantics =
node.identifier.accept(ACCESS_SEMANTICS_VISITOR);
if (accessSemantics.kind == AccessKind.DYNAMIC) {
variableSelector = new Selector.setter(
node.identifier.name, converter.convertElement(currentLibrary));
} else if (accessSemantics.element != null) {
variableElement = converter.convertElement(accessSemantics.element);
variableSelector = new Selector.setter(
variableElement.name,
converter.convertElement(accessSemantics.element.library));
} else {
giveUp(node, 'For-in of unresolved variable: $accessSemantics');
}
} else {
assert(invariant(
node, node.loopVariable != null, "Loop variable expected"));
variableElement = converter.convertElement(node.loopVariable.element);
buildVariableDeclaration = (IrBuilder builder) {
builder.declareLocalVariable(variableElement);
};
}
// TODO(johnniwinther): Support `for-in` as a jump target.
irBuilder.buildForIn(
buildExpression: subbuild(node.iterable),
buildVariableDeclaration: buildVariableDeclaration,
variableElement: variableElement,
variableSelector: variableSelector,
buildBody: subbuild(node.body));
}
@override
ir.Primitive visitIsExpression(IsExpression node) {
return irBuilder.buildTypeOperator(

View file

@ -679,6 +679,61 @@ const TestSpec('''
main(a) {
return a as String;
}
'''),
]),
const Group('For in loop', const <TestSpec>[
// TODO(johnniwinther): Add tests for `i` as top-level, static and instance
// fields.
const TestSpec('''
main(a) {
for (var i in a) {
print(i);
}
}
''', '''
main(a) {
var v0 = a.iterator;
while (v0.moveNext()) {
print(v0.current);
}
}'''),
const TestSpec('''
main(a) {
for (var i in a) {
print(i);
i = 0;
print(i);
}
}
''', '''
main(a) {
var v0 = a.iterator, i;
while (v0.moveNext()) {
i = v0.current;
print(i);
i = 0;
print(i);
}
}
'''),
const TestSpec('''
main(a) {
var i;
for (i in a) {
print(i);
}
}
''', '''
main(a) {
var i, v0 = a.iterator;
while (v0.moveNext()) {
i = v0.current;
print(i);
}
}
'''),
]),
];

View file

@ -1108,6 +1108,97 @@ main(a) {
(LetCont (k0 v0)
(InvokeContinuation return v0))
(TypeOperator as a String k0))
'''),
]),
const Group('For in loop', const <TestSpec>[
// TODO(johnniwinther): Add tests for `i` as top-level, static and instance
// fields.
const TestSpec('''
main(a) {
for (var i in a) {
print(i);
}
}
''', '''
(FunctionDefinition main (a return)
(LetCont (k0 v0)
(LetCont* (k1 v1)
(LetCont (k2 v2)
(LetCont (k3)
(LetPrim v3 (Constant NullConstant))
(InvokeContinuation return v3))
(LetCont (k4)
(LetPrim v4 (Constant NullConstant))
(LetCont (k5 v5)
(LetCont (k6 v6)
(InvokeContinuation* k1 v1))
(InvokeStatic print v5 k6))
(InvokeMethod v0 current k5))
(Branch (IsTrue v2) k4 k3))
(InvokeMethod v0 moveNext k2))
(InvokeContinuation k1 a))
(InvokeMethod a iterator k0))
'''),
const TestSpec('''
main(a) {
for (var i in a) {
print(i);
i = 0;
print(i);
}
}
''', '''
(FunctionDefinition main (a return)
(LetCont (k0 v0)
(LetCont* (k1 v1)
(LetCont (k2 v2)
(LetCont (k3)
(LetPrim v3 (Constant NullConstant))
(InvokeContinuation return v3))
(LetCont (k4)
(LetPrim v4 (Constant NullConstant))
(LetCont (k5 v5)
(LetCont (k6 v6)
(LetPrim v7 (Constant IntConstant(0)))
(LetCont (k7 v8)
(InvokeContinuation* k1 v1))
(InvokeStatic print v7 k7))
(InvokeStatic print v5 k6))
(InvokeMethod v0 current k5))
(Branch (IsTrue v2) k4 k3))
(InvokeMethod v0 moveNext k2))
(InvokeContinuation k1 a))
(InvokeMethod a iterator k0))
'''),
const TestSpec('''
main(a) {
var i;
for (i in a) {
print(i);
}
}
''', '''
(FunctionDefinition main (a return)
(LetPrim v0 (Constant NullConstant))
(LetCont (k0 v1)
(LetCont* (k1 v2 v3)
(LetCont (k2 v4)
(LetCont (k3)
(LetPrim v5 (Constant NullConstant))
(InvokeContinuation return v5))
(LetCont (k4)
(LetCont (k5 v6)
(LetCont (k6 v7)
(InvokeContinuation* k1 v2 v6))
(InvokeStatic print v6 k6))
(InvokeMethod v1 current k5))
(Branch (IsTrue v4) k4 k3))
(InvokeMethod v1 moveNext k2))
(InvokeContinuation k1 a v0))
(InvokeMethod a iterator k0))
'''),
]),
];

View file

@ -884,6 +884,133 @@ class IrBuilder {
}
}
/// Creates a for-in loop, `for (v in e) b`.
///
/// [buildExpression] creates the expression, `e`. The variable, `v`, can
/// take one of three forms:
/// 1) `v` can be declared within the for-in statement, like in
/// `for (var v in e)`, in which case, [buildVariableDeclaration]
/// creates its declaration and [variableElement] is the element for
/// the declared variable,
/// 2) `v` is predeclared statically known variable, that is top-level,
/// static, or local variable, in which case [variableElement] is the
/// variable element, and [variableSelector] defines its write access,
/// 3) `v` is an instance variable in which case [variableSelector]
/// defines its write access.
/// [buildBody] creates the body, `b`, of the loop. The jump [target] is used
/// to identify which `break` and `continue` statements that have this for-in
/// statement as their target.
void buildForIn({SubbuildFunction buildExpression,
SubbuildFunction buildVariableDeclaration,
Element variableElement,
Selector variableSelector,
SubbuildFunction buildBody,
JumpTarget target}) {
// The for-in loop
//
// for (a in e) s;
//
// Is compiled analogously to:
//
// a = e.iterator;
// while (a.moveNext()) {
// var n0 = a.current;
// s;
// }
// The condition and body are delimited.
IrBuilder condBuilder = new IrBuilder.recursive(this);
ir.Primitive expressionReceiver = buildExpression(this);
List<ir.Primitive> emptyArguments = new List<ir.Primitive>();
ir.Parameter iterator = new ir.Parameter(null);
ir.Continuation iteratorInvoked = new ir.Continuation([iterator]);
add(new ir.LetCont(iteratorInvoked,
new ir.InvokeMethod(expressionReceiver,
new Selector.getter("iterator", null), iteratorInvoked,
emptyArguments)));
ir.Parameter condition = new ir.Parameter(null);
ir.Continuation moveNextInvoked = new ir.Continuation([condition]);
condBuilder.add(new ir.LetCont(moveNextInvoked,
new ir.InvokeMethod(iterator,
new Selector.call("moveNext", null, 0),
moveNextInvoked, emptyArguments)));
JumpCollector breakCollector = new JumpCollector(target);
JumpCollector continueCollector = new JumpCollector(target);
state.breakCollectors.add(breakCollector);
state.continueCollectors.add(continueCollector);
IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
if (buildVariableDeclaration != null) {
buildVariableDeclaration(bodyBuilder);
}
ir.Parameter currentValue = new ir.Parameter(null);
ir.Continuation currentInvoked = new ir.Continuation([currentValue]);
bodyBuilder.add(new ir.LetCont(currentInvoked,
new ir.InvokeMethod(iterator, new Selector.getter("current", null),
currentInvoked, emptyArguments)));
if (Elements.isLocal(variableElement)) {
bodyBuilder.buildLocalSet(variableElement, currentValue);
} else if (Elements.isStaticOrTopLevel(variableElement)) {
bodyBuilder.buildStaticSet(
variableElement, variableSelector, currentValue);
} else {
ir.Primitive receiver = bodyBuilder.buildThis();
bodyBuilder.buildDynamicSet(receiver, variableSelector, currentValue);
}
buildBody(bodyBuilder);
assert(state.breakCollectors.last == breakCollector);
assert(state.continueCollectors.last == continueCollector);
state.breakCollectors.removeLast();
state.continueCollectors.removeLast();
// Create body entry and loop exit continuations and a branch to them.
ir.Continuation bodyContinuation = new ir.Continuation([]);
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.LetCont branch =
new ir.LetCont(exitContinuation,
new ir.LetCont(bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation)));
// If there are breaks in the body, then there must be a join-point
// continuation for the normal exit and the breaks.
bool hasBreaks = !breakCollector.isEmpty;
ir.LetCont letJoin;
if (hasBreaks) {
letJoin = new ir.LetCont(null, branch);
condBuilder.add(letJoin);
condBuilder._current = branch;
} else {
condBuilder.add(branch);
}
ir.Continuation loopContinuation =
new ir.Continuation(condBuilder._parameters);
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
invokeFullJoin(
loopContinuation, continueCollector, recursive: true);
bodyContinuation.body = bodyBuilder._root;
loopContinuation.body = condBuilder._root;
add(new ir.LetCont(loopContinuation,
new ir.InvokeContinuation(loopContinuation,
environment.index2value)));
if (hasBreaks) {
_current = branch;
environment = condBuilder.environment;
breakCollector.addJump(this);
letJoin.continuation = createJoin(environment.length, breakCollector);
_current = letJoin;
} else {
_current = condBuilder._current;
environment = condBuilder.environment;
}
}
/// Creates a while loop in which the condition and body are created by
/// [buildCondition] and [buildBody], respectively.
@ -1170,6 +1297,14 @@ class IrBuilder {
return joinContinuation.parameters.last;
}
/// Creates an access to `this`.
ir.Primitive buildThis() {
assert(isOpen);
ir.Primitive result = new ir.This();
add(new ir.LetPrim(result));
return result;
}
/// Create a non-recursive join-point continuation.
///
/// Given the environment length at the join point and a list of

View file

@ -252,123 +252,23 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
target: elements.getTargetDefinition(node));
}
ir.Primitive visitForIn(ast.ForIn node) {
// The for-in loop
//
// for (a in e) s;
//
// Is compiled analogously to:
//
// a = e.iterator;
// while (a.moveNext()) {
// var n0 = a.current;
// s;
// }
// The condition and body are delimited.
IrBuilder condBuilder = new IrBuilder.recursive(irBuilder);
ir.Primitive expressionReceiver = visit(node.expression);
List<ir.Primitive> emptyArguments = new List<ir.Primitive>();
ir.Parameter iterator = new ir.Parameter(null);
ir.Continuation iteratorInvoked = new ir.Continuation([iterator]);
irBuilder.add(new ir.LetCont(iteratorInvoked,
new ir.InvokeMethod(expressionReceiver,
new Selector.getter("iterator", null), iteratorInvoked,
emptyArguments)));
ir.Parameter condition = new ir.Parameter(null);
ir.Continuation moveNextInvoked = new ir.Continuation([condition]);
condBuilder.add(new ir.LetCont(moveNextInvoked,
new ir.InvokeMethod(iterator,
new Selector.call("moveNext", null, 0),
moveNextInvoked, emptyArguments)));
JumpTarget target = elements.getTargetDefinition(node);
JumpCollector breakCollector = new JumpCollector(target);
JumpCollector continueCollector = new JumpCollector(target);
irBuilder.state.breakCollectors.add(breakCollector);
irBuilder.state.continueCollectors.add(continueCollector);
IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
visitForIn(ast.ForIn node) {
// [node.declaredIdentifier] can be either an [ast.VariableDefinitions]
// (defining a new local variable) or a send designating some existing
// variable.
ast.Node identifier = node.declaredIdentifier;
ast.VariableDefinitions variableDeclaration =
identifier.asVariableDefinitions();
Element variableElement = elements.getForInVariable(node);
Selector selector = elements.getSelector(identifier);
// node.declaredIdentifier can be either an ast.VariableDefinitions
// (defining a new local variable) or a send designating some existing
// variable.
ast.Node declaredIdentifier = node.declaredIdentifier;
if (declaredIdentifier is ast.VariableDefinitions) {
withBuilder(bodyBuilder, () => visit(declaredIdentifier));
}
ir.Parameter currentValue = new ir.Parameter(null);
ir.Continuation currentInvoked = new ir.Continuation([currentValue]);
bodyBuilder.add(new ir.LetCont(currentInvoked,
new ir.InvokeMethod(iterator, new Selector.getter("current", null),
currentInvoked, emptyArguments)));
if (Elements.isLocal(variableElement)) {
bodyBuilder.buildLocalSet(variableElement, currentValue);
} else if (Elements.isStaticOrTopLevel(variableElement)) {
bodyBuilder.buildStaticSet(variableElement, selector, currentValue);
} else {
ir.Primitive receiver =
withBuilder(bodyBuilder, () => lookupThis());
bodyBuilder.buildDynamicSet(receiver, selector, currentValue);
}
withBuilder(bodyBuilder, () => visit(node.body));
assert(irBuilder.state.breakCollectors.last == breakCollector);
assert(irBuilder.state.continueCollectors.last == continueCollector);
irBuilder.state.breakCollectors.removeLast();
irBuilder.state.continueCollectors.removeLast();
// Create body entry and loop exit continuations and a branch to them.
ir.Continuation bodyContinuation = new ir.Continuation([]);
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.LetCont branch =
new ir.LetCont(exitContinuation,
new ir.LetCont(bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation)));
// If there are breaks in the body, then there must be a join-point
// continuation for the normal exit and the breaks.
bool hasBreaks = !breakCollector.isEmpty;
ir.LetCont letJoin;
if (hasBreaks) {
letJoin = new ir.LetCont(null, branch);
condBuilder.add(letJoin);
condBuilder._current = branch;
} else {
condBuilder.add(branch);
}
ir.Continuation loopContinuation =
new ir.Continuation(condBuilder._parameters);
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
irBuilder.invokeFullJoin(
loopContinuation, continueCollector, recursive: true);
bodyContinuation.body = bodyBuilder._root;
loopContinuation.body = condBuilder._root;
irBuilder.add(new ir.LetCont(loopContinuation,
new ir.InvokeContinuation(loopContinuation,
irBuilder.environment.index2value)));
if (hasBreaks) {
irBuilder._current = branch;
irBuilder.environment = condBuilder.environment;
breakCollector.addJump(irBuilder);
letJoin.continuation =
irBuilder.createJoin(irBuilder.environment.length, breakCollector);
irBuilder._current = letJoin;
} else {
irBuilder._current = condBuilder._current;
irBuilder.environment = condBuilder.environment;
}
return null;
irBuilder.buildForIn(
buildExpression: subbuild(node.expression),
buildVariableDeclaration: subbuild(variableDeclaration),
variableElement: variableElement,
variableSelector: selector,
buildBody: subbuild(node.body),
target: elements.getTargetDefinition(node));
}
ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) {
@ -489,10 +389,9 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
}
ir.Primitive visitIdentifier(ast.Identifier node) {
assert(irBuilder.isOpen);
// "this" is the only identifier that should be met by the visitor.
assert(node.isThis());
return lookupThis();
return irBuilder.buildThis();
}
ir.Primitive visitParenthesizedExpression(
@ -521,12 +420,6 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
return receiver;
}
ir.Primitive lookupThis() {
ir.Primitive result = new ir.This();
irBuilder.add(new ir.LetPrim(result));
return result;
}
// ==== Sends ====
ir.Primitive visitAssert(ast.Send node) {
assert(irBuilder.isOpen);
@ -573,7 +466,7 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
/// If [node] is super, returns null (for special handling)
/// Otherwise visits [node] and returns the result.
ir.Primitive visitReceiver(ast.Expression node) {
if (node == null) return lookupThis();
if (node == null) return irBuilder.buildThis();
if (node.isSuper()) return null;
return visit(node);
}