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. |
(...skipping 14 matching lines...) Expand all Loading... |
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 FunctionDefinition 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 Set<Definition> inScope = new Set<Definition>(); |
36 Set<Continuation> insideContinuations = new Set<Continuation>(); | 36 Set<Continuation> insideContinuations = new Set<Continuation>(); |
37 | 37 |
38 doInScope(Iterable<Definition> defs, Node binding, action()) { | |
39 for (Definition def in defs) { | |
40 bindings[def] = binding; | |
41 } | |
42 action(); | |
43 for (Definition def in defs) { | |
44 bindings.remove(def); | |
45 } | |
46 } | |
47 | |
48 void markAsSeen(Definition def) { | 38 void markAsSeen(Definition def) { |
49 if (!seenDefinitions.add(def)) { | 39 if (!seenDefinitions.add(def)) { |
50 error('Redeclared $def', def); | 40 error('Redeclared $def', def); |
51 } | 41 } |
52 seenReferences[def] = new Set<Reference>(); | 42 seenReferences[def] = new Set<Reference>(); |
53 } | 43 } |
54 | 44 |
55 @override | 45 void enterScope(Iterable<Definition> definitions) { |
56 visitLetCont(LetCont node) { | 46 inScope.addAll(definitions); |
57 // Analyze each continuation separately without the others in scope. | 47 pushAction(() => inScope.removeAll(definitions)); |
58 for (Continuation continuation in node.continuations) { | 48 } |
59 // We always consider a continuation to be in scope of itself. | 49 |
60 // The isRecursive flag is checked explicitly to give more useful | 50 void enterContinuation(Continuation cont) { |
61 // error messages. | 51 insideContinuations.add(cont); |
62 doInScope([continuation], node, () => visit(continuation)); | 52 pushAction(() => insideContinuations.remove(cont)); |
63 } | 53 } |
64 // Analyze the body with all continuations in scope. | 54 |
65 doInScope(node.continuations, node, () => visit(node.body)); | 55 void check(FunctionDefinition node) { |
| 56 topLevelNode = node; |
| 57 visit(node); |
| 58 // Check for broken reference chains. We check this last, so out-of-scope |
| 59 // references are not classified as a broken reference chain. |
| 60 seenDefinitions.forEach(checkReferenceChain); |
66 } | 61 } |
67 | 62 |
68 @override | 63 @override |
69 visitContinuation(Continuation node) { | 64 Expression traverseLetCont(LetCont node) { |
70 markAsSeen(node); | 65 node.continuations.forEach(markAsSeen); |
71 if (node.isReturnContinuation) { | 66 node.continuations.forEach(push); |
72 error('Non-return continuation missing body', node); | 67 |
73 } | 68 // Put all continuations in scope when visiting the body. |
74 node.parameters.forEach(markAsSeen); | 69 enterScope(node.continuations); |
75 insideContinuations.add(node); | 70 |
76 doInScope(node.parameters, node, () => visit(node.body)); | 71 return node.body; |
77 insideContinuations.remove(node); | |
78 } | 72 } |
79 | 73 |
80 @override | 74 @override |
81 visitLetPrim(LetPrim node) { | 75 Expression traverseLetPrim(LetPrim node) { |
82 markAsSeen(node.primitive); | 76 markAsSeen(node.primitive); |
| 77 |
| 78 // Process references in the primitive. |
83 visit(node.primitive); | 79 visit(node.primitive); |
84 doInScope([node.primitive], node, () => visit(node.body)); | 80 |
| 81 // Put the primitive in scope when visiting the body. |
| 82 enterScope([node.primitive]); |
| 83 |
| 84 return node.body; |
85 } | 85 } |
86 | 86 |
87 @override | 87 @override |
88 visitLetMutable(LetMutable node) { | 88 Expression traverseLetMutable(LetMutable node) { |
89 markAsSeen(node.variable); | 89 markAsSeen(node.variable); |
90 processReference(node.value); | 90 processReference(node.value); |
91 doInScope([node.variable], node, () => visit(node.body)); | 91 |
| 92 // Put the primitive in scope when visiting the body. |
| 93 enterScope([node.variable]); |
| 94 |
| 95 return node.body; |
| 96 } |
| 97 |
| 98 @override |
| 99 Expression traverseContinuation(Continuation cont) { |
| 100 if (cont.isReturnContinuation) { |
| 101 error('Non-return continuation missing body', cont); |
| 102 } |
| 103 cont.parameters.forEach(markAsSeen); |
| 104 enterScope(cont.parameters); |
| 105 // Put every continuation in scope at its own body. The isRecursive |
| 106 // flag is checked explicitly using [insideContinuations]. |
| 107 enterScope([cont]); |
| 108 enterContinuation(cont); |
| 109 return cont.body; |
92 } | 110 } |
93 | 111 |
94 @override | 112 @override |
95 visitFunctionDefinition(FunctionDefinition node) { | 113 visitFunctionDefinition(FunctionDefinition node) { |
96 if (node.thisParameter != null) { | 114 if (node.thisParameter != null) { |
97 markAsSeen(node.thisParameter); | 115 markAsSeen(node.thisParameter); |
| 116 enterScope([node.thisParameter]); |
98 } | 117 } |
99 node.parameters.forEach(markAsSeen); | 118 node.parameters.forEach(markAsSeen); |
| 119 enterScope(node.parameters); |
100 markAsSeen(node.returnContinuation); | 120 markAsSeen(node.returnContinuation); |
| 121 enterScope([node.returnContinuation]); |
101 if (!node.returnContinuation.isReturnContinuation) { | 122 if (!node.returnContinuation.isReturnContinuation) { |
102 error('Return continuation with a body', node); | 123 error('Return continuation with a body', node); |
103 } | 124 } |
104 doInOptionalScope(node.thisParameter, node, | 125 visit(node.body); |
105 () => doInScope(node.parameters, node, | |
106 () => doInScope([node.returnContinuation], node, | |
107 () => visit(node.body)))); | |
108 } | |
109 | |
110 doInOptionalScope(Parameter parameter, Node node, action) { | |
111 return (parameter == null) | |
112 ? action() | |
113 : doInScope([parameter], node, action); | |
114 } | 126 } |
115 | 127 |
116 @override | 128 @override |
117 processReference(Reference reference) { | 129 processReference(Reference reference) { |
118 if (!bindings.containsKey(reference.definition)) { | 130 if (!inScope.contains(reference.definition)) { |
119 error('Referenced out of scope: ${reference.definition}', reference); | 131 error('Referenced out of scope: ${reference.definition}', reference); |
120 } | 132 } |
121 if (!seenReferences[reference.definition].add(reference)) { | 133 if (!seenReferences[reference.definition].add(reference)) { |
122 error('Duplicate use of Reference to ${reference.definition}', reference); | 134 error('Duplicate use of Reference to ${reference.definition}', reference); |
123 } | 135 } |
124 } | 136 } |
125 | 137 |
126 @override | 138 @override |
127 processInvokeContinuation(InvokeContinuation node) { | 139 processInvokeContinuation(InvokeContinuation node) { |
128 Continuation target = node.continuation.definition; | 140 Continuation target = node.continuation.definition; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 } | 186 } |
175 } else { | 187 } else { |
176 sexpr = '(Set DUMP_IR flag to enable)'; | 188 sexpr = '(Set DUMP_IR flag to enable)'; |
177 } | 189 } |
178 throw 'CPS integrity violation in ${topLevelNode.element}:\n' | 190 throw 'CPS integrity violation in ${topLevelNode.element}:\n' |
179 '$message\n\n' | 191 '$message\n\n' |
180 'SExpr dump (offending node marked with **):\n\n' | 192 'SExpr dump (offending node marked with **):\n\n' |
181 '$sexpr\n'; | 193 '$sexpr\n'; |
182 } | 194 } |
183 | 195 |
184 void check(FunctionDefinition node) { | |
185 topLevelNode = node; | |
186 visit(node); | |
187 | |
188 // Check this last, so out-of-scope references are not classified as | |
189 // a broken reference chain. | |
190 seenDefinitions.forEach(checkReferenceChain); | |
191 } | |
192 | |
193 } | 196 } |
OLD | NEW |