2 * Copyright 2010, INRIA, University of Copenhagen
3 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
4 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
5 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
6 * This file is part of Coccinelle.
8 * Coccinelle is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, according to version 2 of the License.
12 * Coccinelle is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
20 * The authors reserve the right to distribute this or future versions of
21 * Coccinelle under other licenses.
26 * Copyright 2010, INRIA, University of Copenhagen
27 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
28 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
29 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
30 * This file is part of Coccinelle.
32 * Coccinelle is free software: you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation, according to version 2 of the License.
36 * Coccinelle is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
41 * You should have received a copy of the GNU General Public License
42 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
44 * The authors reserve the right to distribute this or future versions of
45 * Coccinelle under other licenses.
53 module F
= Control_flow_c
55 (*****************************************************************************)
56 (* Debugging functions *)
57 (*****************************************************************************)
58 let show_or_not_predicate pred
=
59 if !Flag_matcher.debug_engine
then begin
61 adjust_pp_with_indent_and_header
"labeling: pred = " (fun () ->
62 Pretty_print_engine.pp_predicate pred
;
67 let show_or_not_nodes nodes
=
68 if !Flag_matcher.debug_engine
then begin
70 adjust_pp_with_indent_and_header
"labeling: result = " (fun () ->
71 Common.pp_do_in_box
(fun () ->
74 (fun () -> pp
";"; Format.print_cut
())
75 (fun (nodei
, (_predTODO
, subst
)) ->
76 Format.print_int nodei
;
77 Common.pp_do_in_box
(fun () ->
78 Pretty_print_engine.pp_binding2_ctlsubst subst
87 let show_isos rule_elem
=
88 match Ast_cocci.get_isos rule_elem
with
91 let line = Ast_cocci.get_line rule_elem
in
92 Printf.printf
"rule elem: ";
93 Pretty_print_cocci.rule_elem
"" rule_elem
;
94 Format.print_newline
();
97 Printf.printf
" iso: %s(%d): " nm
line;
98 Pretty_print_cocci.pp_print_anything x
;
99 Format.print_newline
())
102 (*****************************************************************************)
103 (* Labeling function *)
104 (*****************************************************************************)
105 let (-->) x v
= Ast_ctl.Subst
(x
,v
);;
107 (* Take list of predicate and for each predicate returns where in the
108 * control flow it matches, and the set of subsitutions for this match.
110 let (labels_for_ctl
: string list
(* dropped isos *) ->
111 (nodei
* F.node
) list
-> Lib_engine.metavars_binding
->
112 Lib_engine.label_ctlcocci
) =
113 fun dropped_isos nodes binding
->
116 show_or_not_predicate p
;
118 let nodes'
= nodes +> List.map
(fun (nodei
, node
) ->
119 (* todo? put part of this code in pattern ? *)
120 (match p
, F.unwrap node
with
121 | Lib_engine.Paren s
, (F.SeqStart
(_
, bracelevel
, _
)) ->
122 let make_var x
= ("",i_to_s x
) in
123 [(nodei
, (p
,[(s
--> (Lib_engine.ParenVal
(make_var bracelevel
)))]))]
124 | Lib_engine.Paren s
, (F.SeqEnd
(bracelevel
, _
)) ->
125 let make_var x
= ("",i_to_s x
) in
126 [(nodei
, (p
,[(s
--> (Lib_engine.ParenVal
(make_var bracelevel
)))]))]
127 | Lib_engine.Paren _
, _
-> []
128 | Lib_engine.Label s
, _
->
129 let labels = F.extract_labels node
in
131 (p
,[(s
--> (Lib_engine.LabelVal
(Lib_engine.Absolute
labels)))]))]
132 | Lib_engine.BCLabel s
, _
->
133 (match F.extract_bclabels node
with
134 [] -> [] (* null for all nodes that are not break or continue *)
138 (Lib_engine.LabelVal
(Lib_engine.Absolute
labels)))]))])
139 | Lib_engine.PrefixLabel s
, _
->
140 let labels = F.extract_labels node
in
142 (p
,[(s
--> (Lib_engine.LabelVal
(Lib_engine.Prefix
labels)))]))]
144 | Lib_engine.Match
(re
), _unwrapnode
->
146 Pattern_c.match_re_node dropped_isos re node binding
147 +> List.map
(fun (re'
, subst
) ->
148 Lib_engine.Match
(re'
), subst
151 substs +> List.map
(fun (p'
, subst
) ->
154 subst
+> List.map
(fun (s
, meta
) ->
155 s
--> Lib_engine.NormalMetaVal meta
158 | Lib_engine.InLoop
, F.InLoopNode
-> [nodei
, (p
,[])]
159 | Lib_engine.TrueBranch
, F.TrueNode
-> [nodei
, (p
,[])]
160 | Lib_engine.FalseBranch
, F.FalseNode
-> [nodei
, (p
,[])]
161 | Lib_engine.After
, F.AfterNode
-> [nodei
, (p
,[])]
162 | Lib_engine.FallThrough
, F.FallThroughNode
-> [nodei
,(p
,[])]
163 | Lib_engine.LoopFallThrough
, F.LoopFallThroughNode
-> [nodei
,(p
,[])]
164 | Lib_engine.FunHeader
, F.FunHeader _
-> [nodei
, (p
,[])]
165 | Lib_engine.Top
, F.TopNode
-> [nodei
, (p
,[])]
166 | Lib_engine.Exit
, F.Exit
-> [nodei
, (p
,[])]
167 | Lib_engine.ErrorExit
, F.ErrorExit
-> [nodei
, (p
,[])]
168 | Lib_engine.Goto
, F.Goto
(_
,_
,_
) -> [nodei
, (p
,[])]
170 | Lib_engine.InLoop
, _
-> []
171 | Lib_engine.TrueBranch
, _
-> []
172 | Lib_engine.FalseBranch
, _
-> []
173 | Lib_engine.After
, _
-> []
174 | Lib_engine.FallThrough
, _
-> []
175 | Lib_engine.LoopFallThrough
, _
-> []
176 | Lib_engine.FunHeader
, _
-> []
177 | Lib_engine.Top
, _
-> []
178 | Lib_engine.Exit
, _
-> []
179 | Lib_engine.ErrorExit
, _
-> []
180 | Lib_engine.Goto
, _
-> []
182 | Lib_engine.BindGood s
, _
-> [(nodei
, (p
,[(s
--> Lib_engine.GoodVal
)]))]
183 | Lib_engine.BindBad s
, _
-> [(nodei
, (p
,[(s
--> Lib_engine.BadVal
)]))]
184 | Lib_engine.FakeBrace
, _
->
185 if F.extract_is_fake node
then [nodei
, (p
,[])] else []
187 | Lib_engine.Return
, node
->
189 (* todo? should match the Exit code ?
190 * todo: one day try also to match the special function
193 | F.Return _
-> [nodei
, (p
,[])]
194 | F.ReturnExpr _
-> [nodei
, (p
,[])]
201 show_or_not_nodes nodes'
;
205 (*****************************************************************************)
206 (* Some fix flow, for CTL, for unparse *)
207 (*****************************************************************************)
208 (* could erase info on nodes, and edge, because they are not used by rene *)
209 let (control_flow_for_ctl
: F.cflow
-> ('a
, 'b
) ograph_mutable
) =
214 (* Just make the final node of the control flow loop over itself.
215 * It seems that one hypothesis of the SAT algorithm is that each node as at
218 * update: do same for errorexit node.
220 * update: also erase the fake nodes (and adjust the edges accordingly),
221 * so that AX in CTL can now work.
222 * Indeed, Ã la fin de la branche then (et else), on devrait aller directement
223 * au suivant du endif, sinon si ecrit if(1) { foo(); }; bar();
224 * sans '...' entre le if et bar(), alors ca matchera pas car le CTL
225 * generera un AX bar() qui il tombera d'abord sur le [endif] :(
226 * Mais chiant de changer l'algo de generation, marche pas tres bien avec
227 * ma facon de faire recursive et compositionnel.
228 * => faire une fonction qui applique des fixes autour de ce control flow,
229 * comme ca passe un bon flow a rene, mais garde un flow a moi pour pouvoir
230 * facilement generate back the ast.
231 * alt: faire un wrapper autourde mon graphe pour lui passer dans le module CFG
232 * une fonction qui passe a travers les Fake, mais bof.
234 * update: also make loop the deadcode nodes, the one that have
237 let (fix_flow_ctl2
: F.cflow
-> F.cflow
) = fun flow
->
240 let topi = F.first_node
!g in
241 !g#add_arc
((topi, topi), F.Direct
);
243 (* for the #define CFG who have no Exit but have at least a EndNode *)
245 let endi = F.find_node
(fun x
-> x
=*= F.EndNode
) !g in
246 !g#add_arc
((endi, endi), F.Direct
);
250 (* for the regular functions *)
252 let exitnodei = F.find_node
(fun x
-> x
=*= F.Exit
) !g in
253 let errornodei = F.find_node
(fun x
-> x
=*= F.ErrorExit
) !g in
255 !g#add_arc
((exitnodei, exitnodei), F.Direct
);
257 if null
((!g#successors
errornodei)#tolist
) &&
258 null
((!g#predecessors
errornodei)#tolist
)
259 then !g#del_node
errornodei
260 else !g#add_arc
((errornodei, errornodei), F.Direct
);
264 let fake_nodes = !g#
nodes#tolist
+> List.filter
(fun (nodei
, node
) ->
265 match F.unwrap node
with
268 (*| F.Fake*) (* [endif], [endswitch], ... *)
273 fake_nodes +> List.iter
(fun (nodei
, node
) -> F.remove_one_node nodei
!g);
275 (* even when have deadcode, julia want loop over those nodes *)
276 !g#
nodes#tolist
+> List.iter
(fun (nodei
, node
) ->
277 if (!g#predecessors nodei
)#null
279 let fakei = !g#add_node
(F.mk_node
F.Fake
[] [] "DEADCODELOOP") in
280 !g#add_arc
((fakei, nodei
), F.Direct
);
281 !g#add_arc
((fakei, fakei), F.Direct
);
285 !g#
nodes#tolist
+> List.iter
(fun (nodei
, node
) ->
286 assert (List.length
((!g#successors nodei
)#tolist
) >= 1);
287 (* no: && List.length ((!g#predecessors nodei)#tolist) >= 1
288 because the enter node at least have no predecessors *)
293 Common.profile_code
"fix_flow" (fun () -> fix_flow_ctl2 a
)
299 (*****************************************************************************)
300 (* subtil: the label must operate on newflow, not (old) cflow
301 * update: now I supposed that we give me a fixed_flow
303 let model_for_ctl dropped_isos cflow binding
=
304 let newflow = cflow
(* old: fix_flow_ctl (control_flow_for_ctl cflow) *) in
305 let labels = labels_for_ctl dropped_isos
(newflow#
nodes#tolist
) binding
in
306 let states = List.map fst
newflow#
nodes#tolist
in
307 newflow, labels, states
310 (*****************************************************************************)
314 type t
= Lib_engine.predicate
315 let print_predicate x
=
316 Pretty_print_cocci.print_plus_flag
:= false;
317 Pretty_print_cocci.print_minus_flag
:= false;
318 Pretty_print_engine.pp_predicate x
321 (* prefix has to be nonempty *)
323 let rec loop = function
326 | (x
::xs
,y
::ys
) when x
= y
-> loop (xs
,ys
)
330 let compatible_labels l1 l2
=
332 (Lib_engine.Absolute
(l1
),Lib_engine.Absolute
(l2
)) -> l1
=*= l2
333 | (Lib_engine.Absolute
(l1
),Lib_engine.Prefix
(l2
)) -> prefix l1 l2
334 | (Lib_engine.Prefix
(l1
),Lib_engine.Absolute
(l2
)) -> prefix l2 l1
335 | (Lib_engine.Prefix
(l1
),Lib_engine.Prefix
(l2
)) ->
336 not
(l1
= []) && not
(l2
= []) &&
337 List.hd l1
=*= List.hd l2
(* labels are never empty *)
339 let merge_labels l1 l2
=
341 (* known to be compatible *)
342 (Lib_engine.Absolute
(_
),Lib_engine.Absolute
(_
)) -> l1
343 | (Lib_engine.Absolute
(_
),Lib_engine.Prefix
(_
)) -> l1
344 | (Lib_engine.Prefix
(_
),Lib_engine.Absolute
(_
)) -> l2
345 | (Lib_engine.Prefix
(l1
),Lib_engine.Prefix
(l2
)) ->
346 let rec max_prefix = function
347 (x
::xs
,y
::ys
) when x
= y
-> x
::(max_prefix(xs
,ys
))
349 Lib_engine.Prefix
(max_prefix(l1
,l2
))
353 type value = Lib_engine.metavar_binding_kind2
354 type mvar
= Ast_cocci.meta_name
355 let eq_mvar x x'
= x
=*= x'
359 (Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min1
,max1
)),
360 Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min2
,max2
))) ->
361 ((min1
<= min2
) && (max1
>= max2
)) or
362 ((min2
<= min1
) && (max2
>= max1
))
363 | (Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal a
),
364 Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal b
)) ->
366 | (Lib_engine.LabelVal
(l1
),Lib_engine.LabelVal
(l2
)) ->
367 compatible_labels l1 l2
369 let merge_val v v'
= (* values guaranteed to be compatible *)
372 (Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min1
,max1
)),
373 Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min2
,max2
))) ->
374 if (min1
<= min2
) && (max1
>= max2
)
375 then Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min1
,max1
))
377 if (min2
<= min1
) && (max2
>= max1
)
378 then Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min2
,max2
))
379 else failwith
"incompatible positions give to merge"
380 | (Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal a
),
381 Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal b
)) ->
382 Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal
(C_vs_c.merge_type a b
))
383 | (Lib_engine.LabelVal
(l1
),Lib_engine.LabelVal
(l2
)) ->
384 Lib_engine.LabelVal
(merge_labels l1 l2
)
387 let print_mvar (_
,s
) = Format.print_string s
388 let print_value x
= Pretty_print_engine.pp_binding_kind2 x
393 type node
= Ograph_extended.nodei
394 type cfg
= (F.node
, F.edge
) Ograph_extended.ograph_mutable
395 let predecessors cfg n
= List.map fst
((cfg#
predecessors n
)#tolist
)
396 let successors cfg n
= List.map fst
((cfg#
successors n
)#tolist
)
397 let extract_is_loop cfg n
=
398 Control_flow_c.extract_is_loop (cfg#
nodes#find n
)
399 let print_node i
= Format.print_string
(i_to_s i
)
400 let size cfg
= cfg#
nodes#length
402 (* In ctl_engine, we use 'node' for the node but in the Ograph_extended
403 * terminology, this 'node' is in fact an index to access the real
404 * node information (that ctl/ wants to abstract away to be more generic),
405 * the 'Ograph_extended.nodei'.
407 let print_graph cfg label border_colors fill_colors filename
=
408 Ograph_extended.print_ograph_mutable_generic cfg label
409 (fun (nodei
, (node
: F.node
)) ->
410 (* the string julia wants to put ? *)
411 let bc = try Some
(List.assoc nodei border_colors
) with _
-> None
in
412 let fc = try Some
(List.assoc nodei fill_colors
) with _
-> None
in
413 (* the string yoann put as debug information in the cfg *)
414 let str = snd node
in
417 ~output_file
:filename
422 module WRAPPED_ENGINE
= Wrapper_ctl.CTL_ENGINE_BIS
(ENV
) (CFG
) (PRED
)
424 let print_bench _
= WRAPPED_ENGINE.print_bench()
426 type pred
= Lib_engine.predicate
* Ast_cocci.meta_name
Ast_ctl.modif
428 (*****************************************************************************)
429 let metavars_binding2_to_binding binding2
=
430 binding2
+> Common.map_filter
(fun (s
, kind2
) ->
432 | Lib_engine.NormalMetaVal kind
-> Some
(s
, kind
)
433 (* I thought it was Impossible to have this when called from
434 satbis_to_trans_info but it does not seems so *)
435 | Lib_engine.ParenVal _
-> None
436 | Lib_engine.LabelVal _
-> None
437 | Lib_engine.BadVal
-> None
(* should not occur *)
438 | Lib_engine.GoodVal
-> None
(* should not occur *)
441 let metavars_binding_to_binding2 binding
=
442 binding
+> List.map
(fun (s
, kind
) -> s
, Lib_engine.NormalMetaVal kind
)
445 let (satbis_to_trans_info
:
447 (nodei
* Lib_engine.metavars_binding2
* Lib_engine.predicate
)) list
->
449 (nodei
* Lib_engine.metavars_binding
* Ast_cocci.rule_elem
)) list
) =
451 xs
+> List.fold_left
(fun prev
(index
,(nodei
, binding2
, pred
)) ->
453 | Lib_engine.Match
(rule_elem
) ->
454 if !Flag.track_iso_usage
then show_isos rule_elem
;
456 (nodei
, metavars_binding2_to_binding binding2
, rule_elem
))
458 (* see BindGood in asttotctl2 *)
459 | Lib_engine.BindGood
(_
) -> prev
460 | _
-> raise Impossible
463 (*****************************************************************************)
465 let rec coalesce_positions = function
467 | (x
,Ast_c.MetaPosValList l
)::rest
->
468 let (same
,others
) = List.partition
(function (x1
,_
) -> x
=*= x1
) rest
in
473 (_
,Ast_c.MetaPosValList l
) -> l
474 | _
-> failwith
"unexpected non-position")
476 let new_ls = List.sort compare
(l
@ls) in
477 (x
,Ast_c.MetaPosValList
new_ls) :: coalesce_positions others
478 | x
::rest
-> x
:: coalesce_positions rest
485 Ast_c.MetaExprVal
(a
,c
) ->
486 Ast_c.MetaExprVal
(Lib_parsing_c.al_inh_expr a
,c
)
487 | Ast_c.MetaExprListVal a
->
488 Ast_c.MetaExprListVal
(Lib_parsing_c.al_inh_arguments a
)
489 | Ast_c.MetaStmtVal a
->
490 Ast_c.MetaStmtVal
(Lib_parsing_c.al_inh_statement a
)
491 | Ast_c.MetaInitVal a
->
492 Ast_c.MetaInitVal
(Lib_parsing_c.al_inh_init a
)
493 | x
-> (*don't contain binding info*) x
in
500 | (x
::xs
) when (List.mem x xs
) -> nub xs
501 | (x
::xs
) -> x
::(nub xs
)
503 (*****************************************************************************)
504 (* Call ctl engine *)
505 (*****************************************************************************)
508 (Lib_engine.ctlcocci
* (pred list list
)) ->
509 (Lib_engine.mvar list
*Lib_engine.metavars_binding
) ->
510 (Lib_engine.numbered_transformation_info
* bool *
511 Lib_engine.metavars_binding
* Lib_engine.metavars_binding list
)) =
512 fun (flow
, label
, states) ctl
(used_after
, binding
) ->
513 let binding2 = metavars_binding_to_binding2 binding
in
514 let (triples
,(trans_info2
, returned_any_states
, used_after_envs
)) =
515 WRAPPED_ENGINE.satbis
(flow
, label
, states) ctl
516 (used_after
, binding2)
518 if not
(!Flag_parsing_cocci.sgrep_mode
|| !Flag.sgrep_mode2
||
519 !Flag_matcher.allow_inconsistent_paths
)
520 then Check_reachability.check_reachability triples flow
;
521 let (trans_info2
,used_after_fresh_envs
) =
522 Postprocess_transinfo.process used_after
binding2 trans_info2
in
523 let used_after_envs =
524 Common.uniq
(List.map2
(@) used_after_fresh_envs
used_after_envs) in
525 let trans_info = satbis_to_trans_info trans_info2
in
526 let newbindings = List.map
metavars_binding2_to_binding used_after_envs in
527 let newbindings = List.map
coalesce_positions newbindings in
528 let newbindings = List.map
strip newbindings in
529 let newbindings = nub newbindings in
530 (trans_info, returned_any_states
, binding
, newbindings)
533 Common.profile_code
"mysat" (fun () -> mysat2 a b c
)