Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(385)

Unified Diff: pkg/compiler/lib/src/tree_ir/tree_ir_integrity.dart

Issue 981523002: Integrity checker for CPS and Tree IR. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Stronger checks on Tree IR and update status file Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/tree_ir/tree_ir_integrity.dart
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_integrity.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_integrity.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f7aa571be7d496d2b6be72fcedd93dd378656f15
--- /dev/null
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_integrity.dart
@@ -0,0 +1,187 @@
+library tree_ir.integrity;
+
+import 'tree_ir_nodes.dart';
+
+/// Performs integrity checks on the tree_ir.
+///
+/// Should only be run for debugging purposes, not in production.
+///
+/// - Reference counts on must match the actual number of references.
+/// - Labels must be in scope when referenced.
+/// - Breaks must target a [LabeledStatement].
+/// - Continues must target a [Loop].
+/// - Variables must only be used after their first assignment
+/// (checked on a best-effort basis).
+/// - Variables with a declaration must only be referenced in scope.
+/// - Variables must not have more than one declaration.
+///
+class CheckTreeIntegrity extends RecursiveVisitor {
+ ExecutableDefinition topLevelNode;
+
+ Map<Variable, int> varReads = <Variable, int>{};
+ Map<Variable, int> varWrites = <Variable, int>{};
+ Map<Label, int> labelUses = <Label, int>{};
+ Map<Label, JumpTarget> label2declaration = <Label, JumpTarget>{};
+
+ /// Variables that are currently in scope.
+ Set<Variable> scope = new Set<Variable>();
+
+ /// Variables for which we have seen a declaration.
+ Set<Variable> seenDeclaration = new Set<Variable>();
+
+ void write(Variable variable) {
+ if (!seenDeclaration.contains(variable)) {
+ // Implicitly-declared variables are in scope after the first assignment.
+ scope.add(variable);
+ } else if (!scope.contains(variable)) {
+ // There is a declaration for variable but it is no longer in scope.
+ error('$variable assigned out of scope');
+ }
+ varWrites.putIfAbsent(variable, () => 0);
+ varWrites[variable]++;
+ }
+
+ void read(Variable variable) {
+ if (!scope.contains(variable)) {
+ error('$variable used out of scope');
+ }
+ varReads.putIfAbsent(variable, () => 0);
+ varReads[variable]++;
+ }
+
+ void declare(Variable variable) {
+ if (!scope.add(variable) || !seenDeclaration.add(variable)) {
+ error('Redeclared $variable');
+ }
+ varWrites.putIfAbsent(variable, () => 0);
+ varWrites[variable]++;
+ }
+
+ void undeclare(Variable variable) {
+ scope.remove(variable);
+ }
+
+ visitVariableUse(VariableUse node) {
+ read(node.variable);
+ }
+
+ visitAssign(Assign node) {
+ visitExpression(node.definition);
+ if (node.isDeclaration) {
+ declare(node.variable);
+ } else {
+ write(node.variable);
+ }
+ visitStatement(node.next);
+ if (node.isDeclaration) {
+ undeclare(node.variable);
+ }
+ }
+
+ visitTry(Try node) {
+ visitStatement(node.tryBody);
+ node.catchParameters.forEach(declare);
+ visitStatement(node.catchBody);
+ node.catchParameters.forEach(undeclare);
+ }
+
+ visitFunctionDefinition(FunctionDefinition node) {
+ node.parameters.forEach(declare);
+ if (node.body != null) visitStatement(node.body);
+ node.parameters.forEach(undeclare);
+ }
+
+ visitConstructorDefinition(ConstructorDefinition node) {
+ node.parameters.forEach(declare);
+ if (node.initializers != null) node.initializers.forEach(visitInitializer);
+ if (node.body != null) visitStatement(node.body);
+ node.parameters.forEach(undeclare);
+ }
+
+ visitFunctionDeclaration(FunctionDeclaration node) {
+ declare(node.variable);
+ visitFunctionDefinition(node.definition);
+ visitStatement(node.next);
+ undeclare(node.variable);
+ if (varWrites[node.variable] > 1) {
+ error('Assignment to function declaration ${node.variable}');
+ }
+ }
+
+ visitJumpTargetBody(JumpTarget target) {
+ Label label = target.label;
+ if (label2declaration.containsKey(label)) {
+ error('Duplicate declaration of label $label');
+ }
+ label2declaration[label] = target;
+ labelUses[label] = 0;
+ visitStatement(target.body);
+ label2declaration.remove(target);
+
+ if (labelUses[label] != label.useCount) {
+ error('Label $label has ${labelUses[label]} uses '
+ 'but its reference count is ${label.useCount}');
+ }
+ }
+
+ visitLabeledStatement(LabeledStatement node) {
+ visitJumpTargetBody(node);
+ visitStatement(node.next);
+ }
+
+ visitWhileTrue(WhileTrue node) {
+ visitJumpTargetBody(node);
+ }
+
+ visitWhileCondition(WhileCondition node) {
+ visitExpression(node.condition);
+ visitJumpTargetBody(node);
+ visitStatement(node.next);
+ }
+
+ visitBreak(Break node) {
+ if (!label2declaration.containsKey(node.target)) {
+ error('Break to label that is not in scope');
+ }
+ if (label2declaration[node.target] is! LabeledStatement) {
+ error('Break to non-labeled statement ${label2declaration[node.target]}');
+ }
+ labelUses[node.target]++;
+ }
+
+ visitContinue(Continue node) {
+ if (!label2declaration.containsKey(node.target)) {
+ error('Continue to label that is not in scope');
+ }
+ if (label2declaration[node.target] is! Loop) {
+ error('Continue to non-loop statement ${label2declaration[node.target]}');
+ }
+ labelUses[node.target]++;
+ }
+
+ dynamic error(String message) {
+ throw 'Tree IR integrity violation in ${topLevelNode.element}:\n$message';
+ }
+
+ void check(ExecutableDefinition node) {
+ topLevelNode = node;
+ visitExecutableDefinition(node);
+
+ // Verify reference counters for all variables.
+ List<Variable> seenVariables = new List<Variable>();
+ seenVariables.addAll(varReads.keys);
+ seenVariables.addAll(varWrites.keys);
+ for (Variable variable in seenVariables) {
+ int reads = varReads.putIfAbsent(variable, () => 0);
+ int writes = varWrites.putIfAbsent(variable, () => 0);
+ if (reads != variable.readCount || writes != variable.writeCount) {
+ error('Invalid reference count for $variable:\n'
+ '- Variable has $reads reads and $writes writes\n'
+ '- Reference count is ${variable.readCount} reads and '
+ '${variable.writeCount} writes');
+ }
+ }
+ }
+
+}
+
« no previous file with comments | « pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart ('k') | pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698