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