| OLD | NEW |
| 1 library tree_ir.integrity; | 1 library tree_ir.integrity; |
| 2 | 2 |
| 3 import 'tree_ir_nodes.dart'; | 3 import 'tree_ir_nodes.dart'; |
| 4 | 4 |
| 5 /// Performs integrity checks on the tree_ir. | 5 /// Performs integrity checks on the tree_ir. |
| 6 /// | 6 /// |
| 7 /// Should only be run for debugging purposes, not in production. | 7 /// Should only be run for debugging purposes, not in production. |
| 8 /// | 8 /// |
| 9 /// - Reference counts on must match the actual number of references. | 9 /// - Reference counts on must match the actual number of references. |
| 10 /// - Labels must be in scope when referenced. | 10 /// - Labels must be in scope when referenced. |
| 11 /// - Breaks must target a [LabeledStatement]. | 11 /// - Breaks must target a [LabeledStatement]. |
| 12 /// - Continues must target a [Loop]. | 12 /// - Continues must target a [Loop]. |
| 13 /// - Variables must only be used after their first assignment | 13 /// - Variables must only be used after their first assignment |
| 14 /// (checked on a best-effort basis). | 14 /// (checked on a best-effort basis). |
| 15 /// - Variables with a declaration must only be referenced in scope. | 15 /// - Variables with a declaration must only be referenced in scope. |
| 16 /// - Variables must not have more than one declaration. | 16 /// - Variables must not have more than one declaration. |
| 17 /// | 17 /// |
| 18 class CheckTreeIntegrity extends RecursiveVisitor { | 18 class CheckTreeIntegrity extends RecursiveVisitor { |
| 19 RootNode topLevelNode; | 19 FunctionDefinition topLevelNode; |
| 20 | 20 |
| 21 Map<Variable, int> varReads = <Variable, int>{}; | 21 Map<Variable, int> varReads = <Variable, int>{}; |
| 22 Map<Variable, int> varWrites = <Variable, int>{}; | 22 Map<Variable, int> varWrites = <Variable, int>{}; |
| 23 Map<Label, int> labelUses = <Label, int>{}; | 23 Map<Label, int> labelUses = <Label, int>{}; |
| 24 Map<Label, JumpTarget> label2declaration = <Label, JumpTarget>{}; | 24 Map<Label, JumpTarget> label2declaration = <Label, JumpTarget>{}; |
| 25 | 25 |
| 26 /// Variables that are currently in scope. | 26 /// Variables that are currently in scope. |
| 27 Set<Variable> scope = new Set<Variable>(); | 27 Set<Variable> scope = new Set<Variable>(); |
| 28 | 28 |
| 29 /// Variables for which we have seen a declaration. | 29 /// Variables for which we have seen a declaration. |
| (...skipping 28 matching lines...) Expand all Loading... |
| 58 } | 58 } |
| 59 | 59 |
| 60 void undeclare(Variable variable) { | 60 void undeclare(Variable variable) { |
| 61 scope.remove(variable); | 61 scope.remove(variable); |
| 62 } | 62 } |
| 63 | 63 |
| 64 visitVariableUse(VariableUse node) { | 64 visitVariableUse(VariableUse node) { |
| 65 read(node.variable); | 65 read(node.variable); |
| 66 } | 66 } |
| 67 | 67 |
| 68 visitVariableDeclaration(VariableDeclaration node) { | |
| 69 visitExpression(node.value); | |
| 70 declare(node.variable); | |
| 71 visitStatement(node.next); | |
| 72 undeclare(node.variable); | |
| 73 } | |
| 74 | |
| 75 visitAssign(Assign node) { | 68 visitAssign(Assign node) { |
| 76 visitExpression(node.value); | 69 visitExpression(node.value); |
| 77 write(node.variable); | 70 write(node.variable); |
| 78 } | 71 } |
| 79 | 72 |
| 80 visitTry(Try node) { | 73 visitTry(Try node) { |
| 81 visitStatement(node.tryBody); | 74 visitStatement(node.tryBody); |
| 82 node.catchParameters.forEach(declare); | 75 node.catchParameters.forEach(declare); |
| 83 visitStatement(node.catchBody); | 76 visitStatement(node.catchBody); |
| 84 node.catchParameters.forEach(undeclare); | 77 node.catchParameters.forEach(undeclare); |
| 85 } | 78 } |
| 86 | 79 |
| 87 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 88 declare(node.variable); | |
| 89 checkBody(node.definition); | |
| 90 visitStatement(node.next); | |
| 91 undeclare(node.variable); | |
| 92 if (varWrites[node.variable] > 1) { | |
| 93 error('Assignment to function declaration ${node.variable}'); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 visitJumpTargetBody(JumpTarget target) { | 80 visitJumpTargetBody(JumpTarget target) { |
| 98 Label label = target.label; | 81 Label label = target.label; |
| 99 if (label2declaration.containsKey(label)) { | 82 if (label2declaration.containsKey(label)) { |
| 100 error('Duplicate declaration of label $label'); | 83 error('Duplicate declaration of label $label'); |
| 101 } | 84 } |
| 102 label2declaration[label] = target; | 85 label2declaration[label] = target; |
| 103 labelUses[label] = 0; | 86 labelUses[label] = 0; |
| 104 visitStatement(target.body); | 87 visitStatement(target.body); |
| 105 label2declaration.remove(target); | 88 label2declaration.remove(target); |
| 106 | 89 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 if (label2declaration[node.target] is! Loop) { | 125 if (label2declaration[node.target] is! Loop) { |
| 143 error('Continue to non-loop statement ${label2declaration[node.target]}'); | 126 error('Continue to non-loop statement ${label2declaration[node.target]}'); |
| 144 } | 127 } |
| 145 labelUses[node.target]++; | 128 labelUses[node.target]++; |
| 146 } | 129 } |
| 147 | 130 |
| 148 visitInnerFunction(FunctionDefinition node) { | 131 visitInnerFunction(FunctionDefinition node) { |
| 149 checkBody(node); | 132 checkBody(node); |
| 150 } | 133 } |
| 151 | 134 |
| 152 void checkBody(RootNode node) { | 135 void checkBody(FunctionDefinition node) { |
| 153 node.parameters.forEach(declare); | 136 node.parameters.forEach(declare); |
| 154 node.forEachBody(visitStatement); | 137 visitStatement(node.body); |
| 155 node.parameters.forEach(undeclare); | 138 node.parameters.forEach(undeclare); |
| 156 } | 139 } |
| 157 | 140 |
| 158 dynamic error(String message) { | 141 dynamic error(String message) { |
| 159 throw 'Tree IR integrity violation in ${topLevelNode.element}:\n$message'; | 142 throw 'Tree IR integrity violation in ${topLevelNode.element}:\n$message'; |
| 160 } | 143 } |
| 161 | 144 |
| 162 void check(RootNode node) { | 145 void check(FunctionDefinition node) { |
| 163 topLevelNode = node; | 146 topLevelNode = node; |
| 164 checkBody(node); | 147 checkBody(node); |
| 165 | 148 |
| 166 // Verify reference counters for all variables. | 149 // Verify reference counters for all variables. |
| 167 List<Variable> seenVariables = new List<Variable>(); | 150 List<Variable> seenVariables = new List<Variable>(); |
| 168 seenVariables.addAll(varReads.keys); | 151 seenVariables.addAll(varReads.keys); |
| 169 seenVariables.addAll(varWrites.keys); | 152 seenVariables.addAll(varWrites.keys); |
| 170 for (Variable variable in seenVariables) { | 153 for (Variable variable in seenVariables) { |
| 171 int reads = varReads.putIfAbsent(variable, () => 0); | 154 int reads = varReads.putIfAbsent(variable, () => 0); |
| 172 int writes = varWrites.putIfAbsent(variable, () => 0); | 155 int writes = varWrites.putIfAbsent(variable, () => 0); |
| 173 if (reads != variable.readCount || writes != variable.writeCount) { | 156 if (reads != variable.readCount || writes != variable.writeCount) { |
| 174 error('Invalid reference count for $variable:\n' | 157 error('Invalid reference count for $variable:\n' |
| 175 '- Variable has $reads reads and $writes writes\n' | 158 '- Variable has $reads reads and $writes writes\n' |
| 176 '- Reference count is ${variable.readCount} reads and ' | 159 '- Reference count is ${variable.readCount} reads and ' |
| 177 '${variable.writeCount} writes'); | 160 '${variable.writeCount} writes'); |
| 178 } | 161 } |
| 179 } | 162 } |
| 180 } | 163 } |
| 181 | 164 |
| 182 } | 165 } |
| OLD | NEW |