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 TrampolineRecursiveVisitor { | 27 class CheckCpsIntegrity extends RecursiveVisitor { |
28 | 28 |
29 FunctionDefinition topLevelNode; | 29 FunctionDefinition topLevelNode; |
30 String previousPass; | |
31 | 30 |
32 Set<Definition> seenDefinitions = new Set<Definition>(); | 31 Set<Definition> seenDefinitions = new Set<Definition>(); |
33 Map<Definition, Set<Reference>> seenReferences = | 32 Map<Definition, Set<Reference>> seenReferences = |
34 <Definition, Set<Reference>>{}; | 33 <Definition, Set<Reference>>{}; |
35 | 34 |
36 Set<Definition> inScope = new Set<Definition>(); | 35 Set<Definition> inScope = new Set<Definition>(); |
37 Set<Continuation> insideContinuations = new Set<Continuation>(); | 36 Set<Continuation> insideContinuations = new Set<Continuation>(); |
38 | 37 |
39 void markAsSeen(Definition def) { | 38 void markAsSeen(Definition def) { |
40 if (!seenDefinitions.add(def)) { | 39 if (!seenDefinitions.add(def)) { |
41 error('Redeclared $def', def); | 40 error('Redeclared $def', def); |
42 } | 41 } |
43 seenReferences[def] = new Set<Reference>(); | 42 seenReferences[def] = new Set<Reference>(); |
44 } | 43 } |
45 | 44 |
46 void enterScope(Iterable<Definition> definitions) { | 45 void enterScope(Iterable<Definition> definitions) { |
47 inScope.addAll(definitions); | 46 inScope.addAll(definitions); |
48 pushAction(() => inScope.removeAll(definitions)); | 47 pushAction(() => inScope.removeAll(definitions)); |
49 } | 48 } |
50 | 49 |
51 void enterContinuation(Continuation cont) { | 50 void enterContinuation(Continuation cont) { |
52 insideContinuations.add(cont); | 51 insideContinuations.add(cont); |
53 pushAction(() => insideContinuations.remove(cont)); | 52 pushAction(() => insideContinuations.remove(cont)); |
54 } | 53 } |
55 | 54 |
56 void check(FunctionDefinition node, String previousPass) { | 55 void check(FunctionDefinition node) { |
57 topLevelNode = node; | 56 topLevelNode = node; |
58 this.previousPass = previousPass; | |
59 ParentChecker.checkParents(node, this); | |
60 visit(node); | 57 visit(node); |
61 // Check for broken reference chains. We check this last, so out-of-scope | 58 // Check for broken reference chains. We check this last, so out-of-scope |
62 // references are not classified as a broken reference chain. | 59 // references are not classified as a broken reference chain. |
63 seenDefinitions.forEach(checkReferenceChain); | 60 seenDefinitions.forEach(checkReferenceChain); |
64 } | 61 } |
65 | 62 |
66 @override | 63 @override |
67 Expression traverseLetCont(LetCont node) { | 64 Expression traverseLetCont(LetCont node) { |
68 node.continuations.forEach(markAsSeen); | 65 node.continuations.forEach(markAsSeen); |
69 node.continuations.forEach(push); | 66 node.continuations.forEach(push); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 error('Reference chain for $def contains orphaned references', def); | 174 error('Reference chain for $def contains orphaned references', def); |
178 } | 175 } |
179 } | 176 } |
180 | 177 |
181 error(String message, node) { | 178 error(String message, node) { |
182 String sexpr; | 179 String sexpr; |
183 if (ENABLE_DUMP) { | 180 if (ENABLE_DUMP) { |
184 try { | 181 try { |
185 Decorator decorator = (n, String s) => n == node ? '**$s**' : s; | 182 Decorator decorator = (n, String s) => n == node ? '**$s**' : s; |
186 sexpr = new SExpressionStringifier(decorator).visit(topLevelNode); | 183 sexpr = new SExpressionStringifier(decorator).visit(topLevelNode); |
187 sexpr = 'SExpr dump (offending node marked with **):\n\n$sexpr'; | |
188 } catch (e) { | 184 } catch (e) { |
189 sexpr = '(Exception thrown by SExpressionStringifier: $e)'; | 185 sexpr = '(Exception thrown by SExpressionStringifier: $e)'; |
190 } | 186 } |
191 } else { | 187 } else { |
192 sexpr = '(Set DUMP_IR flag to enable SExpr dump)'; | 188 sexpr = '(Set DUMP_IR flag to enable)'; |
193 } | 189 } |
194 throw 'CPS integrity violation\n' | 190 throw 'CPS integrity violation in ${topLevelNode.element}:\n' |
195 'After $previousPass on ${topLevelNode.element}\n' | |
196 '$message\n\n' | 191 '$message\n\n' |
| 192 'SExpr dump (offending node marked with **):\n\n' |
197 '$sexpr\n'; | 193 '$sexpr\n'; |
198 } | 194 } |
| 195 |
199 } | 196 } |
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 } | |
241 } | |
OLD | NEW |