| OLD | NEW |
| (Empty) | |
| 1 library tree_ir.integrity; |
| 2 |
| 3 import 'tree_ir_nodes.dart'; |
| 4 |
| 5 /// Performs integrity checks on the tree_ir. |
| 6 /// |
| 7 /// Should only be run for debugging purposes, not in production. |
| 8 /// |
| 9 /// - Reference counts on variables and labels must match be actual |
| 10 /// number of references. |
| 11 /// - Labels must be in scope when referenced. |
| 12 /// - Breaks must target a [LabeledStatement]. |
| 13 /// - Continues must target a [Loop]. |
| 14 /// |
| 15 class CheckTreeIntegrity extends RecursiveVisitor { |
| 16 ExecutableDefinition topLevelNode; |
| 17 |
| 18 Map<Variable, int> varReads = <Variable, int>{}; |
| 19 Map<Variable, int> varWrites = <Variable, int>{}; |
| 20 Map<Label, int> labelUses = <Label, int>{}; |
| 21 Map<Label, JumpTarget> label2declaration = <Label, JumpTarget>{}; |
| 22 |
| 23 void write(Variable variable) { |
| 24 varWrites.putIfAbsent(variable, () => 0); |
| 25 varWrites[variable]++; |
| 26 } |
| 27 |
| 28 void read(Variable variable) { |
| 29 varReads.putIfAbsent(variable, () => 0); |
| 30 varReads[variable]++; |
| 31 } |
| 32 |
| 33 visitVariableUse(VariableUse node) { |
| 34 read(node.variable); |
| 35 } |
| 36 |
| 37 visitAssign(Assign node) { |
| 38 write(node.variable); |
| 39 visitExpression(node.definition); |
| 40 visitStatement(node.next); |
| 41 } |
| 42 |
| 43 visitTry(Try node) { |
| 44 node.catchParameters.forEach(write); |
| 45 visitStatement(node.tryBody); |
| 46 visitStatement(node.catchBody); |
| 47 } |
| 48 |
| 49 visitFunctionDefinition(FunctionDefinition node) { |
| 50 node.parameters.forEach(write); |
| 51 if (node.body != null) visitStatement(node.body); |
| 52 } |
| 53 |
| 54 visitFunctionDeclaration(FunctionDeclaration node) { |
| 55 write(node.variable); |
| 56 visitFunctionDefinition(node.definition); |
| 57 visitStatement(node.next); |
| 58 } |
| 59 |
| 60 visitJumpTargetBody(JumpTarget target) { |
| 61 Label label = target.label; |
| 62 if (label2declaration.containsKey(label)) { |
| 63 error('Duplicate declaration of label $label'); |
| 64 } |
| 65 label2declaration[label] = target; |
| 66 labelUses[label] = 0; |
| 67 visitStatement(target.body); |
| 68 label2declaration.remove(target); |
| 69 |
| 70 if (labelUses[label] != label.useCount) { |
| 71 error('Label $label has ${labelUses[label]} uses ' |
| 72 'but its reference count is ${label.useCount}'); |
| 73 } |
| 74 } |
| 75 |
| 76 visitLabeledStatement(LabeledStatement node) { |
| 77 visitJumpTargetBody(node); |
| 78 visitStatement(node.next); |
| 79 } |
| 80 |
| 81 visitWhileTrue(WhileTrue node) { |
| 82 visitJumpTargetBody(node); |
| 83 } |
| 84 |
| 85 visitWhileCondition(WhileCondition node) { |
| 86 visitExpression(node.condition); |
| 87 visitJumpTargetBody(node); |
| 88 visitStatement(node.next); |
| 89 } |
| 90 |
| 91 visitBreak(Break node) { |
| 92 if (!label2declaration.containsKey(node.target)) { |
| 93 error('Break to label that is not in scope'); |
| 94 } |
| 95 if (label2declaration[node.target] is! LabeledStatement) { |
| 96 error('Break to non-labeled statement ${label2declaration[node.target]}'); |
| 97 } |
| 98 labelUses[node.target]++; |
| 99 } |
| 100 |
| 101 visitContinue(Continue node) { |
| 102 if (!label2declaration.containsKey(node.target)) { |
| 103 error('Continue to label that is not in scope'); |
| 104 } |
| 105 if (label2declaration[node.target] is! Loop) { |
| 106 error('Continue to non-loop statement ${label2declaration[node.target]}'); |
| 107 } |
| 108 labelUses[node.target]++; |
| 109 } |
| 110 |
| 111 dynamic error(String message) { |
| 112 throw 'Tree IR integrity violation in ${topLevelNode.element}:\n$message'; |
| 113 } |
| 114 |
| 115 void check(ExecutableDefinition node) { |
| 116 topLevelNode = node; |
| 117 visitExecutableDefinition(node); |
| 118 |
| 119 // Verify reference counters for all variables. |
| 120 List<Variable> seenVariables = new List<Variable>(); |
| 121 seenVariables.addAll(varReads.keys); |
| 122 seenVariables.addAll(varWrites.keys); |
| 123 for (Variable variable in seenVariables) { |
| 124 int reads = varReads.putIfAbsent(variable, () => 0); |
| 125 int writes = varWrites.putIfAbsent(variable, () => 0); |
| 126 if (reads != variable.readCount || writes != variable.writeCount) { |
| 127 error('Invalid reference count for ${variable.element}:\n' |
| 128 '- Variable has $reads reads and $writes writes\n' |
| 129 '- Reference count is ${variable.readCount} reads and ' |
| 130 '${variable.writeCount} writes'); |
| 131 } |
| 132 } |
| 133 } |
| 134 |
| 135 } |
| 136 |
| OLD | NEW |