2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
27 #
0 "./ctlcocci_integration.ml"
32 module F
= Control_flow_c
34 (*****************************************************************************)
35 (* Debugging functions *)
36 (*****************************************************************************)
37 let show_or_not_predicate pred
=
38 if !Flag_matcher.debug_engine
then begin
40 adjust_pp_with_indent_and_header
"labeling: pred = " (fun () ->
41 Pretty_print_engine.pp_predicate pred
;
46 let show_or_not_nodes nodes
=
47 if !Flag_matcher.debug_engine
then begin
49 adjust_pp_with_indent_and_header
"labeling: result = " (fun () ->
50 Common.pp_do_in_box
(fun () ->
53 (fun () -> pp
";"; Format.print_cut
())
54 (fun (nodei
, (_predTODO
, subst
)) ->
55 Format.print_int nodei
;
56 Common.pp_do_in_box
(fun () ->
57 Pretty_print_engine.pp_binding2_ctlsubst subst
66 let show_isos rule_elem
=
67 match Ast_cocci.get_isos rule_elem
with
70 let line = Ast_cocci.get_line rule_elem
in
71 Printf.printf
"rule elem: ";
72 Pretty_print_cocci.rule_elem
"" rule_elem
;
73 Format.print_newline
();
76 Printf.printf
" iso: %s(%d): " nm
line;
77 Pretty_print_cocci.pp_print_anything x
;
78 Format.print_newline
())
81 (*****************************************************************************)
82 (* Labeling function *)
83 (*****************************************************************************)
84 let (-->) x v
= Ast_ctl.Subst
(x
,v
);;
86 (* Take list of predicate and for each predicate returns where in the
87 * control flow it matches, and the set of subsitutions for this match.
89 let (labels_for_ctl
: string list
(* dropped isos *) ->
90 (nodei
* F.node
) list
-> Lib_engine.metavars_binding
->
91 Lib_engine.label_ctlcocci
) =
92 fun dropped_isos nodes binding
->
95 show_or_not_predicate p
;
97 let nodes'
= nodes +> List.map
(fun (nodei
, node
) ->
98 (* todo? put part of this code in pattern ? *)
99 (match p
, F.unwrap node
with
100 | Lib_engine.Paren s
, (F.SeqStart
(_
, bracelevel
, _
)) ->
101 let make_var x
= ("",i_to_s x
) in
102 [(nodei
, (p
,[(s
--> (Lib_engine.ParenVal
(make_var bracelevel
)))]))]
103 | Lib_engine.Paren s
, (F.SeqEnd
(bracelevel
, _
)) ->
104 let make_var x
= ("",i_to_s x
) in
105 [(nodei
, (p
,[(s
--> (Lib_engine.ParenVal
(make_var bracelevel
)))]))]
106 | Lib_engine.Paren _
, _
-> []
107 | Lib_engine.Label s
, _
->
108 let labels = F.extract_labels node
in
110 (p
,[(s
--> (Lib_engine.LabelVal
(Lib_engine.Absolute
labels)))]))]
111 | Lib_engine.BCLabel s
, _
->
112 (match F.extract_bclabels node
with
113 [] -> [] (* null for all nodes that are not break or continue *)
117 (Lib_engine.LabelVal
(Lib_engine.Absolute
labels)))]))])
118 | Lib_engine.PrefixLabel s
, _
->
119 let labels = F.extract_labels node
in
121 (p
,[(s
--> (Lib_engine.LabelVal
(Lib_engine.Prefix
labels)))]))]
123 | Lib_engine.Match
(re
), _unwrapnode
->
125 Pattern_c.match_re_node dropped_isos re node binding
126 +> List.map
(fun (re'
, subst
) ->
127 Lib_engine.Match
(re'
), subst
130 substs +> List.map
(fun (p'
, subst
) ->
133 subst
+> List.map
(fun (s
, meta
) ->
134 s
--> Lib_engine.NormalMetaVal meta
137 | Lib_engine.InLoop
, F.InLoopNode
-> [nodei
, (p
,[])]
138 | Lib_engine.TrueBranch
, F.TrueNode
-> [nodei
, (p
,[])]
139 | Lib_engine.FalseBranch
, F.FalseNode
-> [nodei
, (p
,[])]
140 | Lib_engine.After
, F.AfterNode
-> [nodei
, (p
,[])]
141 | Lib_engine.FallThrough
, F.FallThroughNode
-> [nodei
,(p
,[])]
142 | Lib_engine.LoopFallThrough
, F.LoopFallThroughNode
-> [nodei
,(p
,[])]
143 | Lib_engine.FunHeader
, F.FunHeader _
-> [nodei
, (p
,[])]
144 | Lib_engine.Top
, F.TopNode
-> [nodei
, (p
,[])]
145 | Lib_engine.Exit
, F.Exit
-> [nodei
, (p
,[])]
146 | Lib_engine.ErrorExit
, F.ErrorExit
-> [nodei
, (p
,[])]
147 | Lib_engine.Goto
, F.Goto
(_
,_
,_
) -> [nodei
, (p
,[])]
149 | Lib_engine.UnsafeBrace
, node
->
150 (* cases where it it not safe to put something on the outer side
153 F.FunHeader _
| F.DoHeader _
| F.TrueNode
| F.Else _
154 | F.InLoopNode
(* while, for *) | F.SwitchHeader _
->
158 | Lib_engine.InLoop
, _
-> []
159 | Lib_engine.TrueBranch
, _
-> []
160 | Lib_engine.FalseBranch
, _
-> []
161 | Lib_engine.After
, _
-> []
162 | Lib_engine.FallThrough
, _
-> []
163 | Lib_engine.LoopFallThrough
, _
-> []
164 | Lib_engine.FunHeader
, _
-> []
165 | Lib_engine.Top
, _
-> []
166 | Lib_engine.Exit
, _
-> []
167 | Lib_engine.ErrorExit
, _
-> []
168 | Lib_engine.Goto
, _
-> []
170 | Lib_engine.BindGood s
, _
-> [(nodei
, (p
,[(s
--> Lib_engine.GoodVal
)]))]
171 | Lib_engine.BindBad s
, _
-> [(nodei
, (p
,[(s
--> Lib_engine.BadVal
)]))]
172 | Lib_engine.FakeBrace
, _
->
173 if F.extract_is_fake node
then [nodei
, (p
,[])] else []
175 | Lib_engine.Return
, node
->
177 (* todo? should match the Exit code ?
178 * todo: one day try also to match the special function
181 | F.Return _
-> [nodei
, (p
,[])]
182 | F.ReturnExpr _
-> [nodei
, (p
,[])]
189 show_or_not_nodes nodes'
;
193 (*****************************************************************************)
194 (* Some fix flow, for CTL, for unparse *)
195 (*****************************************************************************)
196 (* could erase info on nodes, and edge, because they are not used by rene *)
197 let (control_flow_for_ctl
: F.cflow
-> ('a
, 'b
) ograph_mutable
) =
202 (* Just make the final node of the control flow loop over itself.
203 * It seems that one hypothesis of the SAT algorithm is that each node as at
206 * update: do same for errorexit node.
208 * update: also erase the fake nodes (and adjust the edges accordingly),
209 * so that AX in CTL can now work.
210 * Indeed, à la fin de la branche then (et else), on devrait aller directement
211 * au suivant du endif, sinon si ecrit if(1) { foo(); }; bar();
212 * sans '...' entre le if et bar(), alors ca matchera pas car le CTL
213 * generera un AX bar() qui il tombera d'abord sur le [endif] :(
214 * Mais chiant de changer l'algo de generation, marche pas tres bien avec
215 * ma facon de faire recursive et compositionnel.
216 * => faire une fonction qui applique des fixes autour de ce control flow,
217 * comme ca passe un bon flow a rene, mais garde un flow a moi pour pouvoir
218 * facilement generate back the ast.
219 * alt: faire un wrapper autourde mon graphe pour lui passer dans le module CFG
220 * une fonction qui passe a travers les Fake, mais bof.
222 * update: also make loop the deadcode nodes, the one that have
225 let (fix_flow_ctl2
: F.cflow
-> F.cflow
) = fun flow
->
228 let topi = F.first_node
!g in
229 !g#add_arc
((topi, topi), F.Direct
);
231 (* for the #define CFG who have no Exit but have at least a EndNode *)
233 let endi = F.find_node
(fun x
-> x
=*= F.EndNode
) !g in
234 !g#add_arc
((endi, endi), F.Direct
);
238 (* for the regular functions *)
240 let exitnodei = F.find_node
(fun x
-> x
=*= F.Exit
) !g in
241 let errornodei = F.find_node
(fun x
-> x
=*= F.ErrorExit
) !g in
243 !g#add_arc
((exitnodei, exitnodei), F.Direct
);
245 if null
((!g#successors
errornodei)#tolist
) &&
246 null
((!g#predecessors
errornodei)#tolist
)
247 then !g#del_node
errornodei
248 else !g#add_arc
((errornodei, errornodei), F.Direct
);
252 let fake_nodes = !g#
nodes#tolist
+> List.filter
(fun (nodei
, node
) ->
253 match F.unwrap node
with
256 (*| F.Fake*) (* [endif], [endswitch], ... *)
261 fake_nodes +> List.iter
(fun (nodei
, node
) -> F.remove_one_node nodei
!g);
263 (* even when have deadcode, julia want loop over those nodes *)
264 !g#
nodes#tolist
+> List.iter
(fun (nodei
, node
) ->
265 if (!g#predecessors nodei
)#null
267 let fakei = !g#add_node
(F.mk_node
F.Fake
[] [] "DEADCODELOOP") in
268 !g#add_arc
((fakei, nodei
), F.Direct
);
269 !g#add_arc
((fakei, fakei), F.Direct
);
273 !g#
nodes#tolist
+> List.iter
(fun (nodei
, node
) ->
274 assert (List.length
((!g#successors nodei
)#tolist
) >= 1);
275 (* no: && List.length ((!g#predecessors nodei)#tolist) >= 1
276 because the enter node at least have no predecessors *)
281 Common.profile_code
"fix_flow" (fun () -> fix_flow_ctl2 a
)
287 (*****************************************************************************)
288 (* subtil: the label must operate on newflow, not (old) cflow
289 * update: now I supposed that we give me a fixed_flow
291 let model_for_ctl dropped_isos cflow binding
=
292 let newflow = cflow
(* old: fix_flow_ctl (control_flow_for_ctl cflow) *) in
293 let labels = labels_for_ctl dropped_isos
(newflow#
nodes#tolist
) binding
in
294 let states = List.map fst
newflow#
nodes#tolist
in
295 newflow, labels, states
298 (*****************************************************************************)
302 type t
= Lib_engine.predicate
303 let print_predicate x
=
304 Pretty_print_cocci.print_plus_flag
:= false;
305 Pretty_print_cocci.print_minus_flag
:= false;
306 Pretty_print_engine.pp_predicate x
309 (* prefix has to be nonempty *)
311 let rec loop = function
314 | (x
::xs
,y
::ys
) when x
= y
-> loop (xs
,ys
)
318 let compatible_labels l1 l2
=
320 (Lib_engine.Absolute
(l1
),Lib_engine.Absolute
(l2
)) -> l1
=*= l2
321 | (Lib_engine.Absolute
(l1
),Lib_engine.Prefix
(l2
)) -> prefix l1 l2
322 | (Lib_engine.Prefix
(l1
),Lib_engine.Absolute
(l2
)) -> prefix l2 l1
323 | (Lib_engine.Prefix
(l1
),Lib_engine.Prefix
(l2
)) ->
324 not
(l1
= []) && not
(l2
= []) &&
325 List.hd l1
=*= List.hd l2
(* labels are never empty *)
327 let merge_labels l1 l2
=
329 (* known to be compatible *)
330 (Lib_engine.Absolute
(_
),Lib_engine.Absolute
(_
)) -> l1
331 | (Lib_engine.Absolute
(_
),Lib_engine.Prefix
(_
)) -> l1
332 | (Lib_engine.Prefix
(_
),Lib_engine.Absolute
(_
)) -> l2
333 | (Lib_engine.Prefix
(l1
),Lib_engine.Prefix
(l2
)) ->
334 let rec max_prefix = function
335 (x
::xs
,y
::ys
) when x
= y
-> x
::(max_prefix(xs
,ys
))
337 Lib_engine.Prefix
(max_prefix(l1
,l2
))
341 type value = Lib_engine.metavar_binding_kind2
342 type mvar
= Ast_cocci.meta_name
343 let eq_mvar x x'
= x
=*= x'
347 (Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min1
,max1
)),
348 Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min2
,max2
))) ->
349 ((min1
<= min2
) && (max1
>= max2
)) or
350 ((min2
<= min1
) && (max2
>= max1
))
351 | (Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal a
),
352 Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal b
)) ->
354 | (Lib_engine.LabelVal
(l1
),Lib_engine.LabelVal
(l2
)) ->
355 compatible_labels l1 l2
357 let merge_val v v'
= (* values guaranteed to be compatible *)
360 (Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min1
,max1
)),
361 Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min2
,max2
))) ->
362 if (min1
<= min2
) && (max1
>= max2
)
363 then Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min1
,max1
))
365 if (min2
<= min1
) && (max2
>= max1
)
366 then Lib_engine.NormalMetaVal
(Ast_c.MetaPosVal
(min2
,max2
))
367 else failwith
"incompatible positions give to merge"
368 | (Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal a
),
369 Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal b
)) ->
370 Lib_engine.NormalMetaVal
(Ast_c.MetaTypeVal
(C_vs_c.merge_type a b
))
371 | (Lib_engine.LabelVal
(l1
),Lib_engine.LabelVal
(l2
)) ->
372 Lib_engine.LabelVal
(merge_labels l1 l2
)
375 let print_mvar (_
,s
) = Format.print_string s
376 let print_value x
= Pretty_print_engine.pp_binding_kind2 x
381 type node
= Ograph_extended.nodei
382 type cfg
= (F.node
, F.edge
) Ograph_extended.ograph_mutable
383 let predecessors cfg n
= List.map fst
((cfg#
predecessors n
)#tolist
)
384 let successors cfg n
= List.map fst
((cfg#
successors n
)#tolist
)
385 let extract_is_loop cfg n
=
386 Control_flow_c.extract_is_loop (cfg#
nodes#find n
)
387 let print_node i
= Format.print_string
(i_to_s i
)
388 let size cfg
= cfg#
nodes#length
390 (* In ctl_engine, we use 'node' for the node but in the Ograph_extended
391 * terminology, this 'node' is in fact an index to access the real
392 * node information (that ctl/ wants to abstract away to be more generic),
393 * the 'Ograph_extended.nodei'.
395 let print_graph cfg label border_colors fill_colors filename
=
396 Ograph_extended.print_ograph_mutable_generic cfg label
397 (fun (nodei
, (node
: F.node
)) ->
398 (* the string julia wants to put ? *)
399 let bc = try Some
(List.assoc nodei border_colors
) with _
-> None
in
400 let fc = try Some
(List.assoc nodei fill_colors
) with _
-> None
in
401 (* the string yoann put as debug information in the cfg *)
402 let str = snd node
in
405 ~output_file
:filename
410 module WRAPPED_ENGINE
= Wrapper_ctl.CTL_ENGINE_BIS
(ENV
) (CFG
) (PRED
)
412 let print_bench _
= WRAPPED_ENGINE.print_bench()
414 type pred
= Lib_engine.predicate
* Ast_cocci.meta_name
Ast_ctl.modif
416 (*****************************************************************************)
417 let metavars_binding2_to_binding binding2
=
418 binding2
+> Common.map_filter
(fun (s
, kind2
) ->
420 | Lib_engine.NormalMetaVal kind
-> Some
(s
, kind
)
421 (* I thought it was Impossible to have this when called from
422 satbis_to_trans_info but it does not seems so *)
423 | Lib_engine.ParenVal _
-> None
424 | Lib_engine.LabelVal _
-> None
425 | Lib_engine.BadVal
-> None
(* should not occur *)
426 | Lib_engine.GoodVal
-> None
(* should not occur *)
429 let metavars_binding_to_binding2 binding
=
430 binding
+> List.map
(fun (s
, kind
) -> s
, Lib_engine.NormalMetaVal kind
)
433 let (satbis_to_trans_info
:
435 (nodei
* Lib_engine.metavars_binding2
* Lib_engine.predicate
)) list
->
437 (nodei
* Lib_engine.metavars_binding
* Ast_cocci.rule_elem
)) list
) =
439 xs
+> List.fold_left
(fun prev
(index
,(nodei
, binding2
, pred
)) ->
441 | Lib_engine.Match
(rule_elem
) ->
442 if !Flag.track_iso_usage
then show_isos rule_elem
;
444 (nodei
, metavars_binding2_to_binding binding2
, rule_elem
))
446 (* see BindGood in asttotctl2 *)
447 | Lib_engine.BindGood
(_
) -> prev
448 | _
-> raise
(Impossible
50)
451 (*****************************************************************************)
453 let rec coalesce_positions = function
455 | (x
,Ast_c.MetaPosValList l
)::rest
->
456 let (same
,others
) = List.partition
(function (x1
,_
) -> x
=*= x1
) rest
in
461 (_
,Ast_c.MetaPosValList l
) -> l
462 | _
-> failwith
"unexpected non-position")
464 let new_ls = List.sort compare
(l
@ls) in
465 (x
,Ast_c.MetaPosValList
new_ls) :: coalesce_positions others
466 | x
::rest
-> x
:: coalesce_positions rest
473 Ast_c.MetaExprVal
(a
,c
) ->
474 Ast_c.MetaExprVal
(Lib_parsing_c.al_inh_expr a
,c
)
475 | Ast_c.MetaExprListVal a
->
476 Ast_c.MetaExprListVal
(Lib_parsing_c.al_inh_arguments a
)
477 | Ast_c.MetaStmtVal a
->
478 Ast_c.MetaStmtVal
(Lib_parsing_c.al_inh_statement a
)
479 | Ast_c.MetaInitVal a
->
480 Ast_c.MetaInitVal
(Lib_parsing_c.al_inh_init a
)
481 | Ast_c.MetaInitListVal a
->
482 Ast_c.MetaInitListVal
(Lib_parsing_c.al_inh_inits a
)
483 | x
-> (*don't contain binding info*) x
in
487 (* these remove constraints, at least those that contain pcre regexps,
488 which cannot be compared (problem in the unparser) *)
489 let strip_predicate re
=
490 let donothing r k e
= k e
in
494 match Ast_cocci.unwrap
e with
495 Ast_cocci.MetaId
(name
,constraints
,u
,i
) ->
497 (Ast_cocci.MetaId
(name
,Ast_cocci.IdNoConstraint
,u
,i
))
498 | Ast_cocci.MetaFunc
(name
,constraints
,u
,i
) ->
500 (Ast_cocci.MetaFunc
(name
,Ast_cocci.IdNoConstraint
,u
,i
))
501 | Ast_cocci.MetaLocalFunc
(name
,constraints
,u
,i
) ->
503 (Ast_cocci.MetaLocalFunc
(name
,Ast_cocci.IdNoConstraint
,u
,i
))
505 let expression r k
e =
507 match Ast_cocci.unwrap
e with
508 Ast_cocci.MetaErr
(name
,constraints
,u
,i
) ->
510 (Ast_cocci.MetaErr
(name
,Ast_cocci.NoConstraint
,u
,i
))
511 | Ast_cocci.MetaExpr
(name
,constraints
,u
,ty
,form
,i
) ->
513 (Ast_cocci.MetaExpr
(name
,Ast_cocci.NoConstraint
,u
,ty
,form
,i
))
515 let fn = Visitor_ast.rebuilder
516 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
517 donothing donothing donothing donothing donothing
518 ident expression donothing donothing donothing donothing
519 donothing donothing donothing donothing donothing donothing in
521 fn.Visitor_ast.rebuilder_rule_elem re
523 let clean_trans_info2 trans_info2
=
527 (function (node
,env
,pred
) ->
530 Lib_engine.Match re
-> Lib_engine.Match
(strip_predicate re
)
539 | (x
::xs
) when (List.mem x xs
) -> nub xs
540 | (x
::xs
) -> x
::(nub xs
)
542 (*****************************************************************************)
543 (* Call ctl engine *)
544 (***************************************************** ************************)
547 (Lib_engine.ctlcocci
* (pred list list
)) ->
548 (string (*rulename*) * Lib_engine.mvar list
*Lib_engine.metavars_binding
) ->
549 (Lib_engine.numbered_transformation_info
* bool *
550 Lib_engine.metavars_binding
* Lib_engine.metavars_binding list
)) =
551 fun (flow
, label
, states) ctl
(rulename
, used_after
, binding
) ->
552 let binding2 = metavars_binding_to_binding2 binding
in
553 let (triples
,(trans_info2
, returned_any_states
, used_after_envs
)) =
554 WRAPPED_ENGINE.satbis
(flow
, label
, states) ctl
555 (used_after
, binding2)
557 (* drop constraints in the predicate at the end of each match.
558 constraints aren't needed for transformation, and they can contain
559 regular expressions, which are incomparable. *)
560 let trans_info2 = clean_trans_info2 trans_info2 in
561 if not
(!Flag_parsing_cocci.sgrep_mode
|| !Flag.sgrep_mode2
||
562 !Flag_matcher.allow_inconsistent_paths
)
563 then Check_reachability.check_reachability rulename triples flow
;
564 let (trans_info2,used_after_fresh_envs
) =
565 Postprocess_transinfo.process used_after
binding2 trans_info2 in
566 let used_after_envs =
567 Common.uniq
(List.map2
(@) used_after_fresh_envs
used_after_envs) in
568 let trans_info = satbis_to_trans_info
trans_info2 in
569 let newbindings = List.map
metavars_binding2_to_binding used_after_envs in
570 let newbindings = List.map
coalesce_positions newbindings in
571 let newbindings = List.map
strip newbindings in
572 let newbindings = nub newbindings in
573 (trans_info, returned_any_states
, binding
, newbindings)
576 Common.profile_code
"mysat" (fun () -> mysat2 a b c
)