| 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 enum ScopeType { InScope, InDefinition, NotInScope } | 13 enum ScopeType { InScope, InDefinition, NotInScope } |
| 14 | 14 |
| 15 /// Performs integrity checks on the CPS IR. | 15 /// Performs integrity checks on the CPS IR. |
| 16 /// | 16 /// |
| 17 /// To be run for debugging purposes, not for use in production. | 17 /// To be run for debugging purposes, not for use in production. |
| 18 /// | 18 /// |
| 19 /// The following integrity checks are performed: | 19 /// The following integrity checks are performed: |
| 20 /// | 20 /// |
| 21 /// - References are in scope of their definitions. | 21 /// - References are in scope of their definitions. |
| 22 /// - Recursive Continuations and InvokeContinuations are marked as recursive. | 22 /// - Recursive Continuations and InvokeContinuations are marked as recursive. |
| 23 /// - InvokeContinuations have the same arity as their target. | 23 /// - InvokeContinuations have the same arity as their target. |
| 24 /// - Reference chains are valid doubly-linked lists. | 24 /// - Reference chains are valid doubly-linked lists. |
| 25 /// - Reference chains contain exactly the references that are in the IR. | 25 /// - Reference chains contain exactly the references that are in the IR. |
| 26 /// - Each definition object occurs only once in the IR (no redeclaring). | 26 /// - Each definition object occurs only once in the IR (no redeclaring). |
| 27 /// - Each reference object occurs only once in the IR (no sharing). | 27 /// - Each reference object occurs only once in the IR (no sharing). |
| 28 /// | 28 /// |
| 29 class CheckCpsIntegrity extends TrampolineRecursiveVisitor { | 29 class CheckCpsIntegrity extends TrampolineRecursiveVisitor { |
| 30 | |
| 31 FunctionDefinition topLevelNode; | 30 FunctionDefinition topLevelNode; |
| 32 final Map<Definition, ScopeType> inScope = <Definition, ScopeType>{}; | 31 final Map<Definition, ScopeType> inScope = <Definition, ScopeType>{}; |
| 33 final List<Definition> definitions = []; | 32 final List<Definition> definitions = []; |
| 34 String previousPass; | 33 String previousPass; |
| 35 | 34 |
| 36 void handleDeclaration(Definition def) { | 35 void handleDeclaration(Definition def) { |
| 37 definitions.add(def); | 36 definitions.add(def); |
| 38 // Check the reference chain for cycles broken links. | 37 // Check the reference chain for cycles broken links. |
| 39 Reference anchor = null; | 38 Reference anchor = null; |
| 40 int i = 0; | 39 int i = 0; |
| 41 for (Reference ref = def.firstRef; ref != null; ref = ref.next) { | 40 for (Reference ref = def.firstRef; ref != null; ref = ref.next) { |
| 42 if (ref.definition != def) { | 41 if (ref.definition != def) { |
| 43 error('Reference to ${ref.definition} found in ' | 42 error( |
| 44 'reference chain for $def', def); | 43 'Reference to ${ref.definition} found in ' |
| 44 'reference chain for $def', |
| 45 def); |
| 45 } | 46 } |
| 46 if (ref == anchor) { | 47 if (ref == anchor) { |
| 47 error('Cyclic reference chain for $def', def); | 48 error('Cyclic reference chain for $def', def); |
| 48 } | 49 } |
| 49 if (i & ++i == 0) { // Move the anchor every 2^Nth step. | 50 if (i & ++i == 0) { |
| 51 // Move the anchor every 2^Nth step. |
| 50 anchor = ref; | 52 anchor = ref; |
| 51 } | 53 } |
| 52 } | 54 } |
| 53 } | 55 } |
| 54 | 56 |
| 55 void enterScope(Iterable<Definition> definitions) { | 57 void enterScope(Iterable<Definition> definitions) { |
| 56 for (Definition def in definitions) { | 58 for (Definition def in definitions) { |
| 57 inScope[def] = ScopeType.InScope; | 59 inScope[def] = ScopeType.InScope; |
| 58 } | 60 } |
| 59 pushAction(() { | 61 pushAction(() { |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 Decorator decorator = (n, String s) => n == node ? '**$s**' : s; | 220 Decorator decorator = (n, String s) => n == node ? '**$s**' : s; |
| 219 sexpr = new SExpressionStringifier(decorator).visit(topLevelNode); | 221 sexpr = new SExpressionStringifier(decorator).visit(topLevelNode); |
| 220 sexpr = 'SExpr dump (offending node marked with **):\n\n$sexpr'; | 222 sexpr = 'SExpr dump (offending node marked with **):\n\n$sexpr'; |
| 221 } catch (e) { | 223 } catch (e) { |
| 222 sexpr = '(Exception thrown by SExpressionStringifier: $e)'; | 224 sexpr = '(Exception thrown by SExpressionStringifier: $e)'; |
| 223 } | 225 } |
| 224 } else { | 226 } else { |
| 225 sexpr = '(Set DUMP_IR flag to enable SExpr dump)'; | 227 sexpr = '(Set DUMP_IR flag to enable SExpr dump)'; |
| 226 } | 228 } |
| 227 throw 'CPS integrity violation\n' | 229 throw 'CPS integrity violation\n' |
| 228 'After \'$previousPass\' on ${topLevelNode.element}\n' | 230 'After \'$previousPass\' on ${topLevelNode.element}\n' |
| 229 '$message\n\n' | 231 '$message\n\n' |
| 230 '$sexpr\n'; | 232 '$sexpr\n'; |
| 231 } | 233 } |
| 232 } | 234 } |
| 233 | 235 |
| 234 /// Traverses the CPS term and checks that node.parent is correctly set | 236 /// Traverses the CPS term and checks that node.parent is correctly set |
| 235 /// for each visited node. | 237 /// for each visited node. |
| 236 class ParentChecker extends DeepRecursiveVisitor { | 238 class ParentChecker extends DeepRecursiveVisitor { |
| 237 static void checkParents(Node node, CheckCpsIntegrity main) { | 239 static void checkParents(Node node, CheckCpsIntegrity main) { |
| 238 ParentChecker visitor = new ParentChecker._make(main); | 240 ParentChecker visitor = new ParentChecker._make(main); |
| 239 visitor._worklist.add(node); | 241 visitor._worklist.add(node); |
| 240 visitor.trampoline(); | 242 visitor.trampoline(); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 253 } | 255 } |
| 254 } | 256 } |
| 255 | 257 |
| 256 error(String message, node) => main.error(message, node); | 258 error(String message, node) => main.error(message, node); |
| 257 | 259 |
| 258 @override | 260 @override |
| 259 visit(Node node) { | 261 visit(Node node) { |
| 260 _worklist.add(node); | 262 _worklist.add(node); |
| 261 if (node.parent != _parent) { | 263 if (node.parent != _parent) { |
| 262 error('Parent pointer on $node is ${node.parent} but should be $_parent', | 264 error('Parent pointer on $node is ${node.parent} but should be $_parent', |
| 263 node); | 265 node); |
| 264 } | 266 } |
| 265 } | 267 } |
| 266 | 268 |
| 267 @override | 269 @override |
| 268 processReference(Reference node) { | 270 processReference(Reference node) { |
| 269 if (node.parent != _parent) { | 271 if (node.parent != _parent) { |
| 270 error('Parent pointer on $node is ${node.parent} but should be $_parent', | 272 error('Parent pointer on $node is ${node.parent} but should be $_parent', |
| 271 node); | 273 node); |
| 272 } | 274 } |
| 273 } | 275 } |
| 274 } | 276 } |
| OLD | NEW |