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