Index: pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart |
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart |
index e860021bb522100ea2b2ad27ad52ebdc8a2c19d0..53960b5a4e411e9688559f1fbdab6b2ae38f543b 100644 |
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart |
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart |
@@ -1150,10 +1150,16 @@ abstract class Visitor<T> { |
T visitForeignCode(ForeignCode node); |
} |
-/// Recursively visits the entire CPS term, and calls abstract `process*` |
-/// (i.e. `processLetPrim`) functions in pre-order. |
-class RecursiveVisitor implements Visitor { |
- const RecursiveVisitor(); |
+/// Visits all non-recursive children of a CPS term, i.e. anything |
+/// not of type [Expression] or [Continuation]. |
+/// |
+/// Note that the non-recursive nodes can contain other nodes inside of them, |
+/// e.g. [Branch] contains an [IsTrue] which contains a [Reference]. |
+/// |
+/// The `process*` methods are called in pre-order for every node visited. |
+/// These can be overridden without disrupting the visitor traversal. |
+class LeafVisitor implements Visitor { |
+ const LeafVisitor(); |
visit(Node node) => node.accept(this); |
@@ -1165,7 +1171,6 @@ class RecursiveVisitor implements Visitor { |
if (node.thisParameter != null) visit(node.thisParameter); |
node.parameters.forEach(visit); |
visit(node.returnContinuation); |
- visit(node.body); |
} |
// Expressions. |
@@ -1174,21 +1179,17 @@ class RecursiveVisitor implements Visitor { |
visitLetPrim(LetPrim node) { |
processLetPrim(node); |
visit(node.primitive); |
- visit(node.body); |
} |
processLetCont(LetCont node) {} |
visitLetCont(LetCont node) { |
processLetCont(node); |
node.continuations.forEach(visit); |
- visit(node.body); |
} |
processLetHandler(LetHandler node) {} |
visitLetHandler(LetHandler node) { |
processLetHandler(node); |
- visit(node.handler); |
- visit(node.body); |
} |
processLetMutable(LetMutable node) {} |
@@ -1196,7 +1197,6 @@ class RecursiveVisitor implements Visitor { |
processLetMutable(node); |
visit(node.variable); |
processReference(node.value); |
- visit(node.body); |
} |
processInvokeStatic(InvokeStatic node) {} |
@@ -1329,7 +1329,6 @@ class RecursiveVisitor implements Visitor { |
visitContinuation(Continuation node) { |
processContinuation(node); |
node.parameters.forEach(visitParameter); |
- if (node.body != null) visit(node.body); |
} |
processIsTrue(IsTrue node) {} |
@@ -1446,15 +1445,132 @@ class RecursiveVisitor implements Visitor { |
} |
} |
+typedef void StackAction(); |
+ |
+/// Calls `process*` for all nodes in a tree. |
+/// For simple usage, only override the `process*` methods. |
+/// |
+/// To avoid deep recursion, this class uses an "action stack" containing |
+/// callbacks to be invoked after the processing of some term has finished. |
+/// |
+/// To avoid excessive overhead from the action stack, basic blocks of |
+/// interior nodes are iterated in a loop without using the action stack. |
+/// |
+/// The iteration order can be controlled by overriding the `traverse*` |
+/// methods for [LetCont], [LetPrim], [LetMutable], [LetHandler] and |
+/// [Continuation]. |
+/// |
+/// The `traverse*` methods return the expression to visit next, and may |
+/// push other subterms onto the stack using [push] or [pushAction] to visit |
+/// them later. Actions pushed onto the stack will be executed after the body |
+/// has been processed (and the stack actions it pushed have been executed). |
+/// |
+/// By default, the `traverse` methods visit all non-recursive subterms, |
+/// push all bound continuations on the stack, and return the body of the term. |
+/// |
+/// Subclasses should not override the `visit` methods for the nodes that have |
+/// a `traverse` method. |
+class RecursiveVisitor extends LeafVisitor { |
+ List<StackAction> _stack = <StackAction>[]; |
+ |
+ void pushAction(StackAction callback) { |
+ _stack.add(callback); |
+ } |
+ |
+ void push(Continuation cont) { |
+ _stack.add(() { |
+ if (cont.isReturnContinuation) { |
+ traverseContinuation(cont); |
+ } else { |
+ _processBlock(traverseContinuation(cont)); |
+ } |
+ }); |
+ } |
+ |
+ visitFunctionDefinition(FunctionDefinition node) { |
+ processFunctionDefinition(node); |
+ if (node.thisParameter != null) visit(node.thisParameter); |
+ node.parameters.forEach(visit); |
+ visit(node.returnContinuation); |
+ visit(node.body); |
+ } |
+ |
+ visitContinuation(Continuation cont) { |
+ if (cont.isReturnContinuation) { |
+ traverseContinuation(cont); |
+ } else { |
+ _trampoline(traverseContinuation(cont)); |
+ } |
+ } |
+ |
+ visitLetPrim(LetPrim node) => _trampoline(node); |
+ visitLetCont(LetCont node) => _trampoline(node); |
+ visitLetHandler(LetHandler node) => _trampoline(node); |
+ visitLetMutable(LetMutable node) => _trampoline(node); |
+ |
+ Expression traverseContinuation(Continuation cont) { |
+ processContinuation(cont); |
+ cont.parameters.forEach(visitParameter); |
+ return cont.body; |
+ } |
+ |
+ Expression traverseLetCont(LetCont node) { |
+ processLetCont(node); |
+ node.continuations.forEach(push); |
+ return node.body; |
+ } |
+ |
+ Expression traverseLetHandler(LetHandler node) { |
+ processLetHandler(node); |
+ push(node.handler); |
+ return node.body; |
+ } |
+ |
+ Expression traverseLetPrim(LetPrim node) { |
+ processLetPrim(node); |
+ visit(node.primitive); |
+ return node.body; |
+ } |
+ |
+ Expression traverseLetMutable(LetMutable node) { |
+ processLetMutable(node); |
+ visit(node.variable); |
+ processReference(node.value); |
+ return node.body; |
+ } |
+ |
+ void _trampoline(Expression node) { |
+ int initialHeight = _stack.length; |
+ _processBlock(node); |
+ while (_stack.length > initialHeight) { |
+ StackAction callback = _stack.removeLast(); |
+ callback(); |
+ } |
+ } |
+ |
+ _processBlock(Expression node) { |
+ while (node is InteriorExpression) { |
+ if (node is LetCont) { |
+ node = traverseLetCont(node); |
+ } else if (node is LetHandler) { |
+ node = traverseLetHandler(node); |
+ } else if (node is LetPrim) { |
+ node = traverseLetPrim(node); |
+ } else { |
+ node = traverseLetMutable(node); |
+ } |
+ } |
+ visit(node); |
+ } |
+} |
+ |
/// Visit a just-deleted subterm and unlink all [Reference]s in it. |
class RemovalVisitor extends RecursiveVisitor { |
- const RemovalVisitor(); |
- |
processReference(Reference reference) { |
reference.unlink(); |
} |
static void remove(Node node) { |
- (const RemovalVisitor()).visit(node); |
+ (new RemovalVisitor()).visit(node); |
} |
} |