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 |