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