| 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 |