9101c5025e03f0c5941d5ff2fff441aba78a6491
[bpt/coccinelle.git] / cocci.ml
1 (*
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.
7 *
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.
11 *
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.
16 *
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/>.
19 *
20 * The authors reserve the right to distribute this or future versions of
21 * Coccinelle under other licenses.
22 *)
23
24
25 open Common
26
27 module CCI = Ctlcocci_integration
28 module TAC = Type_annoter_c
29
30 module Ast_to_flow = Control_flow_c_build
31
32 (*****************************************************************************)
33 (* This file is a kind of driver. It gathers all the important functions
34 * from coccinelle in one place. The different entities in coccinelle are:
35 * - files
36 * - astc
37 * - astcocci
38 * - flow (contain nodes)
39 * - ctl (contain rule_elems)
40 * This file contains functions to transform one in another.
41 *)
42 (*****************************************************************************)
43
44 (* --------------------------------------------------------------------- *)
45 (* C related *)
46 (* --------------------------------------------------------------------- *)
47 let cprogram_of_file file =
48 let (program2, _stat) = Parse_c.parse_c_and_cpp file in
49 program2
50
51 let cprogram_of_file_cached file =
52 let (program2, _stat) = Parse_c.parse_cache file in
53 if !Flag_cocci.ifdef_to_if
54 then
55 program2 +> Parse_c.with_program2 (fun asts ->
56 Cpp_ast_c.cpp_ifdef_statementize asts
57 )
58 else program2
59
60 let cfile_of_program program2_with_ppmethod outf =
61 Unparse_c.pp_program program2_with_ppmethod outf
62
63 (* for memoization, contains only one entry, the one for the SP *)
64 let _hparse = Hashtbl.create 101
65 let _hctl = Hashtbl.create 101
66
67 (* --------------------------------------------------------------------- *)
68 (* Cocci related *)
69 (* --------------------------------------------------------------------- *)
70 let sp_of_file2 file iso =
71 Common.memoized _hparse (file, iso) (fun () ->
72 let (_,xs,_,_,_,_,_,_) as res = Parse_cocci.process file iso false in
73 (match Prepare_ocamlcocci.prepare file xs with
74 None -> ()
75 | Some ocaml_script_file ->
76 (* compile file *)
77 Prepare_ocamlcocci.load_file ocaml_script_file;
78 if not !Common.save_tmp_files
79 then Prepare_ocamlcocci.clean_file ocaml_script_file);
80 res)
81 let sp_of_file file iso =
82 Common.profile_code "parse cocci" (fun () -> sp_of_file2 file iso)
83
84
85 (* --------------------------------------------------------------------- *)
86 (* Flow related *)
87 (* --------------------------------------------------------------------- *)
88 let print_flow flow =
89 Ograph_extended.print_ograph_mutable flow "/tmp/test.dot" true
90
91
92 let ast_to_flow_with_error_messages2 x =
93 let flowopt =
94 try Ast_to_flow.ast_to_control_flow x
95 with Ast_to_flow.Error x ->
96 Ast_to_flow.report_error x;
97 None
98 in
99 flowopt +> do_option (fun flow ->
100 (* This time even if there is a deadcode, we still have a
101 * flow graph, so I can try the transformation and hope the
102 * deadcode will not bother us.
103 *)
104 try Ast_to_flow.deadcode_detection flow
105 with Ast_to_flow.Error (Ast_to_flow.DeadCode x) ->
106 Ast_to_flow.report_error (Ast_to_flow.DeadCode x);
107 );
108 flowopt
109 let ast_to_flow_with_error_messages a =
110 Common.profile_code "flow" (fun () -> ast_to_flow_with_error_messages2 a)
111
112
113 (* --------------------------------------------------------------------- *)
114 (* Ctl related *)
115 (* --------------------------------------------------------------------- *)
116
117 let ctls_of_ast2 ast (ua,fua,fuas) pos =
118 List.map2
119 (function ast -> function (ua,(fua,(fuas,pos))) ->
120 List.combine
121 (if !Flag_cocci.popl
122 then Popl.popl ast
123 else Asttoctl2.asttoctl ast (ua,fua,fuas) pos)
124 (Asttomember.asttomember ast ua))
125 ast (List.combine ua (List.combine fua (List.combine fuas pos)))
126
127 let ctls_of_ast ast ua =
128 Common.profile_code "asttoctl2" (fun () -> ctls_of_ast2 ast ua)
129
130 (*****************************************************************************)
131 (* Some debugging functions *)
132 (*****************************************************************************)
133
134 (* the inputs *)
135
136 let show_or_not_cfile2 cfile =
137 if !Flag_cocci.show_c then begin
138 Common.pr2_xxxxxxxxxxxxxxxxx ();
139 pr2 ("processing C file: " ^ cfile);
140 Common.pr2_xxxxxxxxxxxxxxxxx ();
141 Common.command2 ("cat " ^ cfile);
142 end
143 let show_or_not_cfile a =
144 Common.profile_code "show_xxx" (fun () -> show_or_not_cfile2 a)
145
146 let show_or_not_cfiles cfiles = List.iter show_or_not_cfile cfiles
147
148
149 let show_or_not_cocci2 coccifile isofile =
150 if !Flag_cocci.show_cocci then begin
151 Common.pr2_xxxxxxxxxxxxxxxxx ();
152 pr2 ("processing semantic patch file: " ^ coccifile);
153 isofile +> (fun s -> pr2 ("with isos from: " ^ s));
154 Common.pr2_xxxxxxxxxxxxxxxxx ();
155 Common.command2 ("cat " ^ coccifile);
156 pr2 "";
157 end
158 let show_or_not_cocci a b =
159 Common.profile_code "show_xxx" (fun () -> show_or_not_cocci2 a b)
160
161 (* ---------------------------------------------------------------------- *)
162 (* the output *)
163
164 let fix_sgrep_diffs l =
165 let l =
166 List.filter (function s -> (s =~ "^\\+\\+\\+") || not (s =~ "^\\+")) l in
167 let l = List.rev l in
168 (* adjust second number for + code *)
169 let rec loop1 n = function
170 [] -> []
171 | s::ss ->
172 if s =~ "^-" && not(s =~ "^---")
173 then s :: loop1 (n+1) ss
174 else if s =~ "^@@"
175 then
176 (match Str.split (Str.regexp " ") s with
177 bef::min::pl::aft ->
178 let (n1,n2) =
179 match Str.split (Str.regexp ",") pl with
180 [n1;n2] -> (n1,n2)
181 | [n1] -> (n1,"1")
182 | _ -> failwith "bad + line information" in
183 let n2 = int_of_string n2 in
184 (Printf.sprintf "%s %s %s,%d %s" bef min n1 (n2-n)
185 (String.concat " " aft))
186 :: loop1 0 ss
187 | _ -> failwith "bad @@ information")
188 else s :: loop1 n ss in
189 let rec loop2 n = function
190 [] -> []
191 | s::ss ->
192 if s =~ "^---"
193 then s :: loop2 0 ss
194 else if s =~ "^@@"
195 then
196 (match Str.split (Str.regexp " ") s with
197 bef::min::pl::aft ->
198 let (m2,n1,n2) =
199 match (Str.split (Str.regexp ",") min,
200 Str.split (Str.regexp ",") pl) with
201 ([_;m2],[n1;n2]) -> (m2,n1,n2)
202 | ([_],[n1;n2]) -> ("1",n1,n2)
203 | ([_;m2],[n1]) -> (m2,n1,"1")
204 | ([_],[n1]) -> ("1",n1,"1")
205 | _ -> failwith "bad -/+ line information" in
206 let n1 =
207 int_of_string (String.sub n1 1 ((String.length n1)-1)) in
208 let m2 = int_of_string m2 in
209 let n2 = int_of_string n2 in
210 (Printf.sprintf "%s %s +%d,%d %s" bef min (n1-n) n2
211 (String.concat " " aft))
212 :: loop2 (n+(m2-n2)) ss
213 | _ -> failwith "bad @@ information")
214 else s :: loop2 n ss in
215 loop2 0 (List.rev (loop1 0 l))
216
217 let normalize_path file =
218 let fullpath =
219 if String.get file 0 = '/' then file else (Sys.getcwd()) ^ "/" ^ file in
220 let elements = Str.split_delim (Str.regexp "/") fullpath in
221 let rec loop prev = function
222 [] -> String.concat "/" (List.rev prev)
223 | "." :: rest -> loop prev rest
224 | ".." :: rest ->
225 (match prev with
226 x::xs -> loop xs rest
227 | _ -> failwith "bad path")
228 | x::rest -> loop (x::prev) rest in
229 loop [] elements
230
231 let show_or_not_diff2 cfile outfile =
232 if !Flag_cocci.show_diff then begin
233 match Common.fst(Compare_c.compare_to_original cfile outfile) with
234 Compare_c.Correct -> () (* diff only in spacing, etc *)
235 | _ ->
236 (* may need --strip-trailing-cr under windows *)
237 pr2 "diff = ";
238
239 let line =
240 match !Flag_parsing_c.diff_lines with
241 | None -> "diff -u -p " ^ cfile ^ " " ^ outfile
242 | Some n -> "diff -U "^n^" -p "^cfile^" "^outfile in
243 let xs =
244 let res = Common.cmd_to_list line in
245 match (!Flag.patch,res) with
246 (* create something that looks like the output of patch *)
247 (Some prefix,minus_file::plus_file::rest) ->
248 let prefix =
249 let lp = String.length prefix in
250 if String.get prefix (lp-1) = '/'
251 then String.sub prefix 0 (lp-1)
252 else prefix in
253 let drop_prefix file =
254 let file = normalize_path file in
255 if Str.string_match (Str.regexp prefix) file 0
256 then
257 let lp = String.length prefix in
258 let lf = String.length file in
259 if lp < lf
260 then String.sub file lp (lf - lp)
261 else
262 failwith
263 (Printf.sprintf "prefix %s doesn't match file %s"
264 prefix file)
265 else
266 failwith
267 (Printf.sprintf "prefix %s doesn't match file %s"
268 prefix file) in
269 let diff_line =
270 match List.rev(Str.split (Str.regexp " ") line) with
271 new_file::old_file::cmdrev ->
272 let old_base_file = drop_prefix old_file in
273 if !Flag.sgrep_mode2
274 then
275 String.concat " "
276 (List.rev
277 (("/tmp/nothing"^old_base_file)
278 :: old_file :: cmdrev))
279 else
280 String.concat " "
281 (List.rev
282 (("b"^old_base_file)::("a"^old_base_file)::cmdrev))
283 | _ -> failwith "bad command" in
284 let (minus_line,plus_line) =
285 match (Str.split (Str.regexp "[ \t]") minus_file,
286 Str.split (Str.regexp "[ \t]") plus_file) with
287 ("---"::old_file::old_rest,"+++"::new_file::new_rest) ->
288 let old_base_file = drop_prefix old_file in
289 if !Flag.sgrep_mode2
290 then (minus_file,"+++ /tmp/nothing"^old_base_file)
291 else
292 (String.concat " "
293 ("---"::("a"^old_base_file)::old_rest),
294 String.concat " "
295 ("+++"::("b"^old_base_file)::new_rest))
296 | (l1,l2) ->
297 failwith
298 (Printf.sprintf "bad diff header lines: %s %s"
299 (String.concat ":" l1) (String.concat ":" l2)) in
300 diff_line::minus_line::plus_line::rest
301 | _ -> res in
302 let xs = if !Flag.sgrep_mode2 then fix_sgrep_diffs xs else xs in
303 xs +> List.iter pr
304 end
305 let show_or_not_diff a b =
306 Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b)
307
308
309 (* the derived input *)
310
311 let show_or_not_ctl_tex2 astcocci ctls =
312 if !Flag_cocci.show_ctl_tex then begin
313 Ctltotex.totex ("/tmp/__cocci_ctl.tex") astcocci ctls;
314 Common.command2 ("cd /tmp; latex __cocci_ctl.tex; " ^
315 "dvips __cocci_ctl.dvi -o __cocci_ctl.ps;" ^
316 "gv __cocci_ctl.ps &");
317 end
318 let show_or_not_ctl_tex a b =
319 Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_tex2 a b)
320
321
322 let show_or_not_rule_name ast rulenb =
323 if !Flag_cocci.show_ctl_text or !Flag.show_trying or
324 !Flag.show_transinfo or !Flag_cocci.show_binding_in_out
325 then
326 begin
327 let name =
328 match ast with
329 Ast_cocci.CocciRule (nm, (deps, drops, exists), x, _, _) -> nm
330 | _ -> i_to_s rulenb in
331 Common.pr_xxxxxxxxxxxxxxxxx ();
332 pr (name ^ " = ");
333 Common.pr_xxxxxxxxxxxxxxxxx ()
334 end
335
336 let show_or_not_scr_rule_name rulenb =
337 if !Flag_cocci.show_ctl_text or !Flag.show_trying or
338 !Flag.show_transinfo or !Flag_cocci.show_binding_in_out
339 then
340 begin
341 let name = i_to_s rulenb in
342 Common.pr_xxxxxxxxxxxxxxxxx ();
343 pr ("script rule " ^ name ^ " = ");
344 Common.pr_xxxxxxxxxxxxxxxxx ()
345 end
346
347 let show_or_not_ctl_text2 ctl ast rulenb =
348 if !Flag_cocci.show_ctl_text then begin
349
350 adjust_pp_with_indent (fun () ->
351 Format.force_newline();
352 Pretty_print_cocci.print_plus_flag := true;
353 Pretty_print_cocci.print_minus_flag := true;
354 Pretty_print_cocci.unparse ast;
355 );
356
357 pr "CTL = ";
358 let (ctl,_) = ctl in
359 adjust_pp_with_indent (fun () ->
360 Format.force_newline();
361 Pretty_print_engine.pp_ctlcocci
362 !Flag_cocci.show_mcodekind_in_ctl !Flag_cocci.inline_let_ctl ctl;
363 );
364 pr "";
365 end
366 let show_or_not_ctl_text a b c =
367 Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_text2 a b c)
368
369
370
371 (* running information *)
372 let get_celem celem : string =
373 match celem with
374 Ast_c.Definition ({Ast_c.f_name = namefuncs;},_) ->
375 Ast_c.str_of_name namefuncs
376 | Ast_c.Declaration
377 (Ast_c.DeclList ([{Ast_c.v_namei = Some (name, _);}, _], _)) ->
378 Ast_c.str_of_name name
379 | _ -> ""
380
381 let show_or_not_celem2 prelude celem =
382 let (tag,trying) =
383 (match celem with
384 | Ast_c.Definition ({Ast_c.f_name = namefuncs},_) ->
385 let funcs = Ast_c.str_of_name namefuncs in
386 Flag.current_element := funcs;
387 (" function: ",funcs)
388 | Ast_c.Declaration
389 (Ast_c.DeclList ([{Ast_c.v_namei = Some (name,_)}, _], _)) ->
390 let s = Ast_c.str_of_name name in
391 Flag.current_element := s;
392 (" variable ",s);
393 | _ ->
394 Flag.current_element := "something_else";
395 (" ","something else");
396 ) in
397 if !Flag.show_trying then pr2 (prelude ^ tag ^ trying)
398
399 let show_or_not_celem a b =
400 Common.profile_code "show_xxx" (fun () -> show_or_not_celem2 a b)
401
402
403 let show_or_not_trans_info2 trans_info =
404 (* drop witness tree indices for printing *)
405 let trans_info =
406 List.map (function (index,trans_info) -> trans_info) trans_info in
407 if !Flag.show_transinfo then begin
408 if null trans_info then pr2 "transformation info is empty"
409 else begin
410 pr2 "transformation info returned:";
411 let trans_info =
412 List.sort (function (i1,_,_) -> function (i2,_,_) -> compare i1 i2)
413 trans_info
414 in
415 indent_do (fun () ->
416 trans_info +> List.iter (fun (i, subst, re) ->
417 pr2 ("transform state: " ^ (Common.i_to_s i));
418 indent_do (fun () ->
419 adjust_pp_with_indent_and_header "with rule_elem: " (fun () ->
420 Pretty_print_cocci.print_plus_flag := true;
421 Pretty_print_cocci.print_minus_flag := true;
422 Pretty_print_cocci.rule_elem "" re;
423 );
424 adjust_pp_with_indent_and_header "with binding: " (fun () ->
425 Pretty_print_engine.pp_binding subst;
426 );
427 )
428 );
429 )
430 end
431 end
432 let show_or_not_trans_info a =
433 Common.profile_code "show_xxx" (fun () -> show_or_not_trans_info2 a)
434
435
436
437 let show_or_not_binding2 s binding =
438 if !Flag_cocci.show_binding_in_out then begin
439 adjust_pp_with_indent_and_header ("binding " ^ s ^ " = ") (fun () ->
440 Pretty_print_engine.pp_binding binding
441 )
442 end
443 let show_or_not_binding a b =
444 Common.profile_code "show_xxx" (fun () -> show_or_not_binding2 a b)
445
446
447
448 (*****************************************************************************)
449 (* Some helper functions *)
450 (*****************************************************************************)
451
452 let worth_trying cfiles tokens =
453 (* drop the following line for a list of list by rules. since we don't
454 allow multiple minirules, all the tokens within a rule should be in
455 a single CFG entity *)
456 match (!Flag_cocci.windows,tokens) with
457 (true,_) | (_,None) -> true
458 | (_,Some tokens) ->
459 (* could also modify the code in get_constants.ml *)
460 let tokens = tokens +> List.map (fun s ->
461 match () with
462 | _ when s =~ "^[A-Za-z_][A-Za-z_0-9]*$" ->
463 "\\b" ^ s ^ "\\b"
464
465 | _ when s =~ "^[A-Za-z_]" ->
466 "\\b" ^ s
467
468 | _ when s =~ ".*[A-Za-z_]$" ->
469 s ^ "\\b"
470 | _ -> s
471
472 ) in
473 let com = sprintf "egrep -q '(%s)' %s" (join "|" tokens) (join " " cfiles)
474 in
475 (match Sys.command com with
476 | 0 (* success *) -> true
477 | _ (* failure *) ->
478 (if !Flag.show_misc
479 then Printf.printf "grep failed: %s\n" com);
480 false (* no match, so not worth trying *))
481
482 let check_macro_in_sp_and_adjust = function
483 None -> ()
484 | Some tokens ->
485 tokens +> List.iter (fun s ->
486 if Hashtbl.mem !Parse_c._defs s
487 then begin
488 if !Flag_cocci.verbose_cocci then begin
489 pr2 "warning: macro in semantic patch was in macro definitions";
490 pr2 ("disabling macro expansion for " ^ s);
491 end;
492 Hashtbl.remove !Parse_c._defs s
493 end)
494
495
496 let contain_loop gopt =
497 match gopt with
498 | Some g ->
499 g#nodes#tolist +> List.exists (fun (xi, node) ->
500 Control_flow_c.extract_is_loop node
501 )
502 | None -> true (* means nothing, if no g then will not model check *)
503
504
505
506 let sp_contain_typed_metavar_z toplevel_list_list =
507 let bind x y = x or y in
508 let option_default = false in
509 let mcode _ _ = option_default in
510 let donothing r k e = k e in
511
512 let expression r k e =
513 match Ast_cocci.unwrap e with
514 | Ast_cocci.MetaExpr (_,_,_,Some t,_,_) -> true
515 | Ast_cocci.MetaExpr (_,_,_,_,Ast_cocci.LocalID,_) -> true
516 | _ -> k e
517 in
518
519 let combiner =
520 Visitor_ast.combiner bind option_default
521 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
522 donothing donothing donothing donothing donothing
523 donothing expression donothing donothing donothing donothing donothing
524 donothing donothing donothing donothing donothing
525 in
526 toplevel_list_list +>
527 List.exists
528 (function (nm,_,rule) ->
529 (List.exists combiner.Visitor_ast.combiner_top_level rule))
530
531 let sp_contain_typed_metavar rules =
532 sp_contain_typed_metavar_z
533 (List.map
534 (function x ->
535 match x with
536 Ast_cocci.CocciRule (a,b,c,d,_) -> (a,b,c)
537 | _ -> failwith "error in filter")
538 (List.filter
539 (function x ->
540 match x with
541 Ast_cocci.CocciRule (a,b,c,d,Ast_cocci.Normal) -> true
542 | _ -> false)
543 rules))
544
545
546
547 (* finding among the #include the one that we need to parse
548 * because they may contain useful type definition or because
549 * we may have to modify them
550 *
551 * For the moment we base in part our heuristic on the name of the file, e.g.
552 * serio.c is related we think to #include <linux/serio.h>
553 *)
554 let rec search_include_path searchlist relpath =
555 match searchlist with
556 [] -> Some relpath
557 | hd::tail ->
558 let file = Filename.concat hd relpath in
559 if Sys.file_exists file then
560 Some file
561 else
562 search_include_path tail relpath
563
564 let interpret_include_path relpath =
565 let searchlist =
566 match !Flag_cocci.include_path with
567 [] -> ["include"]
568 | x -> List.rev x
569 in
570 search_include_path searchlist relpath
571
572 let (includes_to_parse:
573 (Common.filename * Parse_c.program2) list ->
574 Flag_cocci.include_options -> 'a) = fun xs choose_includes ->
575 match choose_includes with
576 Flag_cocci.I_UNSPECIFIED -> failwith "not possible"
577 | Flag_cocci.I_NO_INCLUDES -> []
578 | x ->
579 let all_includes = x =*= Flag_cocci.I_ALL_INCLUDES in
580 xs +> List.map (fun (file, cs) ->
581 let dir = Common.dirname file in
582
583 cs +> Common.map_filter (fun (c,_info_item) ->
584 match c with
585 | Ast_c.CppTop
586 (Ast_c.Include
587 {Ast_c.i_include = ((x,ii)); i_rel_pos = info_h_pos;}) ->
588 (match x with
589 | Ast_c.Local xs ->
590 let relpath = Common.join "/" xs in
591 let f = Filename.concat dir (relpath) in
592 if (Sys.file_exists f) then
593 Some f
594 else
595 if !Flag_cocci.relax_include_path
596 (* for our tests, all the files are flat in the current dir *)
597 then
598 let attempt2 = Filename.concat dir (Common.last xs) in
599 if not (Sys.file_exists attempt2) && all_includes
600 then
601 interpret_include_path relpath
602 else Some attempt2
603 else
604 if all_includes then interpret_include_path relpath
605 else None
606
607 | Ast_c.NonLocal xs ->
608 let relpath = Common.join "/" xs in
609 if all_includes ||
610 Common.fileprefix (Common.last xs) =$= Common.fileprefix file
611 then
612 interpret_include_path relpath
613 else None
614 | Ast_c.Weird _ -> None
615 )
616 | _ -> None))
617 +> List.concat
618 +> Common.uniq
619
620 let rec interpret_dependencies local global = function
621 Ast_cocci.Dep s -> List.mem s local
622 | Ast_cocci.AntiDep s ->
623 (if !Flag_ctl.steps != None
624 then failwith "steps and ! dependency incompatible");
625 not (List.mem s local)
626 | Ast_cocci.EverDep s -> List.mem s global
627 | Ast_cocci.NeverDep s ->
628 (if !Flag_ctl.steps != None
629 then failwith "steps and ! dependency incompatible");
630 not (List.mem s global)
631 | Ast_cocci.AndDep(s1,s2) ->
632 (interpret_dependencies local global s1) &&
633 (interpret_dependencies local global s2)
634 | Ast_cocci.OrDep(s1,s2) ->
635 (interpret_dependencies local global s1) or
636 (interpret_dependencies local global s2)
637 | Ast_cocci.NoDep -> true
638 | Ast_cocci.FailDep -> false
639
640 let rec print_dependencies str local global dep =
641 if !Flag_cocci.show_dependencies
642 then
643 begin
644 pr2 str;
645 let seen = ref [] in
646 let rec loop = function
647 Ast_cocci.Dep s | Ast_cocci.AntiDep s ->
648 if not (List.mem s !seen)
649 then
650 begin
651 if List.mem s local
652 then pr2 (s^" satisfied")
653 else pr2 (s^" not satisfied");
654 seen := s :: !seen
655 end
656 | Ast_cocci.EverDep s | Ast_cocci.NeverDep s ->
657 if not (List.mem s !seen)
658 then
659 begin
660 if List.mem s global
661 then pr2 (s^" satisfied")
662 else pr2 (s^" not satisfied");
663 seen := s :: !seen
664 end
665 | Ast_cocci.AndDep(s1,s2) ->
666 loop s1;
667 loop s2
668 | Ast_cocci.OrDep(s1,s2) ->
669 loop s1;
670 loop s2
671 | Ast_cocci.NoDep -> ()
672 | Ast_cocci.FailDep -> pr2 "False not satisfied" in
673 loop dep
674 end
675
676 (* --------------------------------------------------------------------- *)
677 (* #include relative position in the file *)
678 (* --------------------------------------------------------------------- *)
679
680 (* compute the set of new prefixes
681 * on
682 * "a/b/x"; (* in fact it is now a list of string so ["a";"b";"x"] *)
683 * "a/b/c/x";
684 * "a/x";
685 * "b/x";
686 * it would give for the first element
687 * ""; "a"; "a/b"; "a/b/x"
688 * for the second
689 * "a/b/c/x"
690 *
691 * update: if the include is inside a ifdef a put nothing. cf -test incl.
692 * this is because we dont want code added inside ifdef.
693 *)
694
695 let compute_new_prefixes xs =
696 xs +> Common.map_withenv (fun already xs ->
697 let subdirs_prefixes = Common.inits xs in
698 let new_first = subdirs_prefixes +> List.filter (fun x ->
699 not (List.mem x already)
700 )
701 in
702 new_first,
703 new_first @ already
704 ) []
705 +> fst
706
707
708 (* does via side effect on the ref in the Include in Ast_c *)
709 let rec update_include_rel_pos cs =
710 let only_include = cs +> Common.map_filter (fun c ->
711 match c with
712 | Ast_c.CppTop (Ast_c.Include {Ast_c.i_include = ((x,_));
713 i_rel_pos = aref;
714 i_is_in_ifdef = inifdef}) ->
715 (match x with
716 | Ast_c.Weird _ -> None
717 | _ ->
718 if inifdef
719 then None
720 else Some (x, aref)
721 )
722 | _ -> None
723 )
724 in
725 let (locals, nonlocals) =
726 only_include +> Common.partition_either (fun (c, aref) ->
727 match c with
728 | Ast_c.Local x -> Left (x, aref)
729 | Ast_c.NonLocal x -> Right (x, aref)
730 | Ast_c.Weird x -> raise Impossible
731 ) in
732
733 update_rel_pos_bis locals;
734 update_rel_pos_bis nonlocals;
735 cs
736 and update_rel_pos_bis xs =
737 let xs' = List.map fst xs in
738 let the_first = compute_new_prefixes xs' in
739 let the_last = List.rev (compute_new_prefixes (List.rev xs')) in
740 let merged = Common.zip xs (Common.zip the_first the_last) in
741 merged +> List.iter (fun ((x, aref), (the_first, the_last)) ->
742 aref := Some
743 {
744 Ast_c.first_of = the_first;
745 Ast_c.last_of = the_last;
746 }
747 )
748
749
750 (*****************************************************************************)
751 (* All the information needed around the C elements and Cocci rules *)
752 (*****************************************************************************)
753
754 type toplevel_c_info = {
755 ast_c: Ast_c.toplevel; (* contain refs so can be modified *)
756 tokens_c: Parser_c.token list;
757 fullstring: string;
758
759 flow: Control_flow_c.cflow option; (* it's the "fixed" flow *)
760 contain_loop: bool;
761
762 env_typing_before: TAC.environment;
763 env_typing_after: TAC.environment;
764
765 was_modified: bool ref;
766
767 (* id: int *)
768 }
769
770 type rule_info = {
771 rulename: string;
772 dependencies: Ast_cocci.dependency;
773 used_after: Ast_cocci.meta_name list;
774 ruleid: int;
775 was_matched: bool ref;
776 }
777
778 type toplevel_cocci_info_script_rule = {
779 scr_ast_rule:
780 string *
781 (Ast_cocci.script_meta_name * Ast_cocci.meta_name *
782 Ast_cocci.metavar) list *
783 Ast_cocci.meta_name list (*fresh vars*) *
784 string;
785 language: string;
786 script_code: string;
787 scr_rule_info: rule_info;
788 }
789
790 type toplevel_cocci_info_cocci_rule = {
791 ctl: Lib_engine.ctlcocci * (CCI.pred list list);
792 metavars: Ast_cocci.metavar list;
793 ast_rule: Ast_cocci.rule;
794 isexp: bool; (* true if + code is an exp, only for Flag.make_hrule *)
795
796 (* There are also some hardcoded rule names in parse_cocci.ml:
797 * let reserved_names = ["all";"optional_storage";"optional_qualifier"]
798 *)
799 dropped_isos: string list;
800 free_vars: Ast_cocci.meta_name list;
801 negated_pos_vars: Ast_cocci.meta_name list;
802 positions: Ast_cocci.meta_name list;
803
804 ruletype: Ast_cocci.ruletype;
805
806 rule_info: rule_info;
807 }
808
809 type toplevel_cocci_info =
810 ScriptRuleCocciInfo of toplevel_cocci_info_script_rule
811 | InitialScriptRuleCocciInfo of toplevel_cocci_info_script_rule
812 | FinalScriptRuleCocciInfo of toplevel_cocci_info_script_rule
813 | CocciRuleCocciInfo of toplevel_cocci_info_cocci_rule
814
815 type cocci_info = toplevel_cocci_info list * string list option (* tokens *)
816
817 type kind_file = Header | Source
818 type file_info = {
819 fname : string;
820 full_fname : string;
821 was_modified_once: bool ref;
822 asts: toplevel_c_info list;
823 fpath : string;
824 fkind : kind_file;
825 }
826
827 let g_contain_typedmetavar = ref false
828
829
830 let last_env_toplevel_c_info xs =
831 (Common.last xs).env_typing_after
832
833 let concat_headers_and_c (ccs: file_info list)
834 : (toplevel_c_info * string) list =
835 (List.concat (ccs +> List.map (fun x ->
836 x.asts +> List.map (fun x' ->
837 (x', x.fname)))))
838
839 let for_unparser xs =
840 xs +> List.map (fun x ->
841 (x.ast_c, (x.fullstring, x.tokens_c)), Unparse_c.PPviastr
842 )
843
844 let gen_pdf_graph () =
845 (Ctl_engine.get_graph_files ()) +> List.iter (fun outfile ->
846 Printf.printf "Generation of %s%!" outfile;
847 let filename_stack = Ctl_engine.get_graph_comp_files outfile in
848 List.iter (fun filename ->
849 ignore (Unix.system ("dot " ^ filename ^ " -Tpdf -o " ^ filename ^ ".pdf;"))
850 ) filename_stack;
851 let (head,tail) = (List.hd filename_stack, List.tl filename_stack) in
852 ignore(Unix.system ("cp " ^ head ^ ".pdf " ^ outfile ^ ".pdf;"));
853 tail +> List.iter (fun filename ->
854 ignore(Unix.system ("mv " ^ outfile ^ ".pdf /tmp/tmp.pdf;"));
855 ignore(Unix.system ("pdftk " ^ filename ^ ".pdf /tmp/tmp.pdf cat output " ^ outfile ^ ".pdf"));
856 );
857 ignore(Unix.system ("rm /tmp/tmp.pdf;"));
858 List.iter (fun filename ->
859 ignore (Unix.system ("rm " ^ filename ^ " " ^ filename ^ ".pdf;"))
860 ) filename_stack;
861 Printf.printf " - Done\n")
862
863 let local_python_code =
864 "from coccinelle import *\n"
865
866 let python_code =
867 "import coccinelle\n"^
868 "import coccilib\n"^
869 "import coccilib.org\n"^
870 "import coccilib.report\n" ^
871 local_python_code ^
872 "cocci = Cocci()\n"
873
874 let make_init lang code rule_info =
875 let mv = [] in
876 {
877 scr_ast_rule = (lang, mv, [], code);
878 language = lang;
879 script_code = (if lang = "python" then python_code else "") ^code;
880 scr_rule_info = rule_info;
881 }
882
883 (* --------------------------------------------------------------------- *)
884 let prepare_cocci ctls free_var_lists negated_pos_lists
885 (ua,fua,fuas) positions_list metavars astcocci =
886
887 let gathered = Common.index_list_1
888 (zip (zip (zip (zip (zip (zip (zip (zip ctls metavars) astcocci)
889 free_var_lists)
890 negated_pos_lists) ua) fua) fuas) positions_list)
891 in
892 gathered +> List.map
893 (fun (((((((((ctl_toplevel_list,metavars),ast),free_var_list),
894 negated_pos_list),ua),fua),fuas),positions_list),rulenb) ->
895
896 let build_rule_info rulename deps =
897 {rulename = rulename;
898 dependencies = deps;
899 used_after = (List.hd ua) @ (List.hd fua);
900 ruleid = rulenb;
901 was_matched = ref false;} in
902
903 let is_script_rule r =
904 match r with
905 Ast_cocci.ScriptRule _
906 | Ast_cocci.InitialScriptRule _ | Ast_cocci.FinalScriptRule _ -> true
907 | _ -> false in
908
909 if not (List.length ctl_toplevel_list =|= 1) && not (is_script_rule ast)
910 then failwith "not handling multiple minirules";
911
912 match ast with
913 Ast_cocci.ScriptRule (name,lang,deps,mv,script_vars,code) ->
914 let r =
915 {
916 scr_ast_rule = (lang, mv, script_vars, code);
917 language = lang;
918 script_code = code;
919 scr_rule_info = build_rule_info name deps;
920 }
921 in ScriptRuleCocciInfo r
922 | Ast_cocci.InitialScriptRule (name,lang,deps,code) ->
923 let r = make_init lang code (build_rule_info name deps) in
924 InitialScriptRuleCocciInfo r
925 | Ast_cocci.FinalScriptRule (name,lang,deps,code) ->
926 let mv = [] in
927 let r =
928 {
929 scr_ast_rule = (lang, mv, [], code);
930 language = lang;
931 script_code = code;
932 scr_rule_info = build_rule_info name deps;
933 }
934 in FinalScriptRuleCocciInfo r
935 | Ast_cocci.CocciRule
936 (rulename,(dependencies,dropped_isos,z),restast,isexp,ruletype) ->
937 CocciRuleCocciInfo (
938 {
939 ctl = List.hd ctl_toplevel_list;
940 metavars = metavars;
941 ast_rule = ast;
942 isexp = List.hd isexp;
943 dropped_isos = dropped_isos;
944 free_vars = List.hd free_var_list;
945 negated_pos_vars = List.hd negated_pos_list;
946 positions = List.hd positions_list;
947 ruletype = ruletype;
948 rule_info = build_rule_info rulename dependencies;
949 })
950 )
951
952 (* --------------------------------------------------------------------- *)
953
954 let build_info_program cprogram env =
955
956 let (cs, parseinfos) =
957 Common.unzip cprogram in
958
959 let alltoks =
960 parseinfos +> List.map (fun (s,toks) -> toks) +> List.flatten in
961
962 (* I use cs' but really annotate_xxx work by doing side effects on cs *)
963 let cs' =
964 Comment_annotater_c.annotate_program alltoks cs in
965 let cs_with_envs =
966 Type_annoter_c.annotate_program env (*!g_contain_typedmetavar*) cs'
967 in
968
969 zip cs_with_envs parseinfos +> List.map (fun ((c, (enva,envb)), parseinfo)->
970 let (fullstr, tokens) = parseinfo in
971
972 let flow =
973 ast_to_flow_with_error_messages c +>
974 Common.map_option (fun flow ->
975 let flow = Ast_to_flow.annotate_loop_nodes flow in
976
977 (* remove the fake nodes for julia *)
978 let fixed_flow = CCI.fix_flow_ctl flow in
979
980 if !Flag_cocci.show_flow then print_flow fixed_flow;
981 if !Flag_cocci.show_before_fixed_flow then print_flow flow;
982
983 fixed_flow
984 )
985 in
986
987 {
988 ast_c = c; (* contain refs so can be modified *)
989 tokens_c = tokens;
990 fullstring = fullstr;
991
992 flow = flow;
993
994 contain_loop = contain_loop flow;
995
996 env_typing_before = enva;
997 env_typing_after = envb;
998
999 was_modified = ref false;
1000 }
1001 )
1002
1003
1004
1005 (* Optimisation. Try not unparse/reparse the whole file when have modifs *)
1006 let rebuild_info_program cs file isexp =
1007 cs +> List.map (fun c ->
1008 if !(c.was_modified)
1009 then
1010 let file = Common.new_temp_file "cocci_small_output" ".c" in
1011 cfile_of_program
1012 [(c.ast_c, (c.fullstring, c.tokens_c)), Unparse_c.PPnormal]
1013 file;
1014
1015 (* Common.command2 ("cat " ^ file); *)
1016 let cprogram = cprogram_of_file file in
1017 let xs = build_info_program cprogram c.env_typing_before in
1018
1019 (* TODO: assert env has not changed,
1020 * if yes then must also reparse what follows even if not modified.
1021 * Do that only if contain_typedmetavar of course, so good opti.
1022 *)
1023 (* Common.list_init xs *) (* get rid of the FinalDef *)
1024 xs
1025 else [c]
1026 ) +> List.concat
1027
1028
1029 let rebuild_info_c_and_headers ccs isexp =
1030 ccs +> List.iter (fun c_or_h ->
1031 if c_or_h.asts +> List.exists (fun c -> !(c.was_modified))
1032 then c_or_h.was_modified_once := true;
1033 );
1034 ccs +> List.map (fun c_or_h ->
1035 { c_or_h with
1036 asts =
1037 rebuild_info_program c_or_h.asts c_or_h.full_fname isexp }
1038 )
1039
1040
1041
1042 let prepare_c files choose_includes : file_info list =
1043 let cprograms = List.map cprogram_of_file_cached files in
1044 let includes = includes_to_parse (zip files cprograms) choose_includes in
1045
1046 (* todo?: may not be good to first have all the headers and then all the c *)
1047 let all =
1048 (includes +> List.map (fun hpath -> Right hpath))
1049 ++
1050 ((zip files cprograms) +>
1051 List.map (fun (file, asts) -> Left (file, asts)))
1052 in
1053
1054 let env = ref !TAC.initial_env in
1055
1056 let ccs = all +> Common.map_filter (fun x ->
1057 match x with
1058 | Right hpath ->
1059 if not (Common.lfile_exists hpath)
1060 then begin
1061 pr2 ("TYPE: header " ^ hpath ^ " not found");
1062 None
1063 end
1064 else
1065 let h_cs = cprogram_of_file_cached hpath in
1066 let info_h_cs = build_info_program h_cs !env in
1067 env :=
1068 if null info_h_cs
1069 then !env
1070 else last_env_toplevel_c_info info_h_cs
1071 ;
1072 Some {
1073 fname = Common.basename hpath;
1074 full_fname = hpath;
1075 asts = info_h_cs;
1076 was_modified_once = ref false;
1077 fpath = hpath;
1078 fkind = Header;
1079 }
1080 | Left (file, cprogram) ->
1081 (* todo?: don't update env ? *)
1082 let cs = build_info_program cprogram !env in
1083 (* we do that only for the c, not for the h *)
1084 ignore(update_include_rel_pos (cs +> List.map (fun x -> x.ast_c)));
1085 Some {
1086 fname = Common.basename file;
1087 full_fname = file;
1088 asts = cs;
1089 was_modified_once = ref false;
1090 fpath = file;
1091 fkind = Source;
1092 }
1093 )
1094 in
1095 ccs
1096
1097
1098 (*****************************************************************************)
1099 (* Processing the ctls and toplevel C elements *)
1100 (*****************************************************************************)
1101
1102 (* The main algorithm =~
1103 * The algorithm is roughly:
1104 * for_all ctl rules in SP
1105 * for_all minirule in rule (no more)
1106 * for_all binding (computed during previous phase)
1107 * for_all C elements
1108 * match control flow of function vs minirule
1109 * with the binding and update the set of possible
1110 * bindings, and returned the possibly modified function.
1111 * pretty print modified C elements and reparse it.
1112 *
1113 *
1114 * On ne prends que les newbinding ou returned_any_state est vrai.
1115 * Si ca ne donne rien, on prends ce qu'il y avait au depart.
1116 * Mais au nouveau depart de quoi ?
1117 * - si ca donne rien apres avoir traité toutes les fonctions avec ce binding ?
1118 * - ou alors si ca donne rien, apres avoir traité toutes les fonctions
1119 * avec tous les bindings du round d'avant ?
1120 *
1121 * Julia pense qu'il faut prendre la premiere solution.
1122 * Example: on a deux environnements candidats, E1 et E2 apres avoir traité
1123 * la regle ctl 1. On arrive sur la regle ctl 2.
1124 * E1 ne donne rien pour la regle 2, on garde quand meme E1 pour la regle 3.
1125 * E2 donne un match a un endroit et rend E2' alors on utilise ca pour
1126 * la regle 3.
1127 *
1128 * I have not to look at used_after_list to decide to restart from
1129 * scratch. I just need to look if the binding list is empty.
1130 * Indeed, let's suppose that a SP have 3 regions/rules. If we
1131 * don't find a match for the first region, then if this first
1132 * region does not bind metavariable used after, that is if
1133 * used_after_list is empty, then mysat(), even if does not find a
1134 * match, will return a Left, with an empty transformation_info,
1135 * and so current_binding will grow. On the contrary if the first
1136 * region must bind some metavariables used after, and that we
1137 * dont find any such region, then mysat() will returns lots of
1138 * Right, and current_binding will not grow, and so we will have
1139 * an empty list of binding, and we will catch such a case.
1140 *
1141 * opti: julia says that because the binding is
1142 * determined by the used_after_list, the items in the list
1143 * are kind of sorted, so could optimise the insert_set operations.
1144 *)
1145
1146
1147 (* r(ule), c(element in C code), e(nvironment) *)
1148
1149 let findk f l =
1150 let rec loop k = function
1151 [] -> None
1152 | x::xs ->
1153 if f x
1154 then Some (x, function n -> k (n :: xs))
1155 else loop (function vs -> k (x :: vs)) xs in
1156 loop (function x -> x) l
1157
1158 let merge_env new_e old_e =
1159 let (ext,old_e) =
1160 List.fold_left
1161 (function (ext,old_e) ->
1162 function (e,rules) as elem ->
1163 match findk (function (e1,_) -> e =*= e1) old_e with
1164 None -> (elem :: ext,old_e)
1165 | Some((_,old_rules),k) ->
1166 (ext,k (e,Common.union_set rules old_rules)))
1167 ([],old_e) new_e in
1168 old_e @ (List.rev ext)
1169
1170 let contains_binding e (_,(r,m),_) =
1171 try
1172 let _ = List.find (function ((re, rm), _) -> r =*= re && m =$= rm) e in
1173 true
1174 with Not_found -> false
1175
1176 let python_application mv ve script_vars r =
1177 let mv =
1178 List.map
1179 (function
1180 ((Some x,None),y,z) -> (x,y,z)
1181 | _ ->
1182 failwith
1183 (Printf.sprintf "unexpected ast metavar in rule %s"
1184 r.scr_rule_info.rulename))
1185 mv in
1186 try
1187 Pycocci.build_classes (List.map (function (x,y) -> x) ve);
1188 Pycocci.construct_variables mv ve;
1189 Pycocci.construct_script_variables script_vars;
1190 let _ = Pycocci.pyrun_simplestring (local_python_code ^r.script_code) in
1191 if !Pycocci.inc_match
1192 then Some (Pycocci.retrieve_script_variables script_vars)
1193 else None
1194 with Pycocci.Pycocciexception ->
1195 (pr2 ("Failure in " ^ r.scr_rule_info.rulename);
1196 raise Pycocci.Pycocciexception)
1197
1198 let ocaml_application mv ve script_vars r =
1199 try
1200 let script_vals =
1201 Run_ocamlcocci.run mv ve script_vars
1202 r.scr_rule_info.rulename r.script_code in
1203 if !Coccilib.inc_match
1204 then Some script_vals
1205 else None
1206 with e -> (pr2 ("Failure in " ^ r.scr_rule_info.rulename); raise e)
1207
1208 let apply_script_rule r cache newes e rules_that_have_matched
1209 rules_that_have_ever_matched script_application =
1210 Common.profile_code r.language (fun () ->
1211 show_or_not_scr_rule_name r.scr_rule_info.ruleid;
1212 if not(interpret_dependencies rules_that_have_matched
1213 !rules_that_have_ever_matched r.scr_rule_info.dependencies)
1214 then
1215 begin
1216 print_dependencies "dependencies for script not satisfied:"
1217 rules_that_have_matched
1218 !rules_that_have_ever_matched r.scr_rule_info.dependencies;
1219 show_or_not_binding "in environment" e;
1220 (cache, (e, rules_that_have_matched)::newes)
1221 end
1222 else
1223 begin
1224 let (_, mv, script_vars, _) = r.scr_ast_rule in
1225 let ve =
1226 (List.map (function (n,v) -> (("virtual",n),Ast_c.MetaIdVal (v,[])))
1227 !Flag.defined_virtual_env) @ e in
1228 let not_bound x = not (contains_binding ve x) in
1229 (match List.filter not_bound mv with
1230 [] ->
1231 let relevant_bindings =
1232 List.filter
1233 (function ((re,rm),_) ->
1234 List.exists (function (_,(r,m),_) -> r =*= re && m =$= rm) mv)
1235 e in
1236 (try
1237 match List.assoc relevant_bindings cache with
1238 None -> (cache,newes)
1239 | Some script_vals ->
1240 print_dependencies
1241 "dependencies for script satisfied, but cached:"
1242 rules_that_have_matched
1243 !rules_that_have_ever_matched
1244 r.scr_rule_info.dependencies;
1245 show_or_not_binding "in" e;
1246 (* env might be bigger than what was cached against, so have to
1247 merge with newes anyway *)
1248 let new_e = (List.combine script_vars script_vals) @ e in
1249 let new_e =
1250 new_e +>
1251 List.filter
1252 (fun (s,v) -> List.mem s r.scr_rule_info.used_after) in
1253 (cache,merge_env [(new_e, rules_that_have_matched)] newes)
1254 with Not_found ->
1255 begin
1256 print_dependencies "dependencies for script satisfied:"
1257 rules_that_have_matched
1258 !rules_that_have_ever_matched
1259 r.scr_rule_info.dependencies;
1260 show_or_not_binding "in" e;
1261 match script_application mv ve script_vars r with
1262 None ->
1263 (* failure means we should drop e, no new bindings *)
1264 (((relevant_bindings,None) :: cache), newes)
1265 | Some script_vals ->
1266 let script_vals =
1267 List.map (function x -> Ast_c.MetaIdVal(x,[]))
1268 script_vals in
1269 let new_e =
1270 (List.combine script_vars script_vals) @ e in
1271 let new_e =
1272 new_e +>
1273 List.filter
1274 (fun (s,v) -> List.mem s r.scr_rule_info.used_after) in
1275 r.scr_rule_info.was_matched := true;
1276 (((relevant_bindings,Some script_vals) :: cache),
1277 merge_env
1278 [(new_e,
1279 r.scr_rule_info.rulename :: rules_that_have_matched)]
1280 newes)
1281 end)
1282 | unbound ->
1283 (if !Flag_cocci.show_dependencies
1284 then
1285 let m2c (_,(r,x),_) = r^"."^x in
1286 pr2 (Printf.sprintf "script not applied: %s not bound"
1287 (String.concat ", " (List.map m2c unbound))));
1288 let e =
1289 e +>
1290 List.filter
1291 (fun (s,v) -> List.mem s r.scr_rule_info.used_after) in
1292 (cache, merge_env [(e, rules_that_have_matched)] newes))
1293 end)
1294
1295 let rec apply_cocci_rule r rules_that_have_ever_matched es
1296 (ccs:file_info list ref) =
1297 Common.profile_code r.rule_info.rulename (fun () ->
1298 show_or_not_rule_name r.ast_rule r.rule_info.ruleid;
1299 show_or_not_ctl_text r.ctl r.ast_rule r.rule_info.ruleid;
1300
1301 let reorganized_env =
1302 reassociate_positions r.free_vars r.negated_pos_vars !es in
1303
1304 (* looping over the environments *)
1305 let (_,newes (* envs for next round/rule *)) =
1306 List.fold_left
1307 (function (cache,newes) ->
1308 function ((e,rules_that_have_matched),relevant_bindings) ->
1309 if not(interpret_dependencies rules_that_have_matched
1310 !rules_that_have_ever_matched
1311 r.rule_info.dependencies)
1312 then
1313 begin
1314 print_dependencies
1315 ("dependencies for rule "^r.rule_info.rulename^
1316 " not satisfied:")
1317 rules_that_have_matched
1318 !rules_that_have_ever_matched r.rule_info.dependencies;
1319 show_or_not_binding "in environment" e;
1320 (cache,
1321 merge_env
1322 [(e +>
1323 List.filter
1324 (fun (s,v) -> List.mem s r.rule_info.used_after),
1325 rules_that_have_matched)]
1326 newes)
1327 end
1328 else
1329 let new_bindings =
1330 try List.assoc relevant_bindings cache
1331 with
1332 Not_found ->
1333 print_dependencies
1334 ("dependencies for rule "^r.rule_info.rulename^
1335 " satisfied:")
1336 rules_that_have_matched
1337 !rules_that_have_ever_matched
1338 r.rule_info.dependencies;
1339 show_or_not_binding "in" e;
1340 show_or_not_binding "relevant in" relevant_bindings;
1341
1342 (* applying the rule *)
1343 (match r.ruletype with
1344 Ast_cocci.Normal ->
1345 (* looping over the functions and toplevel elements in
1346 .c and .h *)
1347 List.rev
1348 (concat_headers_and_c !ccs +>
1349 List.fold_left (fun children_e (c,f) ->
1350 if c.flow <> None
1351 then
1352 (* does also some side effects on c and r *)
1353 let processed =
1354 process_a_ctl_a_env_a_toplevel r
1355 relevant_bindings c f in
1356 match processed with
1357 | None -> children_e
1358 | Some newbindings ->
1359 newbindings +>
1360 List.fold_left
1361 (fun children_e newbinding ->
1362 if List.mem newbinding children_e
1363 then children_e
1364 else newbinding :: children_e)
1365 children_e
1366 else children_e)
1367 [])
1368 | Ast_cocci.Generated ->
1369 process_a_generated_a_env_a_toplevel r
1370 relevant_bindings !ccs;
1371 []) in
1372
1373 let old_bindings_to_keep =
1374 Common.nub
1375 (e +>
1376 List.filter
1377 (fun (s,v) -> List.mem s r.rule_info.used_after)) in
1378 let new_e =
1379 if null new_bindings
1380 then
1381 begin
1382 (*use the old bindings, specialized to the used_after_list*)
1383 if !Flag_ctl.partial_match
1384 then
1385 printf
1386 "Empty list of bindings, I will restart from old env\n";
1387 [(old_bindings_to_keep,rules_that_have_matched)]
1388 end
1389 else
1390 (* combine the new bindings with the old ones, and
1391 specialize to the used_after_list *)
1392 let old_variables = List.map fst old_bindings_to_keep in
1393 (* have to explicitly discard the inherited variables
1394 because we want the inherited value of the positions
1395 variables not the extended one created by
1396 reassociate_positions. want to reassociate freshly
1397 according to the free variables of each rule. *)
1398 let new_bindings_to_add =
1399 Common.nub
1400 (new_bindings +>
1401 List.map
1402 (List.filter
1403 (function
1404 (* see comment before combine_pos *)
1405 (s,Ast_c.MetaPosValList []) -> false
1406 | (s,v) ->
1407 List.mem s r.rule_info.used_after &&
1408 not (List.mem s old_variables)))) in
1409 List.map
1410 (function new_binding_to_add ->
1411 (List.sort compare
1412 (Common.union_set
1413 old_bindings_to_keep new_binding_to_add),
1414 r.rule_info.rulename::rules_that_have_matched))
1415 new_bindings_to_add in
1416 ((relevant_bindings,new_bindings)::cache,
1417 merge_env new_e newes))
1418 ([],[]) reorganized_env in (* end iter es *)
1419 if !(r.rule_info.was_matched)
1420 then Common.push2 r.rule_info.rulename rules_that_have_ever_matched;
1421
1422 es := newes;
1423
1424 (* apply the tagged modifs and reparse *)
1425 if not !Flag.sgrep_mode2
1426 then ccs := rebuild_info_c_and_headers !ccs r.isexp)
1427
1428 and reassociate_positions free_vars negated_pos_vars envs =
1429 (* issues: isolate the bindings that are relevant to a given rule.
1430 separate out the position variables
1431 associate all of the position variables for a given set of relevant
1432 normal variable bindings with each set of relevant normal variable
1433 bindings. Goal: if eg if@p (E) matches in two places, then both inherited
1434 occurrences of E should see both bindings of p, not just its own.
1435 Otherwise, a position constraint for something that matches in two
1436 places will never be useful, because the position can always be
1437 different from the other one. *)
1438 let relevant =
1439 List.map
1440 (function (e,_) ->
1441 List.filter (function (x,_) -> List.mem x free_vars) e)
1442 envs in
1443 let splitted_relevant =
1444 (* separate the relevant variables into the non-position ones and the
1445 position ones *)
1446 List.map
1447 (function r ->
1448 List.fold_left
1449 (function (non_pos,pos) ->
1450 function (v,_) as x ->
1451 if List.mem v negated_pos_vars
1452 then (non_pos,x::pos)
1453 else (x::non_pos,pos))
1454 ([],[]) r)
1455 relevant in
1456 let splitted_relevant =
1457 List.map
1458 (function (non_pos,pos) ->
1459 (List.sort compare non_pos,List.sort compare pos))
1460 splitted_relevant in
1461 let non_poss =
1462 List.fold_left
1463 (function non_pos ->
1464 function (np,_) ->
1465 if List.mem np non_pos then non_pos else np::non_pos)
1466 [] splitted_relevant in
1467 let extended_relevant =
1468 (* extend the position variables with the values found at other identical
1469 variable bindings *)
1470 List.map
1471 (function non_pos ->
1472 let others =
1473 List.filter
1474 (function (other_non_pos,other_pos) ->
1475 (* do we want equal? or just somehow compatible? eg non_pos
1476 binds only E, but other_non_pos binds both E and E1 *)
1477 non_pos =*= other_non_pos)
1478 splitted_relevant in
1479 (non_pos,
1480 List.sort compare
1481 (non_pos @
1482 (combine_pos negated_pos_vars
1483 (List.map (function (_,x) -> x) others)))))
1484 non_poss in
1485 List.combine envs
1486 (List.map (function (non_pos,_) -> List.assoc non_pos extended_relevant)
1487 splitted_relevant)
1488
1489 (* If the negated posvar is not bound at all, this function will
1490 nevertheless bind it to []. If we get rid of these bindings, then the
1491 matching of the term the position variable with the constraints will fail
1492 because some variables are unbound. So we let the binding be [] and then
1493 we will have to clean these up afterwards. This should be the only way
1494 that a position variable can have an empty binding. *)
1495 and combine_pos negated_pos_vars others =
1496 List.map
1497 (function posvar ->
1498 let positions =
1499 List.sort compare
1500 (List.fold_left
1501 (function positions ->
1502 function other_list ->
1503 try
1504 match List.assoc posvar other_list with
1505 Ast_c.MetaPosValList l1 ->
1506 Common.union_set l1 positions
1507 | _ -> failwith "bad value for a position variable"
1508 with Not_found -> positions)
1509 [] others) in
1510 (posvar,Ast_c.MetaPosValList positions))
1511 negated_pos_vars
1512
1513 and process_a_generated_a_env_a_toplevel2 r env = function
1514 [cfile] ->
1515 let free_vars =
1516 List.filter
1517 (function
1518 (rule,_) when rule =$= r.rule_info.rulename -> false
1519 | (_,"ARGS") -> false
1520 | _ -> true)
1521 r.free_vars in
1522 let env_domain = List.map (function (nm,vl) -> nm) env in
1523 let metavars =
1524 List.filter
1525 (function md ->
1526 let (rl,_) = Ast_cocci.get_meta_name md in rl =$= r.rule_info.rulename)
1527 r.metavars in
1528 if Common.include_set free_vars env_domain
1529 then Unparse_hrule.pp_rule metavars r.ast_rule env cfile.full_fname
1530 | _ -> failwith "multiple files not supported"
1531
1532 and process_a_generated_a_env_a_toplevel rule env ccs =
1533 Common.profile_code "process_a_ctl_a_env_a_toplevel"
1534 (fun () -> process_a_generated_a_env_a_toplevel2 rule env ccs)
1535
1536 (* does side effects on C ast and on Cocci info rule *)
1537 and process_a_ctl_a_env_a_toplevel2 r e c f =
1538 indent_do (fun () ->
1539 show_or_not_celem "trying" c.ast_c;
1540 Flag.currentfile := Some (f ^ ":" ^get_celem c.ast_c);
1541 let (trans_info, returned_any_states, inherited_bindings, newbindings) =
1542 Common.save_excursion Flag_ctl.loop_in_src_code (fun () ->
1543 Flag_ctl.loop_in_src_code := !Flag_ctl.loop_in_src_code||c.contain_loop;
1544
1545 (***************************************)
1546 (* !Main point! The call to the engine *)
1547 (***************************************)
1548 let model_ctl = CCI.model_for_ctl r.dropped_isos (Common.some c.flow) e
1549 in CCI.mysat model_ctl r.ctl (r.rule_info.used_after, e)
1550 )
1551 in
1552 if not returned_any_states
1553 then None
1554 else begin
1555 show_or_not_celem "found match in" c.ast_c;
1556 show_or_not_trans_info trans_info;
1557 List.iter (show_or_not_binding "out") newbindings;
1558
1559 r.rule_info.was_matched := true;
1560
1561 if not (null trans_info) &&
1562 not (!Flag.sgrep_mode2 && not !Flag_cocci.show_diff)
1563 then begin
1564 c.was_modified := true;
1565 try
1566 (* les "more than one var in a decl" et "already tagged token"
1567 * font crasher coccinelle. Si on a 5 fichiers, donc on a 5
1568 * failed. Le try limite le scope des crashes pendant la
1569 * trasformation au fichier concerne. *)
1570
1571 (* modify ast via side effect *)
1572 ignore(Transformation_c.transform r.rule_info.rulename r.dropped_isos
1573 inherited_bindings trans_info (Common.some c.flow));
1574 with Timeout -> raise Timeout | UnixExit i -> raise (UnixExit i)
1575 end;
1576
1577 Some (List.map (function x -> x@inherited_bindings) newbindings)
1578 end
1579 )
1580
1581 and process_a_ctl_a_env_a_toplevel a b c f=
1582 Common.profile_code "process_a_ctl_a_env_a_toplevel"
1583 (fun () -> process_a_ctl_a_env_a_toplevel2 a b c f)
1584
1585
1586 let rec bigloop2 rs (ccs: file_info list) =
1587 let init_es = [(Ast_c.emptyMetavarsBinding,[])] in
1588 let es = ref init_es in
1589 let ccs = ref ccs in
1590 let rules_that_have_ever_matched = ref [] in
1591
1592 (* looping over the rules *)
1593 rs +> List.iter (fun r ->
1594 match r with
1595 InitialScriptRuleCocciInfo r | FinalScriptRuleCocciInfo r -> ()
1596 | ScriptRuleCocciInfo r ->
1597 if !Flag_cocci.show_ctl_text then begin
1598 Common.pr_xxxxxxxxxxxxxxxxx ();
1599 pr ("script: " ^ r.language);
1600 Common.pr_xxxxxxxxxxxxxxxxx ();
1601
1602 adjust_pp_with_indent (fun () ->
1603 Format.force_newline();
1604 let (l,mv,script_vars,code) = r.scr_ast_rule in
1605 let nm = r.scr_rule_info.rulename in
1606 let deps = r.scr_rule_info.dependencies in
1607 Pretty_print_cocci.unparse
1608 (Ast_cocci.ScriptRule (nm,l,deps,mv,script_vars,code)));
1609 end;
1610
1611 if !Flag.show_misc then print_endline "RESULT =";
1612
1613 let (_, newes) =
1614 List.fold_left
1615 (function (cache, newes) ->
1616 function (e, rules_that_have_matched) ->
1617 match r.language with
1618 "python" ->
1619 apply_script_rule r cache newes e rules_that_have_matched
1620 rules_that_have_ever_matched python_application
1621 | "ocaml" ->
1622 apply_script_rule r cache newes e rules_that_have_matched
1623 rules_that_have_ever_matched ocaml_application
1624 | "test" ->
1625 concat_headers_and_c !ccs +> List.iter (fun (c,_) ->
1626 if c.flow <> None
1627 then
1628 Printf.printf "Flow: %s\r\nFlow!\r\n%!" c.fullstring);
1629 (cache, newes)
1630 | _ ->
1631 Printf.printf "Unknown language: %s\n" r.language;
1632 (cache, newes))
1633 ([],[]) !es in
1634
1635 (if !(r.scr_rule_info.was_matched)
1636 then
1637 Common.push2 r.scr_rule_info.rulename rules_that_have_ever_matched);
1638
1639 es := newes (*(if newes = [] then init_es else newes)*);
1640 | CocciRuleCocciInfo r ->
1641 apply_cocci_rule r rules_that_have_ever_matched
1642 es ccs);
1643
1644 if !Flag.sgrep_mode2
1645 then begin
1646 (* sgrep can lead to code that is not parsable, but we must
1647 * still call rebuild_info_c_and_headers to pretty print the
1648 * action (MINUS), so that later the diff will show what was
1649 * matched by sgrep. But we don't want the parsing error message
1650 * hence the following flag setting. So this code propably
1651 * will generate a NotParsedCorrectly for the matched parts
1652 * and the very final pretty print and diff will work
1653 *)
1654 Flag_parsing_c.verbose_parsing := false;
1655 ccs := rebuild_info_c_and_headers !ccs false
1656 end;
1657 !ccs (* return final C asts *)
1658
1659 let bigloop a b =
1660 Common.profile_code "bigloop" (fun () -> bigloop2 a b)
1661
1662 type init_final = Initial | Final
1663
1664 let initial_final_bigloop2 ty rebuild r =
1665 if !Flag_cocci.show_ctl_text then
1666 begin
1667 Common.pr_xxxxxxxxxxxxxxxxx ();
1668 pr ((match ty with Initial -> "initial" | Final -> "final") ^ ": " ^
1669 r.language);
1670 Common.pr_xxxxxxxxxxxxxxxxx ();
1671
1672 adjust_pp_with_indent (fun () ->
1673 Format.force_newline();
1674 Pretty_print_cocci.unparse(rebuild r.scr_ast_rule r.scr_rule_info.dependencies));
1675 end;
1676
1677 match r.language with
1678 "python" ->
1679 (* include_match makes no sense in an initial or final rule, although
1680 we have no way to prevent it *)
1681 let _ = apply_script_rule r [] [] [] [] (ref []) python_application in
1682 ()
1683 | "ocaml" when ty = Initial -> () (* nothing to do *)
1684 | "ocaml" ->
1685 (* include_match makes no sense in an initial or final rule, although
1686 we have no way to prevent it *)
1687 let _ = apply_script_rule r [] [] [] [] (ref []) ocaml_application in
1688 ()
1689 | _ ->
1690 failwith ("Unknown language for initial/final script: "^
1691 r.language)
1692
1693 let initial_final_bigloop a b c =
1694 Common.profile_code "initial_final_bigloop"
1695 (fun () -> initial_final_bigloop2 a b c)
1696
1697 (*****************************************************************************)
1698 (* The main functions *)
1699 (*****************************************************************************)
1700
1701 let pre_engine2 (coccifile, isofile) =
1702 show_or_not_cocci coccifile isofile;
1703 Pycocci.set_coccifile coccifile;
1704
1705 let isofile =
1706 if not (Common.lfile_exists isofile)
1707 then begin
1708 pr2 ("warning: Can't find default iso file: " ^ isofile);
1709 None
1710 end
1711 else Some isofile in
1712
1713 (* useful opti when use -dir *)
1714 let (metavars,astcocci,
1715 free_var_lists,negated_pos_lists,used_after_lists,
1716 positions_lists,toks,_) =
1717 sp_of_file coccifile isofile in
1718 let ctls = ctls_of_ast astcocci used_after_lists positions_lists in
1719
1720 g_contain_typedmetavar := sp_contain_typed_metavar astcocci;
1721
1722 check_macro_in_sp_and_adjust toks;
1723
1724 show_or_not_ctl_tex astcocci ctls;
1725
1726 let cocci_infos =
1727 prepare_cocci ctls free_var_lists negated_pos_lists
1728 used_after_lists positions_lists metavars astcocci in
1729
1730 let used_languages =
1731 List.fold_left
1732 (function languages ->
1733 function
1734 ScriptRuleCocciInfo(r) ->
1735 if List.mem r.language languages then
1736 languages
1737 else
1738 r.language::languages
1739 | _ -> languages)
1740 [] cocci_infos in
1741
1742 let initialized_languages =
1743 List.fold_left
1744 (function languages ->
1745 function
1746 InitialScriptRuleCocciInfo(r) ->
1747 (if List.mem r.language languages
1748 then
1749 failwith
1750 ("double initializer found for "^r.language));
1751 if interpret_dependencies [] [] r.scr_rule_info.dependencies
1752 then
1753 begin
1754 initial_final_bigloop Initial
1755 (fun (x,_,_,y) -> fun deps ->
1756 Ast_cocci.InitialScriptRule(r.scr_rule_info.rulename,x,deps,y))
1757 r;
1758 r.language::languages
1759 end
1760 else languages
1761 | _ -> languages)
1762 [] cocci_infos in
1763
1764 let uninitialized_languages =
1765 List.filter
1766 (fun used -> not (List.mem used initialized_languages))
1767 used_languages in
1768 List.iter
1769 (fun lgg ->
1770 let rule_info =
1771 {rulename = "";
1772 dependencies = Ast_cocci.NoDep;
1773 used_after = [];
1774 ruleid = (-1);
1775 was_matched = ref false;} in
1776 initial_final_bigloop Initial
1777 (fun (x,_,_,y) -> fun deps ->
1778 Ast_cocci.InitialScriptRule("",x,deps,y))
1779 (make_init lgg "" rule_info))
1780 uninitialized_languages;
1781
1782 (cocci_infos,toks)
1783
1784 let pre_engine a =
1785 Common.profile_code "pre_engine" (fun () -> pre_engine2 a)
1786
1787 let full_engine2 (cocci_infos,toks) cfiles =
1788
1789 show_or_not_cfiles cfiles;
1790
1791 (* optimisation allowing to launch coccinelle on all the drivers *)
1792 if !Flag_cocci.worth_trying_opt && not (worth_trying cfiles toks)
1793 then
1794 begin
1795 (match toks with
1796 None -> ()
1797 | Some toks ->
1798 pr2 ("No matches found for " ^ (Common.join " " toks)
1799 ^ "\nSkipping:" ^ (Common.join " " cfiles)));
1800 cfiles +> List.map (fun s -> s, None)
1801 end
1802 else
1803 begin
1804
1805 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1806 if !Flag.show_misc then pr "let's go";
1807 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx();
1808
1809 let choose_includes =
1810 match !Flag_cocci.include_options with
1811 Flag_cocci.I_UNSPECIFIED ->
1812 if !g_contain_typedmetavar
1813 then Flag_cocci.I_NORMAL_INCLUDES
1814 else Flag_cocci.I_NO_INCLUDES
1815 | x -> x in
1816 let c_infos = prepare_c cfiles choose_includes in
1817
1818 (* ! the big loop ! *)
1819 let c_infos' = bigloop cocci_infos c_infos in
1820
1821 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1822 if !Flag.show_misc then pr "Finished";
1823 if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx ();
1824 if !Flag_ctl.graphical_trace then gen_pdf_graph ();
1825
1826 c_infos' +> List.map (fun c_or_h ->
1827 if !(c_or_h.was_modified_once)
1828 then
1829 begin
1830 let outfile =
1831 Common.new_temp_file "cocci-output" ("-" ^ c_or_h.fname) in
1832
1833 if c_or_h.fkind =*= Header
1834 then pr2 ("a header file was modified: " ^ c_or_h.fname);
1835
1836 (* and now unparse everything *)
1837 cfile_of_program (for_unparser c_or_h.asts) outfile;
1838
1839 show_or_not_diff c_or_h.fpath outfile;
1840
1841 (c_or_h.fpath,
1842 if !Flag.sgrep_mode2 then None else Some outfile)
1843 end
1844 else (c_or_h.fpath, None))
1845 end
1846
1847 let full_engine a b =
1848 Common.profile_code "full_engine"
1849 (fun () -> let res = full_engine2 a b in (*Gc.print_stat stderr; *)res)
1850
1851 let post_engine2 (cocci_infos,_) =
1852 let _ =
1853 List.fold_left
1854 (function languages ->
1855 function
1856 FinalScriptRuleCocciInfo(r) ->
1857 (if List.mem r.language languages
1858 then failwith ("double finalizer found for "^r.language));
1859 initial_final_bigloop Final
1860 (fun (x,_,_,y) -> fun deps ->
1861 Ast_cocci.FinalScriptRule(r.scr_rule_info.rulename,x,deps,y))
1862 r;
1863 r.language::languages
1864 | _ -> languages)
1865 [] cocci_infos in
1866 ()
1867
1868 let post_engine a =
1869 Common.profile_code "post_engine" (fun () -> post_engine2 a)
1870
1871 (*****************************************************************************)
1872 (* check duplicate from result of full_engine *)
1873 (*****************************************************************************)
1874
1875 let check_duplicate_modif2 xs =
1876 (* opti: let groups = Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) xs *)
1877 if !Flag_cocci.verbose_cocci
1878 then pr2 ("Check duplication for " ^ i_to_s (List.length xs) ^ " files");
1879
1880 let groups = Common.group_assoc_bykey_eff xs in
1881 groups +> Common.map_filter (fun (file, xs) ->
1882 match xs with
1883 | [] -> raise Impossible
1884 | [res] -> Some (file, res)
1885 | res::xs ->
1886 match res with
1887 | None ->
1888 if not (List.for_all (fun res2 -> res2 =*= None) xs)
1889 then begin
1890 pr2 ("different modification result for " ^ file);
1891 None
1892 end
1893 else Some (file, None)
1894 | Some res ->
1895 if not(List.for_all (fun res2 ->
1896 match res2 with
1897 | None -> false
1898 | Some res2 ->
1899 let diff = Common.cmd_to_list ("diff -u -b -B "^res^" "^res2)
1900 in
1901 null diff
1902 ) xs) then begin
1903 pr2 ("different modification result for " ^ file);
1904 None
1905 end
1906 else Some (file, Some res)
1907 )
1908 let check_duplicate_modif a =
1909 Common.profile_code "check_duplicate" (fun () -> check_duplicate_modif2 a)
1910