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

Side by Side Diff: pkg/compiler/lib/src/cps_ir/cps_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: Fix typo Created 5 years, 9 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 library dart2js.cps_ir_integrity;
2
3 import 'cps_ir_nodes.dart';
4 import 'cps_ir_nodes_sexpr.dart';
5 import '../tracer.dart' as tracer;
6
7 /// Dump S-expressions on error if the tracer is enabled.
8 ///
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.
11 const bool ENABLE_DUMP = tracer.TRACE_FILTER_PATTERN != null;
12
13 /// Performs integrity checks on the CPS IR.
14 ///
15 /// To be run for debugging purposes, not for use in production.
16 ///
17 /// The following integrity checks are performed:
18 ///
19 /// - References are in scope of their definitions.
20 /// - Recursive Continuations and InvokeContinuations are marked as recursive.
21 /// - InvokeContinuations have the same arity as their target.
22 /// - Reference chains are valid doubly-linked lists.
23 /// - Reference chains contain exactly the references that are in the IR.
24 /// - Each definition object occurs only once in the IR (no redeclaring).
25 /// - Each reference object occurs only once in the IR (no sharing).
26 ///
27 class CheckCpsIntegrity extends RecursiveVisitor {
28
29 ExecutableDefinition topLevelNode;
30
31 Set<Definition> seenDefinitions = new Set<Definition>();
32 Map<Definition, Set<Reference>> seenReferences =
33 <Definition, Set<Reference>>{};
34
35 Map<Definition, Node> bindings = <Definition, Node>{};
36 Set<Continuation> insideContinuations = new Set<Continuation>();
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) {
49 if (!seenDefinitions.add(def)) {
50 error('Redeclared $def', def);
51 }
52 seenReferences[def] = new Set<Reference>();
53 }
54
55 @override
56 visitLetCont(LetCont node) {
57 // Analyze each continuation separately without the others in scope.
58 for (Continuation continuation in node.continuations) {
59 // We always consider a continuation to be in scope of itself.
60 // The isRecursive flag is checked explicitly to give more useful
61 // error messages.
62 doInScope([continuation], node, () => visit(continuation));
63 }
64 // Analyze the body with all continuations in scope.
65 doInScope(node.continuations, node, () => visit(node.body));
66 }
67
68 @override
69 visitContinuation(Continuation node) {
70 markAsSeen(node);
71 if (node.isReturnContinuation) {
72 error('Non-return continuation missing body', node);
73 }
74 node.parameters.forEach(markAsSeen);
75 insideContinuations.add(node);
76 doInScope(node.parameters, node, () => visit(node.body));
77 insideContinuations.remove(node);
78 }
79
80 @override
81 visitRunnableBody(RunnableBody node) {
82 markAsSeen(node.returnContinuation);
83 if (!node.returnContinuation.isReturnContinuation) {
84 error('Return continuation with a body', node);
85 }
86 doInScope([node.returnContinuation], node, () => visit(node.body));
87 }
88
89 @override
90 visitLetPrim(LetPrim node) {
91 markAsSeen(node.primitive);
92 visit(node.primitive);
93 doInScope([node.primitive], node, () => visit(node.body));
94 }
95
96 @override
97 visitLetMutable(LetMutable node) {
98 markAsSeen(node.variable);
99 processReference(node.value);
100 doInScope([node.variable], node, () => visit(node.body));
101 }
102
103 @override
104 visitFunctionDefinition(FunctionDefinition node) {
105 node.parameters.forEach(markAsSeen);
106 if (node.body != null) {
107 doInScope(node.parameters, node, () => visit(node.body));
108 }
109 }
110
111 @override
112 visitConstructorDefinition(ConstructorDefinition node) {
113 node.parameters.forEach(markAsSeen);
114 doInScope(node.parameters, node, () {
115 if (node.initializers != null) node.initializers.forEach(visit);
116 if (node.body != null) visit(node.body);
117 });
118 }
119
120 @override
121 visitDeclareFunction(DeclareFunction node) {
122 markAsSeen(node.variable);
123 doInScope([node.variable], node, () {
124 visit(node.definition);
125 visit(node.body);
126 });
127 }
128
129 @override
130 processReference(Reference reference) {
131 if (!bindings.containsKey(reference.definition)) {
132 error('Referenced out of scope: ${reference.definition}', reference);
133 }
134 if (!seenReferences[reference.definition].add(reference)) {
135 error('Duplicate use of Reference to ${reference.definition}', reference);
136 }
137 }
138
139 @override
140 processInvokeContinuation(InvokeContinuation node) {
141 Continuation target = node.continuation.definition;
142 if (node.isRecursive && !insideContinuations.contains(target)) {
143 error('Non-recursive InvokeContinuation marked as recursive', node);
144 }
145 if (!node.isRecursive && insideContinuations.contains(target)) {
146 error('Recursive InvokeContinuation marked as non-recursive', node);
147 }
148 if (node.isRecursive && !target.isRecursive) {
149 error('Recursive Continuation was not marked as recursive', node);
150 }
151 if (node.arguments.length != target.parameters.length) {
152 error('Arity mismatch in InvokeContinuation', node);
153 }
154 }
155
156 void checkReferenceChain(Definition def) {
157 Set<Reference> chainedReferences = new Set<Reference>();
158 Reference prev = null;
159 for (Reference ref = def.firstRef; ref != null; ref = ref.next) {
160 if (ref.definition != def) {
161 error('Reference in chain for $def points to ${ref.definition}', def);
162 }
163 if (ref.previous != prev) {
164 error('Broken .previous link in reference to $def', def);
165 }
166 prev = ref;
167 if (!chainedReferences.add(ref)) {
168 error('Cyclic reference chain for $def', def);
169 }
170 }
171 if (!chainedReferences.containsAll(seenReferences[def])) {
172 error('Seen reference to $def not in reference chain', def);
173 }
174 if (!seenReferences[def].containsAll(chainedReferences)) {
175 error('Reference chain for $def contains orphaned references', def);
176 }
177 }
178
179 error(String message, node) {
180 String sexpr;
181 if (ENABLE_DUMP) {
182 try {
183 Decorator decorator = (n, String s) => n == node ? '**$s**' : s;
184 sexpr = new SExpressionStringifier(decorator).visit(topLevelNode);
185 } catch (e) {
186 sexpr = '(Exception thrown by SExpressionStringifier: $e)';
187 }
188 } else {
189 sexpr = '(Set DUMP_IR flag to enable)';
190 }
191 throw 'CPS integrity violation in ${topLevelNode.element}:\n'
192 '$message\n\n'
193 'SExpr dump (offending node marked with **):\n\n'
194 '$sexpr\n';
195 }
196
197 void check(ExecutableDefinition node) {
198 topLevelNode = node;
199 visit(node);
200
201 // Check this last, so out-of-scope references are not classified as
202 // a broken reference chain.
203 seenDefinitions.forEach(checkReferenceChain);
204 }
205
206 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart » ('j') | pkg/compiler/lib/src/dart_backend/backend.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698