| OLD | NEW | 
|---|
| 1 library dart2js.cps_ir_integrity; | 1 library dart2js.cps_ir_integrity; | 
| 2 | 2 | 
| 3 import 'cps_ir_nodes.dart'; | 3 import 'cps_ir_nodes.dart'; | 
| 4 import 'cps_ir_nodes_sexpr.dart'; | 4 import 'cps_ir_nodes_sexpr.dart'; | 
| 5 import '../tracer.dart' as tracer; | 5 import '../tracer.dart' as tracer; | 
| 6 | 6 | 
| 7 /// Dump S-expressions on error if the tracer is enabled. | 7 /// Dump S-expressions on error if the tracer is enabled. | 
| 8 /// | 8 /// | 
| 9 /// Technically this has nothing to do with the tracer, but if you want one | 9 /// Technically this has nothing to do with the tracer, but if you want one | 
| 10 /// enabled, you typically want the other as well, so we use the same flag. | 10 /// enabled, you typically want the other as well, so we use the same flag. | 
| 11 const bool ENABLE_DUMP = tracer.TRACE_FILTER_PATTERN != null; | 11 const bool ENABLE_DUMP = tracer.TRACE_FILTER_PATTERN != null; | 
| 12 | 12 | 
| 13 /// Performs integrity checks on the CPS IR. | 13 /// Performs integrity checks on the CPS IR. | 
| 14 /// | 14 /// | 
| 15 /// To be run for debugging purposes, not for use in production. | 15 /// To be run for debugging purposes, not for use in production. | 
| 16 /// | 16 /// | 
| 17 /// The following integrity checks are performed: | 17 /// The following integrity checks are performed: | 
| 18 /// | 18 /// | 
| 19 /// - References are in scope of their definitions. | 19 /// - References are in scope of their definitions. | 
| 20 /// - Recursive Continuations and InvokeContinuations are marked as recursive. | 20 /// - Recursive Continuations and InvokeContinuations are marked as recursive. | 
| 21 /// - InvokeContinuations have the same arity as their target. | 21 /// - InvokeContinuations have the same arity as their target. | 
| 22 /// - Reference chains are valid doubly-linked lists. | 22 /// - Reference chains are valid doubly-linked lists. | 
| 23 /// - Reference chains contain exactly the references that are in the IR. | 23 /// - Reference chains contain exactly the references that are in the IR. | 
| 24 /// - Each definition object occurs only once in the IR (no redeclaring). | 24 /// - Each definition object occurs only once in the IR (no redeclaring). | 
| 25 /// - Each reference object occurs only once in the IR (no sharing). | 25 /// - Each reference object occurs only once in the IR (no sharing). | 
| 26 /// | 26 /// | 
| 27 class CheckCpsIntegrity extends RecursiveVisitor { | 27 class CheckCpsIntegrity extends TrampolineRecursiveVisitor { | 
| 28 | 28 | 
| 29   FunctionDefinition topLevelNode; | 29   FunctionDefinition topLevelNode; | 
|  | 30   String previousPass; | 
| 30 | 31 | 
| 31   Set<Definition> seenDefinitions = new Set<Definition>(); | 32   Set<Definition> seenDefinitions = new Set<Definition>(); | 
| 32   Map<Definition, Set<Reference>> seenReferences = | 33   Map<Definition, Set<Reference>> seenReferences = | 
| 33       <Definition, Set<Reference>>{}; | 34       <Definition, Set<Reference>>{}; | 
| 34 | 35 | 
| 35   Set<Definition> inScope = new Set<Definition>(); | 36   Set<Definition> inScope = new Set<Definition>(); | 
| 36   Set<Continuation> insideContinuations = new Set<Continuation>(); | 37   Set<Continuation> insideContinuations = new Set<Continuation>(); | 
| 37 | 38 | 
| 38   void markAsSeen(Definition def) { | 39   void markAsSeen(Definition def) { | 
| 39     if (!seenDefinitions.add(def)) { | 40     if (!seenDefinitions.add(def)) { | 
| 40       error('Redeclared $def', def); | 41       error('Redeclared $def', def); | 
| 41     } | 42     } | 
| 42     seenReferences[def] = new Set<Reference>(); | 43     seenReferences[def] = new Set<Reference>(); | 
| 43   } | 44   } | 
| 44 | 45 | 
| 45   void enterScope(Iterable<Definition> definitions) { | 46   void enterScope(Iterable<Definition> definitions) { | 
| 46     inScope.addAll(definitions); | 47     inScope.addAll(definitions); | 
| 47     pushAction(() => inScope.removeAll(definitions)); | 48     pushAction(() => inScope.removeAll(definitions)); | 
| 48   } | 49   } | 
| 49 | 50 | 
| 50   void enterContinuation(Continuation cont) { | 51   void enterContinuation(Continuation cont) { | 
| 51     insideContinuations.add(cont); | 52     insideContinuations.add(cont); | 
| 52     pushAction(() => insideContinuations.remove(cont)); | 53     pushAction(() => insideContinuations.remove(cont)); | 
| 53   } | 54   } | 
| 54 | 55 | 
| 55   void check(FunctionDefinition node) { | 56   void check(FunctionDefinition node, String previousPass) { | 
| 56     topLevelNode = node; | 57     topLevelNode = node; | 
|  | 58     this.previousPass = previousPass; | 
|  | 59     ParentChecker.checkParents(node, this); | 
| 57     visit(node); | 60     visit(node); | 
| 58     // Check for broken reference chains. We check this last, so out-of-scope | 61     // Check for broken reference chains. We check this last, so out-of-scope | 
| 59     // references are not classified as a broken reference chain. | 62     // references are not classified as a broken reference chain. | 
| 60     seenDefinitions.forEach(checkReferenceChain); | 63     seenDefinitions.forEach(checkReferenceChain); | 
| 61   } | 64   } | 
| 62 | 65 | 
| 63   @override | 66   @override | 
| 64   Expression traverseLetCont(LetCont node) { | 67   Expression traverseLetCont(LetCont node) { | 
| 65     node.continuations.forEach(markAsSeen); | 68     node.continuations.forEach(markAsSeen); | 
| 66     node.continuations.forEach(push); | 69     node.continuations.forEach(push); | 
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 174       error('Reference chain for $def contains orphaned references', def); | 177       error('Reference chain for $def contains orphaned references', def); | 
| 175     } | 178     } | 
| 176   } | 179   } | 
| 177 | 180 | 
| 178   error(String message, node) { | 181   error(String message, node) { | 
| 179     String sexpr; | 182     String sexpr; | 
| 180     if (ENABLE_DUMP) { | 183     if (ENABLE_DUMP) { | 
| 181       try { | 184       try { | 
| 182         Decorator decorator = (n, String s) => n == node ? '**$s**' : s; | 185         Decorator decorator = (n, String s) => n == node ? '**$s**' : s; | 
| 183         sexpr = new SExpressionStringifier(decorator).visit(topLevelNode); | 186         sexpr = new SExpressionStringifier(decorator).visit(topLevelNode); | 
|  | 187         sexpr = 'SExpr dump (offending node marked with **):\n\n$sexpr'; | 
| 184       } catch (e) { | 188       } catch (e) { | 
| 185         sexpr = '(Exception thrown by SExpressionStringifier: $e)'; | 189         sexpr = '(Exception thrown by SExpressionStringifier: $e)'; | 
| 186       } | 190       } | 
| 187     } else { | 191     } else { | 
| 188       sexpr = '(Set DUMP_IR flag to enable)'; | 192       sexpr = '(Set DUMP_IR flag to enable SExpr dump)'; | 
| 189     } | 193     } | 
| 190     throw 'CPS integrity violation in ${topLevelNode.element}:\n' | 194     throw 'CPS integrity violation\n' | 
|  | 195           'After $previousPass on ${topLevelNode.element}\n' | 
| 191           '$message\n\n' | 196           '$message\n\n' | 
| 192           'SExpr dump (offending node marked with **):\n\n' |  | 
| 193           '$sexpr\n'; | 197           '$sexpr\n'; | 
| 194   } | 198   } | 
|  | 199 } | 
| 195 | 200 | 
|  | 201 /// Traverses the CPS term and checks that node.parent is correctly set | 
|  | 202 /// for each visited node. | 
|  | 203 class ParentChecker extends DeepRecursiveVisitor { | 
|  | 204   static void checkParents(Node node, CheckCpsIntegrity main) { | 
|  | 205     ParentChecker visitor = new ParentChecker._make(main); | 
|  | 206     visitor._worklist.add(node); | 
|  | 207     visitor.trampoline(); | 
|  | 208   } | 
|  | 209 | 
|  | 210   ParentChecker._make(this.main); | 
|  | 211 | 
|  | 212   Node _parent; | 
|  | 213   final List<Node> _worklist = <Node>[]; | 
|  | 214   final CheckCpsIntegrity main; | 
|  | 215 | 
|  | 216   void trampoline() { | 
|  | 217     while (_worklist.isNotEmpty) { | 
|  | 218       _parent = _worklist.removeLast(); | 
|  | 219       _parent.accept(this); | 
|  | 220     } | 
|  | 221   } | 
|  | 222 | 
|  | 223   error(String message, node) => main.error(message, node); | 
|  | 224 | 
|  | 225   @override | 
|  | 226   visit(Node node) { | 
|  | 227     _worklist.add(node); | 
|  | 228     if (node.parent != _parent) { | 
|  | 229       error('Parent pointer on $node is ${node.parent} but should be $_parent', | 
|  | 230             node); | 
|  | 231     } | 
|  | 232   } | 
|  | 233 | 
|  | 234   @override | 
|  | 235   processReference(Reference node) { | 
|  | 236     if (node.parent != _parent) { | 
|  | 237       error('Parent pointer on $node is ${node.parent} but should be $_parent', | 
|  | 238             node); | 
|  | 239     } | 
|  | 240   } | 
| 196 } | 241 } | 
| OLD | NEW | 
|---|