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 RecursiveVisitor { |
28 | 28 |
29 RootNode topLevelNode; | 29 FunctionDefinition topLevelNode; |
30 | 30 |
31 Set<Definition> seenDefinitions = new Set<Definition>(); | 31 Set<Definition> seenDefinitions = new Set<Definition>(); |
32 Map<Definition, Set<Reference>> seenReferences = | 32 Map<Definition, Set<Reference>> seenReferences = |
33 <Definition, Set<Reference>>{}; | 33 <Definition, Set<Reference>>{}; |
34 | 34 |
35 Map<Definition, Node> bindings = <Definition, Node>{}; | 35 Map<Definition, Node> bindings = <Definition, Node>{}; |
36 Set<Continuation> insideContinuations = new Set<Continuation>(); | 36 Set<Continuation> insideContinuations = new Set<Continuation>(); |
37 | 37 |
38 doInScope(Iterable<Definition> defs, Node binding, action()) { | 38 doInScope(Iterable<Definition> defs, Node binding, action()) { |
39 for (Definition def in defs) { | 39 for (Definition def in defs) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 if (node.isReturnContinuation) { | 71 if (node.isReturnContinuation) { |
72 error('Non-return continuation missing body', node); | 72 error('Non-return continuation missing body', node); |
73 } | 73 } |
74 node.parameters.forEach(markAsSeen); | 74 node.parameters.forEach(markAsSeen); |
75 insideContinuations.add(node); | 75 insideContinuations.add(node); |
76 doInScope(node.parameters, node, () => visit(node.body)); | 76 doInScope(node.parameters, node, () => visit(node.body)); |
77 insideContinuations.remove(node); | 77 insideContinuations.remove(node); |
78 } | 78 } |
79 | 79 |
80 @override | 80 @override |
81 visitBody(Body node) { | |
82 markAsSeen(node.returnContinuation); | |
83 if (!node.returnContinuation.isReturnContinuation) { | |
84 error('Return continuation with a body', node); | |
85 } | |
86 doInScope([node.returnContinuation], node, () => visit(node.body)); | |
87 } | |
88 | |
89 @override | |
90 visitLetPrim(LetPrim node) { | 81 visitLetPrim(LetPrim node) { |
91 markAsSeen(node.primitive); | 82 markAsSeen(node.primitive); |
92 visit(node.primitive); | 83 visit(node.primitive); |
93 doInScope([node.primitive], node, () => visit(node.body)); | 84 doInScope([node.primitive], node, () => visit(node.body)); |
94 } | 85 } |
95 | 86 |
96 @override | 87 @override |
97 visitLetMutable(LetMutable node) { | 88 visitLetMutable(LetMutable node) { |
98 markAsSeen(node.variable); | 89 markAsSeen(node.variable); |
99 processReference(node.value); | 90 processReference(node.value); |
100 doInScope([node.variable], node, () => visit(node.body)); | 91 doInScope([node.variable], node, () => visit(node.body)); |
101 } | 92 } |
102 | 93 |
103 @override | 94 @override |
104 visitFunctionDefinition(FunctionDefinition node) { | 95 visitFunctionDefinition(FunctionDefinition node) { |
105 if (node.thisParameter != null) { | 96 if (node.thisParameter != null) { |
106 markAsSeen(node.thisParameter); | 97 markAsSeen(node.thisParameter); |
107 } | 98 } |
108 node.parameters.forEach(markAsSeen); | 99 node.parameters.forEach(markAsSeen); |
109 if (node.body != null) { | 100 markAsSeen(node.returnContinuation); |
110 doInOptionalScope(node.thisParameter, node, | 101 if (!node.returnContinuation.isReturnContinuation) { |
111 () => doInScope(node.parameters, node, () => visit(node.body))); | 102 error('Return continuation with a body', node); |
112 } | 103 } |
113 } | 104 doInOptionalScope(node.thisParameter, node, |
114 | 105 () => doInScope(node.parameters, node, |
115 @override | 106 () => doInScope([node.returnContinuation], node, |
116 visitConstructorDefinition(ConstructorDefinition node) { | 107 () => visit(node.body)))); |
117 if (node.thisParameter != null) { | |
118 markAsSeen(node.thisParameter); | |
119 } | |
120 node.parameters.forEach(markAsSeen); | |
121 doInScope(node.parameters, node, () { | |
122 if (node.initializers != null) node.initializers.forEach(visit); | |
123 if (node.body != null) { | |
124 doInOptionalScope(node.thisParameter, node, () => visit(node.body)); | |
125 } | |
126 }); | |
127 } | 108 } |
128 | 109 |
129 doInOptionalScope(Parameter parameter, Node node, action) { | 110 doInOptionalScope(Parameter parameter, Node node, action) { |
130 return (parameter == null) | 111 return (parameter == null) |
131 ? action() | 112 ? action() |
132 : doInScope([parameter], node, action); | 113 : doInScope([parameter], node, action); |
133 } | 114 } |
134 | 115 |
135 @override | 116 @override |
136 visitDeclareFunction(DeclareFunction node) { | |
137 markAsSeen(node.variable); | |
138 doInScope([node.variable], node, () { | |
139 visit(node.definition); | |
140 visit(node.body); | |
141 }); | |
142 } | |
143 | |
144 @override | |
145 processReference(Reference reference) { | 117 processReference(Reference reference) { |
146 if (!bindings.containsKey(reference.definition)) { | 118 if (!bindings.containsKey(reference.definition)) { |
147 error('Referenced out of scope: ${reference.definition}', reference); | 119 error('Referenced out of scope: ${reference.definition}', reference); |
148 } | 120 } |
149 if (!seenReferences[reference.definition].add(reference)) { | 121 if (!seenReferences[reference.definition].add(reference)) { |
150 error('Duplicate use of Reference to ${reference.definition}', reference); | 122 error('Duplicate use of Reference to ${reference.definition}', reference); |
151 } | 123 } |
152 } | 124 } |
153 | 125 |
154 @override | 126 @override |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 } | 174 } |
203 } else { | 175 } else { |
204 sexpr = '(Set DUMP_IR flag to enable)'; | 176 sexpr = '(Set DUMP_IR flag to enable)'; |
205 } | 177 } |
206 throw 'CPS integrity violation in ${topLevelNode.element}:\n' | 178 throw 'CPS integrity violation in ${topLevelNode.element}:\n' |
207 '$message\n\n' | 179 '$message\n\n' |
208 'SExpr dump (offending node marked with **):\n\n' | 180 'SExpr dump (offending node marked with **):\n\n' |
209 '$sexpr\n'; | 181 '$sexpr\n'; |
210 } | 182 } |
211 | 183 |
212 void check(RootNode node) { | 184 void check(FunctionDefinition node) { |
213 topLevelNode = node; | 185 topLevelNode = node; |
214 visit(node); | 186 visit(node); |
215 | 187 |
216 // Check this last, so out-of-scope references are not classified as | 188 // Check this last, so out-of-scope references are not classified as |
217 // a broken reference chain. | 189 // a broken reference chain. |
218 seenDefinitions.forEach(checkReferenceChain); | 190 seenDefinitions.forEach(checkReferenceChain); |
219 } | 191 } |
220 | 192 |
221 } | 193 } |
OLD | NEW |