ni: added general comment for module ni
[nit.git] / src / flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Intraprocedural static flow.
18 module flow
19
20 import parser
21 import toolcontext
22 import scope
23
24 # The visitor that derermine flowcontext for nodes
25 private class FlowVisitor
26 super Visitor
27
28 var current_flow_context: FlowContext
29
30 var toolcontext: ToolContext
31
32 init(toolcontext: ToolContext)
33 do
34 self.toolcontext = toolcontext
35 current_flow_context = new FlowContext
36 flows.add(current_flow_context)
37 current_flow_context.is_start = true
38 end
39
40 var first: nullable ANode
41
42 redef fun visit(node)
43 do
44 if node != null then
45 if first == null then first = node
46
47 if current_flow_context.node == null then current_flow_context.node = node
48 node.accept_flow_visitor(self)
49 if node isa AExpr then
50 var flow = self.current_flow_context
51 node.after_flow_context = flow
52 # Force the creation of a specific merge after the analysis of the node.
53 if flow.when_true != flow or flow.when_false != flow then
54 self.make_sub_flow
55 self.current_flow_context.name = "AUTOSUB"
56 end
57 end
58
59 if first == node then
60 #self.printflow
61 end
62 end
63 end
64
65 fun visit_expr(node: AExpr): FlowContext
66 do
67 self.enter_visit(node)
68 return node.after_flow_context.as(not null)
69 end
70
71 var flows: Array[FlowContext] = new Array[FlowContext]
72
73 fun printflow
74 do
75 var file = new OFStream.open("flow.dot")
76 file.write("digraph \{\n")
77 for f in flows do
78 var s = ""
79 if f.node isa AExpr then
80 s = "\\nmain={f.node.as(AExpr).after_flow_context.object_id}"
81 end
82 file.write "F{f.object_id} [label=\"{f.object_id}\\n{f.node.location}\\n{f.node.class_name}\\n{f.name}{s}\"];\n"
83 for p in f.previous do
84 file.write "F{p.object_id} -> F{f.object_id};\n"
85 end
86 if f.when_true != f then
87 file.write "F{f.object_id} -> F{f.when_true.object_id}[label=TRUE, style=dotted];\n"
88 end
89 if f.when_false != f then
90 file.write "F{f.object_id} -> F{f.when_false.object_id}[label=FALSE,style=dotted];\n"
91 end
92 end
93 file.write("\}\n")
94 file.close
95 end
96
97
98 fun make_sub_flow: FlowContext
99 do
100 var flow = new FlowContext
101 flows.add(flow)
102 flow.node = current_node
103 flow.add_previous(self.current_flow_context)
104 self.current_flow_context = flow
105 return flow
106 end
107
108 fun make_merge_flow(flow1, flow2: FlowContext): FlowContext
109 do
110 var flow = new FlowContext
111 flows.add(flow)
112 flow.node = current_node
113 flow.add_previous(flow1)
114 flow.add_previous(flow2)
115 self.current_flow_context = flow
116 return flow
117 end
118
119 fun make_true_false_flow(true_flow, false_flow: FlowContext): FlowContext
120 do
121 var flow = new FlowContext
122 flows.add(flow)
123 flow.node = current_node
124 flow.add_previous(true_flow)
125 flow.add_previous(false_flow)
126 flow.when_true = true_flow
127 flow.when_false = false_flow
128 self.current_flow_context = flow
129 return flow
130 end
131
132 fun make_sub_true_false_flow: FlowContext
133 do
134 var orig_flow = self.current_flow_context
135 var true_flow = new FlowContext
136 flows.add(true_flow)
137 true_flow.node = current_node
138 true_flow.add_previous(orig_flow)
139 true_flow.name = "TRUE"
140 var false_flow = new FlowContext
141 flows.add(false_flow)
142 false_flow.node = current_node
143 false_flow.add_previous(orig_flow)
144 false_flow.name = "FALSE"
145 return make_true_false_flow(true_flow, false_flow)
146 end
147
148 fun make_unreachable_flow: FlowContext
149 do
150 var flow = new FlowContext
151 flows.add(flow)
152 flow.node = current_node
153 flow.add_previous(self.current_flow_context)
154 flow.is_marked_unreachable = true
155 self.current_flow_context = flow
156 return flow
157 end
158
159 fun merge_continues_to(before_loop: FlowContext, escapemark: nullable EscapeMark)
160 do
161 if escapemark == null then return
162 for b in escapemark.continues do
163 var before = b.before_flow_context
164 if before == null then continue # Forward error
165 before_loop.add_loop(before)
166 end
167 end
168
169 fun merge_breaks(escapemark: nullable EscapeMark)
170 do
171 if escapemark == null then return
172 for b in escapemark.breaks do
173 var before = b.before_flow_context
174 if before == null then continue # Forward error
175 self.make_merge_flow(self.current_flow_context, before)
176 end
177 end
178 end
179
180 # A Node in the static flow graph.
181 # A same FlowContext can be shared by more than one ANode.
182 class FlowContext
183 # The reachable previous flow
184 var previous: Array[FlowContext] = new Array[FlowContext]
185
186 # Additional reachable flow that loop up to self.
187 # Loops apears in 'loop', 'while', 'for', closure and with 'continue'
188 var loops: Array[FlowContext] = new Array[FlowContext]
189
190 private var is_marked_unreachable: Bool = false
191
192 # Is the flow dead?
193 fun is_unreachable: Bool
194 do
195 # Are we explicitely marked unreachable?
196 if self.is_marked_unreachable then return true
197
198 # Are we the starting flow context?
199 if is_start then return false
200
201 # De we have a reachable previous?
202 if previous.length == 0 then return true
203 return false
204 end
205
206 # Flag to avoid repeaed errors
207 var is_already_unreachable: Bool = false
208
209 # Mark that self is the starting flow context.
210 # Such a context is reachable even if there is no previous flow
211 var is_start: Bool = false
212
213 # The node that introduce the flow (for debuging)
214 var node: nullable ANode = null
215
216 # Additional information for the flor (for debuging)
217 var name: String = ""
218
219 # The sub-flow to use if the associated expr is true
220 var when_true: FlowContext = self
221
222 # The sub-flow to use if the associated expr is true
223 var when_false: FlowContext = self
224
225 # Add a previous flow (iff it is reachable)
226 private fun add_previous(flow: FlowContext)
227 do
228 if not flow.is_unreachable and not previous.has(flow) then
229 previous.add(flow)
230 end
231 end
232
233 # Add a previous loop flow (iff it is reachable)
234 private fun add_loop(flow: FlowContext)
235 do
236 if not flow.is_unreachable and not previous.has(flow) then
237 loops.add(flow)
238 end
239 end
240
241 end
242
243 redef class ANode
244 private fun accept_flow_visitor(v: FlowVisitor)
245 do
246 self.visit_all(v)
247 end
248 end
249
250 redef class APropdef
251 # The entry point of the whole flow analysis
252 fun do_flow(toolcontext: ToolContext)
253 do
254 var v = new FlowVisitor(toolcontext)
255 v.enter_visit(self)
256 end
257
258
259 # The starting flow
260 var before_flow_context: nullable FlowContext
261
262 # The ending flow
263 var after_flow_context: nullable FlowContext
264
265 redef fun accept_flow_visitor(v)
266 do
267 self.before_flow_context = v.current_flow_context
268 super
269 self.after_flow_context = v.current_flow_context
270 end
271 end
272
273 redef class AExpr
274 # The flow after the full evaluation of the expression/statement
275 var after_flow_context: nullable FlowContext
276 end
277
278 redef class AVarAssignExpr
279 redef fun accept_flow_visitor(v)
280 do
281 super
282 self.after_flow_context = v.make_sub_flow
283 end
284 end
285
286 redef class AReassignFormExpr
287 redef fun accept_flow_visitor(v)
288 do
289 super
290 self.after_flow_context = v.make_sub_flow
291 end
292 end
293
294 redef class ABlockExpr
295 redef fun accept_flow_visitor(v)
296 do
297 for e in n_expr do
298 if not v.current_flow_context.is_unreachable then
299 v.enter_visit(e)
300 else if not v.current_flow_context.is_already_unreachable then
301 v.current_flow_context.is_already_unreachable = true
302 v.toolcontext.error(e.hot_location, "Error: unreachable statement.")
303 end
304 end
305 end
306 end
307
308 redef class AReturnExpr
309 redef fun accept_flow_visitor(v)
310 do
311 super
312 v.make_unreachable_flow
313 end
314 end
315
316 redef class AContinueExpr
317 # The flow just before it become unreachable
318 fun before_flow_context: nullable FlowContext
319 do
320 var after = self.after_flow_context
321 if after == null then return null
322 return after.previous.first
323 end
324 redef fun accept_flow_visitor(v)
325 do
326 super
327 v.make_unreachable_flow
328 end
329 end
330
331 redef class ABreakExpr
332 # The flow just before it become unreachable
333 fun before_flow_context: nullable FlowContext
334 do
335 var after = self.after_flow_context
336 if after == null then return null
337 return after.previous.first
338 end
339 redef fun accept_flow_visitor(v)
340 do
341 super
342 v.make_unreachable_flow
343 end
344 end
345
346 redef class AAbortExpr
347 redef fun accept_flow_visitor(v)
348 do
349 super
350 v.make_unreachable_flow
351 end
352 end
353
354 redef class ADoExpr
355 redef fun accept_flow_visitor(v)
356 do
357 super
358 v.merge_breaks(self.escapemark)
359 end
360 end
361
362 redef class AIfExpr
363 redef fun accept_flow_visitor(v)
364 do
365 var after_expr = v.visit_expr(self.n_expr)
366
367 v.current_flow_context = after_expr.when_true
368 v.enter_visit(self.n_then)
369 var after_then = v.current_flow_context
370
371 v.current_flow_context = after_expr.when_false
372 v.enter_visit(self.n_else)
373 var after_else = v.current_flow_context
374
375 v.make_merge_flow(after_then, after_else)
376 end
377 end
378
379 redef class AIfexprExpr
380 redef fun accept_flow_visitor(v)
381 do
382 var after_expr = v.visit_expr(self.n_expr)
383
384 v.current_flow_context = after_expr.when_true
385 v.enter_visit(self.n_then)
386 var after_then = v.current_flow_context
387
388 v.current_flow_context = after_expr.when_false
389 v.enter_visit(self.n_else)
390 var after_else = v.current_flow_context
391
392 v.make_merge_flow(after_then, after_else)
393 end
394 end
395
396 redef class AWhileExpr
397 redef fun accept_flow_visitor(v)
398 do
399 var before_loop = v.make_sub_flow
400
401 var after_expr = v.visit_expr(self.n_expr)
402
403 v.current_flow_context = after_expr.when_true
404 v.enter_visit(self.n_block)
405 var after_block = v.current_flow_context
406
407 before_loop.add_loop(after_block)
408 v.merge_continues_to(after_block, self.escapemark)
409
410 v.current_flow_context = after_expr.when_false
411 v.merge_breaks(self.escapemark)
412 end
413 end
414
415 redef class ALoopExpr
416 redef fun accept_flow_visitor(v)
417 do
418 var before_loop = v.make_sub_flow
419
420 v.enter_visit(self.n_block)
421
422 var after_block = v.current_flow_context
423
424 before_loop.add_loop(after_block)
425 v.merge_continues_to(after_block, self.escapemark)
426
427 v.make_unreachable_flow
428 v.merge_breaks(self.escapemark)
429 end
430 end
431
432 redef class AForExpr
433 redef fun accept_flow_visitor(v)
434 do
435 v.enter_visit(self.n_expr)
436
437 var before_loop = v.make_sub_flow
438
439 v.enter_visit(self.n_block)
440
441 var after_block = v.current_flow_context
442
443 before_loop.add_loop(after_block)
444 v.merge_continues_to(after_block, self.escapemark)
445
446 v.make_merge_flow(v.current_flow_context, before_loop)
447 v.merge_breaks(self.escapemark)
448 end
449 end
450
451 redef class AAssertExpr
452 redef fun accept_flow_visitor(v)
453 do
454 var after_expr = v.visit_expr(self.n_expr)
455
456 v.current_flow_context = after_expr.when_false
457 v.enter_visit(n_else)
458 # the after context of n_else is a dead end, so we do not care
459
460 v.current_flow_context = after_expr.when_true
461 end
462 end
463
464 redef class AOrExpr
465 redef fun accept_flow_visitor(v)
466 do
467 var after_expr = v.visit_expr(self.n_expr)
468
469 v.current_flow_context = after_expr.when_false
470 var after_expr2 = v.visit_expr(self.n_expr2)
471
472 var merge_true = v.make_merge_flow(after_expr.when_true, after_expr2.when_true)
473 merge_true.name = "OR TRUE"
474
475 v.make_true_false_flow(merge_true, after_expr2.when_false)
476 end
477 end
478
479 redef class AAndExpr
480 redef fun accept_flow_visitor(v)
481 do
482 var after_expr = v.visit_expr(self.n_expr)
483
484 v.current_flow_context = after_expr.when_true
485 var after_expr2 = v.visit_expr(self.n_expr2)
486
487 var merge_false = v.make_merge_flow(after_expr.when_false, after_expr2.when_false)
488 merge_false.name = "AND FALSE"
489
490 v.make_true_false_flow(after_expr2.when_true, merge_false)
491 end
492 end
493
494 redef class ANotExpr
495 redef fun accept_flow_visitor(v)
496 do
497 var after_expr = v.visit_expr(self.n_expr)
498
499 v.make_true_false_flow(after_expr.when_false, after_expr.when_true)
500 end
501 end
502
503 redef class AOrElseExpr
504 redef fun accept_flow_visitor(v)
505 do
506 super
507 end
508 end
509
510 redef class AEqExpr
511 redef fun accept_flow_visitor(v)
512 do
513 super
514 v.make_sub_true_false_flow
515 end
516 end
517
518
519 redef class ANeExpr
520 redef fun accept_flow_visitor(v)
521 do
522 super
523 v.make_sub_true_false_flow
524 end
525 end
526
527 redef class AClosureCallExpr
528 redef fun accept_flow_visitor(v)
529 do
530 super
531 # FIXME: break closure call?
532 # v.make_unreachable_flow
533 end
534 end
535
536 redef class AClosureDef
537 redef fun accept_flow_visitor(v)
538 do
539 var before_loop = v.make_sub_flow
540
541 v.enter_visit(self.n_expr)
542
543 var after_block = v.current_flow_context
544 before_loop.add_loop(after_block)
545
546 v.make_merge_flow(v.current_flow_context, before_loop)
547 end
548 end
549
550 redef class AIsaExpr
551 redef fun accept_flow_visitor(v)
552 do
553 super
554 v.make_sub_true_false_flow
555 end
556 end
557
558 redef class AProxyExpr
559 redef fun accept_flow_visitor(v)
560 do
561 var after_expr = v.visit_expr(self.n_expr)
562 v.current_flow_context = after_expr
563 end
564 end