mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:42:11 +00:00
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:
parent
0928369fc9
commit
dadff8c117
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
'''),
|
||||
]),
|
||||
];
|
||||
|
|
|
@ -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))
|
||||
'''),
|
||||
]),
|
||||
];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue