X-Git-Url: https://git.hcoop.net/bpt/coccinelle.git/blobdiff_plain/0708f913629519b5dbc99f68b6f3ea5ab068230c..5636bb2c2537506718da74f85a2b81a5ff3df16f:/cocci.ml diff --git a/cocci.ml b/cocci.ml index 32bb774..0ddd588 100644 --- a/cocci.ml +++ b/cocci.ml @@ -1,23 +1,45 @@ (* -* Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen -* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller -* This file is part of Coccinelle. -* -* Coccinelle is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, according to version 2 of the License. -* -* Coccinelle is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Coccinelle. If not, see . -* -* The authors reserve the right to distribute this or future versions of -* Coccinelle under other licenses. -*) + * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen + * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix + * This file is part of Coccinelle. + * + * Coccinelle is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, according to version 2 of the License. + * + * Coccinelle is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Coccinelle. If not, see . + * + * The authors reserve the right to distribute this or future versions of + * Coccinelle under other licenses. + *) + + +(* + * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen + * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix + * This file is part of Coccinelle. + * + * Coccinelle is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, according to version 2 of the License. + * + * Coccinelle is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Coccinelle. If not, see . + * + * The authors reserve the right to distribute this or future versions of + * Coccinelle under other licenses. + *) open Common @@ -25,8 +47,10 @@ open Common module CCI = Ctlcocci_integration module TAC = Type_annoter_c +module Ast_to_flow = Control_flow_c_build + (*****************************************************************************) -(* This file is a kind of driver. It gathers all the important functions +(* This file is a kind of driver. It gathers all the important functions * from coccinelle in one place. The different entities in coccinelle are: * - files * - astc @@ -40,20 +64,20 @@ module TAC = Type_annoter_c (* --------------------------------------------------------------------- *) (* C related *) (* --------------------------------------------------------------------- *) -let cprogram_of_file file = - let (program2, _stat) = Parse_c.parse_print_error_heuristic file in - program2 +let cprogram_of_file file = + let (program2, _stat) = Parse_c.parse_c_and_cpp file in + program2 -let cprogram_of_file_cached file = +let cprogram_of_file_cached file = let (program2, _stat) = Parse_c.parse_cache file in if !Flag_cocci.ifdef_to_if - then - program2 +> Parse_c.with_program2 (fun asts -> + then + program2 +> Parse_c.with_program2 (fun asts -> Cpp_ast_c.cpp_ifdef_statementize asts ) else program2 -let cfile_of_program program2_with_ppmethod outf = +let cfile_of_program program2_with_ppmethod outf = Unparse_c.pp_program program2_with_ppmethod outf (* for memoization, contains only one entry, the one for the SP *) @@ -66,50 +90,51 @@ let _hctl = Hashtbl.create 101 let sp_of_file2 file iso = Common.memoized _hparse (file, iso) (fun () -> Parse_cocci.process file iso false) -let sp_of_file file iso = +let sp_of_file file iso = Common.profile_code "parse cocci" (fun () -> sp_of_file2 file iso) (* --------------------------------------------------------------------- *) (* Flow related *) (* --------------------------------------------------------------------- *) -let print_flow flow = +let print_flow flow = Ograph_extended.print_ograph_mutable flow "/tmp/test.dot" true let ast_to_flow_with_error_messages2 x = - let flowopt = + let flowopt = try Ast_to_flow.ast_to_control_flow x - with Ast_to_flow.Error x -> + with Ast_to_flow.Error x -> Ast_to_flow.report_error x; None in - flowopt +> do_option (fun flow -> + flowopt +> do_option (fun flow -> (* This time even if there is a deadcode, we still have a * flow graph, so I can try the transformation and hope the - * deadcode will not bother us. + * deadcode will not bother us. *) try Ast_to_flow.deadcode_detection flow - with Ast_to_flow.Error (Ast_to_flow.DeadCode x) -> + with Ast_to_flow.Error (Ast_to_flow.DeadCode x) -> Ast_to_flow.report_error (Ast_to_flow.DeadCode x); ); flowopt -let ast_to_flow_with_error_messages a = +let ast_to_flow_with_error_messages a = Common.profile_code "flow" (fun () -> ast_to_flow_with_error_messages2 a) (* --------------------------------------------------------------------- *) (* Ctl related *) (* --------------------------------------------------------------------- *) -let ctls_of_ast2 ast ua pos = + +let ctls_of_ast2 ast (ua,fua,fuas) pos = List.map2 - (function ast -> function (ua,pos) -> + (function ast -> function (ua,(fua,(fuas,pos))) -> List.combine (if !Flag_cocci.popl then Popl.popl ast - else Asttoctl2.asttoctl ast ua pos) + else Asttoctl2.asttoctl ast (ua,fua,fuas) pos) (Asttomember.asttomember ast ua)) - ast (List.combine ua pos) + ast (List.combine ua (List.combine fua (List.combine fuas pos))) let ctls_of_ast ast ua = Common.profile_code "asttoctl2" (fun () -> ctls_of_ast2 ast ua) @@ -127,13 +152,13 @@ let show_or_not_cfile2 cfile = Common.pr2_xxxxxxxxxxxxxxxxx (); Common.command2 ("cat " ^ cfile); end -let show_or_not_cfile a = +let show_or_not_cfile a = Common.profile_code "show_xxx" (fun () -> show_or_not_cfile2 a) let show_or_not_cfiles cfiles = List.iter show_or_not_cfile cfiles -let show_or_not_cocci2 coccifile isofile = +let show_or_not_cocci2 coccifile isofile = if !Flag_cocci.show_cocci then begin Common.pr2_xxxxxxxxxxxxxxxxx (); pr2 ("processing semantic patch file: " ^ coccifile); @@ -142,15 +167,77 @@ let show_or_not_cocci2 coccifile isofile = Common.command2 ("cat " ^ coccifile); pr2 ""; end -let show_or_not_cocci a b = +let show_or_not_cocci a b = Common.profile_code "show_xxx" (fun () -> show_or_not_cocci2 a b) - +(* ---------------------------------------------------------------------- *) (* the output *) -let show_or_not_diff2 cfile outfile show_only_minus = +let fix_sgrep_diffs l = + let l = + List.filter (function s -> (s =~ "^\\+\\+\\+") || not (s =~ "^\\+")) l in + let l = List.rev l in + (* adjust second number for + code *) + let rec loop1 n = function + [] -> [] + | s::ss -> + if s =~ "^-" && not(s =~ "^---") + then s :: loop1 (n+1) ss + else if s =~ "^@@" + then + (match Str.split (Str.regexp " ") s with + bef::min::pl::aft -> + (match Str.split (Str.regexp ",") pl with + [n1;n2] -> + let n2 = int_of_string n2 in + (Printf.sprintf "%s %s %s,%d %s" bef min n1 (n2-n) + (String.concat " " aft)) + :: loop1 0 ss + | _ -> failwith "bad + line information") + | _ -> failwith "bad @@ information") + else s :: loop1 n ss in + let rec loop2 n = function + [] -> [] + | s::ss -> + if s =~ "^---" + then s :: loop2 0 ss + else if s =~ "^@@" + then + (match Str.split (Str.regexp " ") s with + bef::min::pl::aft -> + (match (Str.split (Str.regexp ",") min, + Str.split (Str.regexp ",") pl) with + ([_;m2],[n1;n2]) -> + let n1 = + int_of_string + (String.sub n1 1 ((String.length n1)-1)) in + let m2 = int_of_string m2 in + let n2 = int_of_string n2 in + (Printf.sprintf "%s %s +%d,%d %s" bef min (n1-n) n2 + (String.concat " " aft)) + :: loop2 (n+(m2-n2)) ss + | _ -> failwith "bad -/+ line information") + | _ -> failwith "bad @@ information") + else s :: loop2 n ss in + loop2 0 (List.rev (loop1 0 l)) + +let normalize_path file = + let fullpath = + if String.get file 0 = '/' then file else (Sys.getcwd()) ^ "/" ^ file in + let elements = Str.split_delim (Str.regexp "/") fullpath in + let rec loop prev = function + [] -> String.concat "/" (List.rev prev) + | "." :: rest -> loop prev rest + | ".." :: rest -> + (match prev with + x::xs -> loop xs rest + | _ -> failwith "bad path") + | x::rest -> loop (x::prev) rest in + loop [] elements + +let show_or_not_diff2 cfile outfile = if !Flag_cocci.show_diff then begin - match Common.fst(Compare_c.compare_default cfile outfile) with + match Common.fst(Compare_c.compare_to_original cfile outfile) with Compare_c.Correct -> () (* diff only in spacing, etc *) | _ -> (* may need --strip-trailing-cr under windows *) @@ -165,13 +252,27 @@ let show_or_not_diff2 cfile outfile show_only_minus = match (!Flag.patch,res) with (* create something that looks like the output of patch *) (Some prefix,minus_file::plus_file::rest) -> + let prefix = + let lp = String.length prefix in + if String.get prefix (lp-1) = '/' + then String.sub prefix 0 (lp-1) + else prefix in let drop_prefix file = - if prefix = "" - then "/"^file + let file = normalize_path file in + if Str.string_match (Str.regexp prefix) file 0 + then + let lp = String.length prefix in + let lf = String.length file in + if lp < lf + then String.sub file lp (lf - lp) + else + failwith + (Printf.sprintf "prefix %s doesn't match file %s" + prefix file) else - (match Str.split (Str.regexp prefix) file with - [base_file] -> base_file - | _ -> failwith "prefix not found in the old file name") in + failwith + (Printf.sprintf "prefix %s doesn't match file %s" + prefix file) in let diff_line = match List.rev(Str.split (Str.regexp " ") line) with new_file::old_file::cmdrev -> @@ -187,7 +288,7 @@ let show_or_not_diff2 cfile outfile show_only_minus = | _ -> failwith "bad command" in let (minus_line,plus_line) = if !Flag.sgrep_mode2 - then (minus_file,plus_file) + then (minus_file,"+++ /tmp/nothing") else match (Str.split (Str.regexp "[ \t]") minus_file, Str.split (Str.regexp "[ \t]") plus_file) with @@ -202,18 +303,16 @@ let show_or_not_diff2 cfile outfile show_only_minus = (Printf.sprintf "bad diff header lines: %s %s" (String.concat ":" l1) (String.concat ":" l2)) in diff_line::minus_line::plus_line::rest - | _ -> res in - xs +> List.iter (fun s -> - if s =~ "^\\+" && show_only_minus - then () - else pr s) + | _ -> res in + let xs = if !Flag.sgrep_mode2 then fix_sgrep_diffs xs else xs in + xs +> List.iter pr end -let show_or_not_diff a b c = - Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b c) - - +let show_or_not_diff a b = + Common.profile_code "show_xxx" (fun () -> show_or_not_diff2 a b) + + (* the derived input *) - + let show_or_not_ctl_tex2 astcocci ctls = if !Flag_cocci.show_ctl_tex then begin Ctltotex.totex ("/tmp/__cocci_ctl.tex") astcocci ctls; @@ -221,11 +320,10 @@ let show_or_not_ctl_tex2 astcocci ctls = "dvips __cocci_ctl.dvi -o __cocci_ctl.ps;" ^ "gv __cocci_ctl.ps &"); end -let show_or_not_ctl_tex a b = +let show_or_not_ctl_tex a b = Common.profile_code "show_xxx" (fun () -> show_or_not_ctl_tex2 a b) - - - + + let show_or_not_rule_name ast rulenb = if !Flag_cocci.show_ctl_text or !Flag.show_trying or !Flag.show_transinfo or !Flag_cocci.show_binding_in_out @@ -253,19 +351,19 @@ let show_or_not_scr_rule_name rulenb = let show_or_not_ctl_text2 ctl ast rulenb = if !Flag_cocci.show_ctl_text then begin - - adjust_pp_with_indent (fun () -> + + adjust_pp_with_indent (fun () -> Format.force_newline(); Pretty_print_cocci.print_plus_flag := true; Pretty_print_cocci.print_minus_flag := true; Pretty_print_cocci.unparse ast; ); - + pr "CTL = "; let (ctl,_) = ctl in - adjust_pp_with_indent (fun () -> + adjust_pp_with_indent (fun () -> Format.force_newline(); - Pretty_print_engine.pp_ctlcocci + Pretty_print_engine.pp_ctlcocci !Flag_cocci.show_mcodekind_in_ctl !Flag_cocci.inline_let_ctl ctl; ); pr ""; @@ -276,21 +374,25 @@ let show_or_not_ctl_text a b c = (* running information *) -let get_celem celem : string = - match celem with - Ast_c.Definition ({Ast_c.f_name = funcs;},_) -> funcs +let get_celem celem : string = + match celem with + Ast_c.Definition ({Ast_c.f_name = namefuncs;},_) -> + Ast_c.str_of_name namefuncs | Ast_c.Declaration - (Ast_c.DeclList ([{Ast_c.v_namei = Some ((s, _),_);}, _], _)) -> s + (Ast_c.DeclList ([{Ast_c.v_namei = Some (name, _);}, _], _)) -> + Ast_c.str_of_name name | _ -> "" -let show_or_not_celem2 prelude celem = +let show_or_not_celem2 prelude celem = let (tag,trying) = - (match celem with - | Ast_c.Definition ({Ast_c.f_name = funcs;},_) -> + (match celem with + | Ast_c.Definition ({Ast_c.f_name = namefuncs},_) -> + let funcs = Ast_c.str_of_name namefuncs in Flag.current_element := funcs; (" function: ",funcs) | Ast_c.Declaration - (Ast_c.DeclList ([{Ast_c.v_namei = Some ((s, _),_);}, _], _)) -> + (Ast_c.DeclList ([{Ast_c.v_namei = Some (name,_)}, _], _)) -> + let s = Ast_c.str_of_name name in Flag.current_element := s; (" variable ",s); | _ -> @@ -298,30 +400,33 @@ let show_or_not_celem2 prelude celem = (" ","something else"); ) in if !Flag.show_trying then pr2 (prelude ^ tag ^ trying) - -let show_or_not_celem a b = + +let show_or_not_celem a b = Common.profile_code "show_xxx" (fun () -> show_or_not_celem2 a b) -let show_or_not_trans_info2 trans_info = +let show_or_not_trans_info2 trans_info = + (* drop witness tree indices for printing *) + let trans_info = + List.map (function (index,trans_info) -> trans_info) trans_info in if !Flag.show_transinfo then begin if null trans_info then pr2 "transformation info is empty" else begin pr2 "transformation info returned:"; let trans_info = List.sort (function (i1,_,_) -> function (i2,_,_) -> compare i1 i2) - trans_info + trans_info in - indent_do (fun () -> - trans_info +> List.iter (fun (i, subst, re) -> + indent_do (fun () -> + trans_info +> List.iter (fun (i, subst, re) -> pr2 ("transform state: " ^ (Common.i_to_s i)); - indent_do (fun () -> - adjust_pp_with_indent_and_header "with rule_elem: " (fun () -> + indent_do (fun () -> + adjust_pp_with_indent_and_header "with rule_elem: " (fun () -> Pretty_print_cocci.print_plus_flag := true; Pretty_print_cocci.print_minus_flag := true; Pretty_print_cocci.rule_elem "" re; ); - adjust_pp_with_indent_and_header "with binding: " (fun () -> + adjust_pp_with_indent_and_header "with binding: " (fun () -> Pretty_print_engine.pp_binding subst; ); ) @@ -329,18 +434,18 @@ let show_or_not_trans_info2 trans_info = ) end end -let show_or_not_trans_info a = +let show_or_not_trans_info a = Common.profile_code "show_xxx" (fun () -> show_or_not_trans_info2 a) let show_or_not_binding2 s binding = if !Flag_cocci.show_binding_in_out then begin - adjust_pp_with_indent_and_header ("binding " ^ s ^ " = ") (fun () -> + adjust_pp_with_indent_and_header ("binding " ^ s ^ " = ") (fun () -> Pretty_print_engine.pp_binding binding ) end -let show_or_not_binding a b = +let show_or_not_binding a b = Common.profile_code "show_xxx" (fun () -> show_or_not_binding2 a b) @@ -349,7 +454,7 @@ let show_or_not_binding a b = (* Some helper functions *) (*****************************************************************************) -let worth_trying cfiles tokens = +let worth_trying cfiles tokens = (* drop the following line for a list of list by rules. since we don't allow multiple minirules, all the tokens within a rule should be in a single CFG entity *) @@ -357,15 +462,15 @@ let worth_trying cfiles tokens = if not !Flag_cocci.windows && not (null tokens) then (* could also modify the code in get_constants.ml *) - let tokens = tokens +> List.map (fun s -> - match () with - | _ when s =~ "^[A-Za-z_][A-Za-z_0-9]*$" -> + let tokens = tokens +> List.map (fun s -> + match () with + | _ when s =~ "^[A-Za-z_][A-Za-z_0-9]*$" -> "\\b" ^ s ^ "\\b" - | _ when s =~ "^[A-Za-z_]" -> + | _ when s =~ "^[A-Za-z_]" -> "\\b" ^ s - | _ when s =~ ".*[A-Za-z_]$" -> + | _ when s =~ ".*[A-Za-z_]$" -> s ^ "\\b" | _ -> s @@ -381,29 +486,31 @@ let worth_trying cfiles tokens = ) else true -let check_macro_in_sp_and_adjust tokens = +let check_macro_in_sp_and_adjust tokens = let tokens = Common.union_all tokens in - tokens +> List.iter (fun s -> - if Hashtbl.mem !Parsing_hacks._defs s + tokens +> List.iter (fun s -> + if Hashtbl.mem !Parse_c._defs s then begin - pr2 "warning: macro in semantic patch was in macro definitions"; - pr2 ("disabling macro expansion for " ^ s); - Hashtbl.remove !Parsing_hacks._defs s + if !Flag_cocci.verbose_cocci then begin + pr2 "warning: macro in semantic patch was in macro definitions"; + pr2 ("disabling macro expansion for " ^ s); + end; + Hashtbl.remove !Parse_c._defs s end ) -let contain_loop gopt = +let contain_loop gopt = match gopt with - | Some g -> - g#nodes#tolist +> List.exists (fun (xi, node) -> + | Some g -> + g#nodes#tolist +> List.exists (fun (xi, node) -> Control_flow_c.extract_is_loop node ) | None -> true (* means nothing, if no g then will not model check *) -let sp_contain_typed_metavar_z toplevel_list_list = +let sp_contain_typed_metavar_z toplevel_list_list = let bind x y = x or y in let option_default = false in let mcode _ _ = option_default in @@ -413,24 +520,23 @@ let sp_contain_typed_metavar_z toplevel_list_list = match Ast_cocci.unwrap e with | Ast_cocci.MetaExpr (_,_,_,Some t,_,_) -> true | Ast_cocci.MetaExpr (_,_,_,_,Ast_cocci.LocalID,_) -> true - | _ -> k e + | _ -> k e in - let combiner = + let combiner = Visitor_ast.combiner bind option_default mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode donothing donothing donothing donothing donothing expression donothing donothing donothing donothing donothing - donothing donothing donothing donothing donothing + donothing donothing donothing donothing donothing in - toplevel_list_list +> + toplevel_list_list +> List.exists (function (nm,_,rule) -> (List.exists combiner.Visitor_ast.combiner_top_level rule)) - let sp_contain_typed_metavar rules = - sp_contain_typed_metavar_z + sp_contain_typed_metavar_z (List.map (function x -> match x with @@ -448,10 +554,27 @@ let sp_contain_typed_metavar rules = (* finding among the #include the one that we need to parse * because they may contain useful type definition or because * we may have to modify them - * + * * For the moment we base in part our heuristic on the name of the file, e.g. - * serio.c is related we think to #include + * serio.c is related we think to #include *) +let rec search_include_path searchlist relpath = + match searchlist with + [] -> Some relpath + | hd::tail -> + let file = Filename.concat hd relpath in + if Sys.file_exists file then + Some file + else + search_include_path tail relpath + +let interpret_include_path relpath = + let searchlist = + match !Flag_cocci.include_path with + [] -> ["include"] + | x -> List.rev x + in + search_include_path searchlist relpath let (includes_to_parse: (Common.filename * Parse_c.program2) list -> @@ -460,41 +583,42 @@ let (includes_to_parse: Flag_cocci.I_UNSPECIFIED -> failwith "not possible" | Flag_cocci.I_NO_INCLUDES -> [] | x -> - let all_includes = x = Flag_cocci.I_ALL_INCLUDES in - xs +> List.map (fun (file, cs) -> + let all_includes = x =*= Flag_cocci.I_ALL_INCLUDES in + xs +> List.map (fun (file, cs) -> let dir = Common.dirname file in - - cs +> Common.map_filter (fun (c,_info_item) -> + + cs +> Common.map_filter (fun (c,_info_item) -> match c with | Ast_c.CppTop (Ast_c.Include - {Ast_c.i_include = ((x,ii)); i_rel_pos = info_h_pos;}) -> + {Ast_c.i_include = ((x,ii)); i_rel_pos = info_h_pos;}) -> (match x with - | Ast_c.Local xs -> - let f = Filename.concat dir (Common.join "/" xs) in + | Ast_c.Local xs -> + let relpath = Common.join "/" xs in + let f = Filename.concat dir (relpath) in (* for our tests, all the files are flat in the current dir *) if not (Sys.file_exists f) && !Flag_cocci.relax_include_path then let attempt2 = Filename.concat dir (Common.last xs) in if not (Sys.file_exists f) && all_includes - then Some (Filename.concat !Flag_cocci.include_path - (Common.join "/" xs)) + then + interpret_include_path relpath else Some attempt2 else Some f - | Ast_c.NonLocal xs -> + | Ast_c.NonLocal xs -> + let relpath = Common.join "/" xs in if all_includes || - Common.fileprefix (Common.last xs) = Common.fileprefix file - then - Some (Filename.concat !Flag_cocci.include_path - (Common.join "/" xs)) + Common.fileprefix (Common.last xs) =$= Common.fileprefix file + then + interpret_include_path relpath else None | Ast_c.Weird _ -> None ) | _ -> None)) +> List.concat +> Common.uniq - + let rec interpret_dependencies local global = function Ast_cocci.Dep s -> List.mem s local | Ast_cocci.AntiDep s -> @@ -513,7 +637,8 @@ let rec interpret_dependencies local global = function (interpret_dependencies local global s1) or (interpret_dependencies local global s2) | Ast_cocci.NoDep -> true - + | Ast_cocci.FailDep -> false + let rec print_dependencies str local global dep = if !Flag_cocci.show_dependencies then @@ -522,62 +647,61 @@ let rec print_dependencies str local global dep = let seen = ref [] in let rec loop = function Ast_cocci.Dep s | Ast_cocci.AntiDep s -> - if not (List.mem s !seen) - then - begin - if List.mem s local - then pr2 (s^" satisfied") - else pr2 (s^" not satisfied"); - seen := s :: !seen - end + if not (List.mem s !seen) + then + begin + if List.mem s local + then pr2 (s^" satisfied") + else pr2 (s^" not satisfied"); + seen := s :: !seen + end | Ast_cocci.EverDep s | Ast_cocci.NeverDep s -> - if not (List.mem s !seen) - then - begin - if List.mem s global - then pr2 (s^" satisfied") - else pr2 (s^" not satisfied"); - seen := s :: !seen - end + if not (List.mem s !seen) + then + begin + if List.mem s global + then pr2 (s^" satisfied") + else pr2 (s^" not satisfied"); + seen := s :: !seen + end | Ast_cocci.AndDep(s1,s2) -> loop s1; loop s2 | Ast_cocci.OrDep(s1,s2) -> loop s1; loop s2 - | Ast_cocci.NoDep -> () in + | Ast_cocci.NoDep -> () + | Ast_cocci.FailDep -> pr2 "False not satisfied" in loop dep end - - - + (* --------------------------------------------------------------------- *) (* #include relative position in the file *) (* --------------------------------------------------------------------- *) - + (* compute the set of new prefixes - * on + * on * "a/b/x"; (* in fact it is now a list of string so ["a";"b";"x"] *) * "a/b/c/x"; * "a/x"; * "b/x"; - * it would give for the first element + * it would give for the first element * ""; "a"; "a/b"; "a/b/x" * for the second * "a/b/c/x" - * + * * update: if the include is inside a ifdef a put nothing. cf -test incl. * this is because we dont want code added inside ifdef. *) -let compute_new_prefixes xs = - xs +> Common.map_withenv (fun already xs -> +let compute_new_prefixes xs = + xs +> Common.map_withenv (fun already xs -> let subdirs_prefixes = Common.inits xs in - let new_first = subdirs_prefixes +> List.filter (fun x -> + let new_first = subdirs_prefixes +> List.filter (fun x -> not (List.mem x already) ) in - new_first, + new_first, new_first @ already ) [] +> fst @@ -585,23 +709,23 @@ let compute_new_prefixes xs = (* does via side effect on the ref in the Include in Ast_c *) let rec update_include_rel_pos cs = - let only_include = cs +> Common.map_filter (fun c -> - match c with + let only_include = cs +> Common.map_filter (fun c -> + match c with | Ast_c.CppTop (Ast_c.Include {Ast_c.i_include = ((x,_)); i_rel_pos = aref; i_is_in_ifdef = inifdef}) -> (match x with | Ast_c.Weird _ -> None - | _ -> - if inifdef + | _ -> + if inifdef then None else Some (x, aref) ) | _ -> None ) in - let (locals, nonlocals) = - only_include +> Common.partition_either (fun (c, aref) -> + let (locals, nonlocals) = + only_include +> Common.partition_either (fun (c, aref) -> match c with | Ast_c.Local x -> Left (x, aref) | Ast_c.NonLocal x -> Right (x, aref) @@ -611,36 +735,32 @@ let rec update_include_rel_pos cs = update_rel_pos_bis locals; update_rel_pos_bis nonlocals; cs -and update_rel_pos_bis xs = +and update_rel_pos_bis xs = let xs' = List.map fst xs in let the_first = compute_new_prefixes xs' in let the_last = List.rev (compute_new_prefixes (List.rev xs')) in let merged = Common.zip xs (Common.zip the_first the_last) in - merged +> List.iter (fun ((x, aref), (the_first, the_last)) -> - aref := Some - { + merged +> List.iter (fun ((x, aref), (the_first, the_last)) -> + aref := Some + { Ast_c.first_of = the_first; Ast_c.last_of = the_last; } ) - - - - (*****************************************************************************) (* All the information needed around the C elements and Cocci rules *) (*****************************************************************************) -type toplevel_c_info = { +type toplevel_c_info = { ast_c: Ast_c.toplevel; (* contain refs so can be modified *) tokens_c: Parser_c.token list; fullstring: string; flow: Control_flow_c.cflow option; (* it's the "fixed" flow *) contain_loop: bool; - + env_typing_before: TAC.environment; env_typing_after: TAC.environment; @@ -650,7 +770,7 @@ type toplevel_c_info = { } type toplevel_cocci_info_script_rule = { - scr_ast_rule: string * (string * (string * string)) list * string; + scr_ast_rule: string * (string * Ast_cocci.meta_name) list * string; language: string; scr_dependencies: Ast_cocci.dependency; scr_ruleid: int; @@ -666,7 +786,7 @@ type toplevel_cocci_info_cocci_rule = { rulename: string; dependencies: Ast_cocci.dependency; (* There are also some hardcoded rule names in parse_cocci.ml: - * let reserved_names = ["all";"optional_storage";"optional_qualifier"] + * let reserved_names = ["all";"optional_storage";"optional_qualifier"] *) dropped_isos: string list; free_vars: Ast_cocci.meta_name list; @@ -680,12 +800,16 @@ type toplevel_cocci_info_cocci_rule = { was_matched: bool ref; } -type toplevel_cocci_info = +type toplevel_cocci_info = ScriptRuleCocciInfo of toplevel_cocci_info_script_rule + | InitialScriptRuleCocciInfo of toplevel_cocci_info_script_rule + | FinalScriptRuleCocciInfo of toplevel_cocci_info_script_rule | CocciRuleCocciInfo of toplevel_cocci_info_cocci_rule -type kind_file = Header | Source -type file_info = { +type cocci_info = toplevel_cocci_info list * string list list (* tokens *) + +type kind_file = Header | Source +type file_info = { fname : string; full_fname : string; was_modified_once: bool ref; @@ -694,25 +818,25 @@ type file_info = { fkind : kind_file; } -let g_contain_typedmetavar = ref false +let g_contain_typedmetavar = ref false let last_env_toplevel_c_info xs = (Common.last xs).env_typing_after -let concat_headers_and_c (ccs: file_info list) - : (toplevel_c_info * string) list = - (List.concat (ccs +> List.map (fun x -> +let concat_headers_and_c (ccs: file_info list) + : (toplevel_c_info * string) list = + (List.concat (ccs +> List.map (fun x -> x.asts +> List.map (fun x' -> (x', x.fname))))) -let for_unparser xs = - xs +> List.map (fun x -> +let for_unparser xs = + xs +> List.map (fun x -> (x.ast_c, (x.fullstring, x.tokens_c)), Unparse_c.PPviastr ) let gen_pdf_graph () = - (Ctl_engine.get_graph_files ()) +> List.iter (fun outfile -> + (Ctl_engine.get_graph_files ()) +> List.iter (fun outfile -> Printf.printf "Generation of %s%!" outfile; let filename_stack = Ctl_engine.get_graph_comp_files outfile in List.iter (fun filename -> @@ -730,28 +854,52 @@ let gen_pdf_graph () = ) filename_stack; Printf.printf " - Done\n") +let local_python_code = + "from coccinelle import *\n" + +let python_code = + "import coccinelle\n"^ + "import coccilib\n"^ + "import coccilib.org\n"^ + "import coccilib.report\n" ^ + local_python_code ^ + "cocci = Cocci()\n" + +let make_init rulenb lang deps code = + let mv = [] in + { + scr_ast_rule = (lang, mv, code); + language = lang; + scr_dependencies = deps; + scr_ruleid = rulenb; + script_code = (if lang = "python" then python_code else "") ^code +} (* --------------------------------------------------------------------- *) let prepare_cocci ctls free_var_lists negated_pos_lists - used_after_lists positions_list metavars astcocci = + (ua,fua,fuas) positions_list metavars astcocci = let gathered = Common.index_list_1 - (zip (zip (zip (zip (zip (zip ctls metavars) astcocci) free_var_lists) - negated_pos_lists) used_after_lists) positions_list) + (zip (zip (zip (zip (zip (zip (zip (zip ctls metavars) astcocci) + free_var_lists) + negated_pos_lists) ua) fua) fuas) positions_list) in - gathered +> List.map - (fun (((((((ctl_toplevel_list,metavars),ast),free_var_list), - negated_pos_list),used_after_list),positions_list),rulenb) -> - + gathered +> List.map + (fun (((((((((ctl_toplevel_list,metavars),ast),free_var_list), + negated_pos_list),ua),fua),fuas),positions_list),rulenb) -> + let is_script_rule r = - match r with Ast_cocci.ScriptRule _ -> true | _ -> false in + match r with + Ast_cocci.ScriptRule _ + | Ast_cocci.InitialScriptRule _ | Ast_cocci.FinalScriptRule _ -> true + | _ -> false in - if not (List.length ctl_toplevel_list = 1) && not (is_script_rule ast) + if not (List.length ctl_toplevel_list =|= 1) && not (is_script_rule ast) then failwith "not handling multiple minirules"; match ast with Ast_cocci.ScriptRule (lang,deps,mv,code) -> - let r = + let r = { scr_ast_rule = (lang, mv, code); language = lang; @@ -760,6 +908,20 @@ let prepare_cocci ctls free_var_lists negated_pos_lists script_code = code; } in ScriptRuleCocciInfo r + | Ast_cocci.InitialScriptRule (lang,deps,code) -> + let r = make_init rulenb lang deps code in + InitialScriptRuleCocciInfo r + | Ast_cocci.FinalScriptRule (lang,deps,code) -> + let mv = [] in + let r = + { + scr_ast_rule = (lang, mv, code); + language = lang; + scr_dependencies = deps; + scr_ruleid = rulenb; + script_code = code; + } + in FinalScriptRuleCocciInfo r | Ast_cocci.CocciRule (rulename,(dependencies,dropped_isos,z),restast,isexp,ruletype) -> CocciRuleCocciInfo ( @@ -773,7 +935,7 @@ let prepare_cocci ctls free_var_lists negated_pos_lists dropped_isos = dropped_isos; free_vars = List.hd free_var_list; negated_pos_vars = List.hd negated_pos_list; - used_after = List.hd used_after_list; + used_after = (List.hd ua) @ (List.hd fua); positions = List.hd positions_list; ruleid = rulenb; ruletype = ruletype; @@ -784,26 +946,27 @@ let prepare_cocci ctls free_var_lists negated_pos_lists (* --------------------------------------------------------------------- *) -let build_info_program cprogram env = - - let (cs, parseinfos) = +let build_info_program cprogram env = + + let (cs, parseinfos) = Common.unzip cprogram in - let alltoks = + let alltoks = parseinfos +> List.map (fun (s,toks) -> toks) +> List.flatten in (* I use cs' but really annotate_xxx work by doing side effects on cs *) - let cs' = + let cs' = Comment_annotater_c.annotate_program alltoks cs in - let cs_with_envs = + let cs_with_envs = Type_annoter_c.annotate_program env (*!g_contain_typedmetavar*) cs' in - + zip cs_with_envs parseinfos +> List.map (fun ((c, (enva,envb)), parseinfo)-> let (fullstr, tokens) = parseinfo in - let flow = - ast_to_flow_with_error_messages c +> Common.map_option (fun flow -> + let flow = + ast_to_flow_with_error_messages c +> + Common.map_option (fun flow -> let flow = Ast_to_flow.annotate_loop_nodes flow in (* remove the fake nodes for julia *) @@ -824,7 +987,7 @@ let build_info_program cprogram env = flow = flow; contain_loop = contain_loop flow; - + env_typing_before = enva; env_typing_after = envb; @@ -835,19 +998,19 @@ let build_info_program cprogram env = (* Optimisation. Try not unparse/reparse the whole file when have modifs *) -let rebuild_info_program cs file isexp = +let rebuild_info_program cs file isexp = cs +> List.map (fun c -> if !(c.was_modified) then let file = Common.new_temp_file "cocci_small_output" ".c" in - cfile_of_program - [(c.ast_c, (c.fullstring, c.tokens_c)), Unparse_c.PPnormal] + cfile_of_program + [(c.ast_c, (c.fullstring, c.tokens_c)), Unparse_c.PPnormal] file; - + (* Common.command2 ("cat " ^ file); *) let cprogram = cprogram_of_file file in let xs = build_info_program cprogram c.env_typing_before in - + (* TODO: assert env has not changed, * if yes then must also reparse what follows even if not modified. * Do that only if contain_typedmetavar of course, so good opti. @@ -859,51 +1022,49 @@ let rebuild_info_program cs file isexp = let rebuild_info_c_and_headers ccs isexp = - ccs +> List.iter (fun c_or_h -> + ccs +> List.iter (fun c_or_h -> if c_or_h.asts +> List.exists (fun c -> !(c.was_modified)) then c_or_h.was_modified_once := true; ); - ccs +> List.map (fun c_or_h -> + ccs +> List.map (fun c_or_h -> { c_or_h with - asts = rebuild_info_program c_or_h.asts c_or_h.full_fname isexp } + asts = + rebuild_info_program c_or_h.asts c_or_h.full_fname isexp } ) - - - - -let prepare_c files choose_includes : file_info list = +let prepare_c files choose_includes : file_info list = let cprograms = List.map cprogram_of_file_cached files in let includes = includes_to_parse (zip files cprograms) choose_includes in (* todo?: may not be good to first have all the headers and then all the c *) - let all = + let all = (includes +> List.map (fun hpath -> Right hpath)) ++ - ((zip files cprograms) +> List.map (fun (file, asts) -> Left (file, asts))) + ((zip files cprograms) +> + List.map (fun (file, asts) -> Left (file, asts))) in let env = ref !TAC.initial_env in - let ccs = all +> Common.map_filter (fun x -> - match x with - | Right hpath -> - if not (Common.lfile_exists hpath) - then begin - pr2 ("TYPE: header " ^ hpath ^ " not found"); - None + let ccs = all +> Common.map_filter (fun x -> + match x with + | Right hpath -> + if not (Common.lfile_exists hpath) + then begin + pr2 ("TYPE: header " ^ hpath ^ " not found"); + None end - else + else let h_cs = cprogram_of_file_cached hpath in let info_h_cs = build_info_program h_cs !env in - env := + env := if null info_h_cs then !env else last_env_toplevel_c_info info_h_cs ; - Some { + Some { fname = Common.basename hpath; full_fname = hpath; asts = info_h_cs; @@ -911,12 +1072,12 @@ let prepare_c files choose_includes : file_info list = fpath = hpath; fkind = Header; } - | Left (file, cprogram) -> + | Left (file, cprogram) -> (* todo?: don't update env ? *) let cs = build_info_program cprogram !env in (* we do that only for the c, not for the h *) ignore(update_include_rel_pos (cs +> List.map (fun x -> x.ast_c))); - Some { + Some { fname = Common.basename file; full_fname = file; asts = cs; @@ -924,9 +1085,9 @@ let prepare_c files choose_includes : file_info list = fpath = file; fkind = Source; } - ) + ) in - ccs + ccs (*****************************************************************************) @@ -934,31 +1095,31 @@ let prepare_c files choose_includes : file_info list = (*****************************************************************************) (* The main algorithm =~ - * The algorithm is roughly: + * The algorithm is roughly: * for_all ctl rules in SP * for_all minirule in rule (no more) * for_all binding (computed during previous phase) * for_all C elements - * match control flow of function vs minirule - * with the binding and update the set of possible + * match control flow of function vs minirule + * with the binding and update the set of possible * bindings, and returned the possibly modified function. * pretty print modified C elements and reparse it. * - * + * * On ne prends que les newbinding ou returned_any_state est vrai. * Si ca ne donne rien, on prends ce qu'il y avait au depart. - * Mais au nouveau depart de quoi ? + * Mais au nouveau depart de quoi ? * - si ca donne rien apres avoir traité toutes les fonctions avec ce binding ? - * - ou alors si ca donne rien, apres avoir traité toutes les fonctions + * - ou alors si ca donne rien, apres avoir traité toutes les fonctions * avec tous les bindings du round d'avant ? - * + * * Julia pense qu'il faut prendre la premiere solution. * Example: on a deux environnements candidats, E1 et E2 apres avoir traité * la regle ctl 1. On arrive sur la regle ctl 2. * E1 ne donne rien pour la regle 2, on garde quand meme E1 pour la regle 3. * E2 donne un match a un endroit et rend E2' alors on utilise ca pour * la regle 3. - * + * * I have not to look at used_after_list to decide to restart from * scratch. I just need to look if the binding list is empty. * Indeed, let's suppose that a SP have 3 regions/rules. If we @@ -970,7 +1131,7 @@ let prepare_c files choose_includes : file_info list = * region must bind some metavariables used after, and that we * dont find any such region, then mysat() will returns lots of * Right, and current_binding will not grow, and so we will have - * an empty list of binding, and we will catch such a case. + * an empty list of binding, and we will catch such a case. * * opti: julia says that because the binding is * determined by the used_after_list, the items in the list @@ -980,8 +1141,30 @@ let prepare_c files choose_includes : file_info list = (* r(ule), c(element in C code), e(nvironment) *) -let rec apply_python_rule r cache newes e rules_that_have_matched +let findk f l = + let rec loop k = function + [] -> None + | x::xs -> + if f x + then Some (x, function n -> k (n :: xs)) + else loop (function vs -> k (x :: vs)) xs in + loop (function x -> x) l + +let merge_env new_e old_e = + let (ext,old_e) = + List.fold_left + (function (ext,old_e) -> + function (e,rules) as elem -> + match findk (function (e1,_) -> e =*= e1) old_e with + None -> (elem :: ext,old_e) + | Some((_,old_rules),k) -> + (ext,k (e,Common.union_set rules old_rules))) + ([],old_e) new_e in + old_e @ (List.rev ext) + +let apply_python_rule r cache newes e rules_that_have_matched rules_that_have_ever_matched = + Common.profile_code "python" (fun () -> show_or_not_scr_rule_name r.scr_ruleid; if not(interpret_dependencies rules_that_have_matched !rules_that_have_ever_matched r.scr_dependencies) @@ -996,40 +1179,58 @@ let rec apply_python_rule r cache newes e rules_that_have_matched else begin let (_, mv, _) = r.scr_ast_rule in - if List.for_all (Pycocci.contains_binding e) mv - then - begin + let ve = + (List.map (function (n,v) -> (("virtual",n),Ast_c.MetaIdVal (v,[]))) + !Flag.defined_virtual_env) @ e in + let not_bound x = not (Pycocci.contains_binding ve x) in + (match List.filter not_bound mv with + [] -> let relevant_bindings = List.filter (function ((re,rm),_) -> - List.exists (function (_,(r,m)) -> r = re && m = rm) mv) + List.exists (function (_,(r,m)) -> r =*= re && m =$= rm) mv) e in let new_cache = if List.mem relevant_bindings cache - then cache + then + begin + print_dependencies + "dependencies for script satisfied, but cached:" + rules_that_have_matched + !rules_that_have_ever_matched + r.scr_dependencies; + show_or_not_binding "in" e; + cache + end else begin print_dependencies "dependencies for script satisfied:" rules_that_have_matched - !rules_that_have_ever_matched r.scr_dependencies; + !rules_that_have_ever_matched + r.scr_dependencies; show_or_not_binding "in" e; - Pycocci.build_classes (List.map (function (x,y) -> x) e); - Pycocci.construct_variables mv e; - let _ = Pycocci.pyrun_simplestring - ("import coccinelle\nfrom coccinelle "^ - "import *\ncocci = Cocci()\n" ^ - r.script_code) in + Pycocci.build_classes (List.map (function (x,y) -> x) ve); + Pycocci.construct_variables mv ve; + let _ = + Pycocci.pyrun_simplestring + (local_python_code ^r.script_code) in relevant_bindings :: cache end in if !Pycocci.inc_match then (new_cache, merge_env [(e, rules_that_have_matched)] newes) else (new_cache, newes) - end - else (cache, merge_env [(e, rules_that_have_matched)] newes) - end - -and apply_cocci_rule r rules_that_have_ever_matched es (ccs:file_info list ref) = - Common.profile_code r.rulename (fun () -> + | unbound -> + (if !Flag_cocci.show_dependencies + then + let m2c (_,(r,x)) = r^"."^x in + pr2 (Printf.sprintf "script not applied: %s not bound" + (String.concat ", " (List.map m2c unbound)))); + (cache, merge_env [(e, rules_that_have_matched)] newes)) + end) + +let rec apply_cocci_rule r rules_that_have_ever_matched es + (ccs:file_info list ref) = + Common.profile_code r.rulename (fun () -> show_or_not_rule_name r.ast_rule r.ruleid; show_or_not_ctl_text r.ctl r.ast_rule r.ruleid; @@ -1042,7 +1243,8 @@ and apply_cocci_rule r rules_that_have_ever_matched es (ccs:file_info list ref) (function (cache,newes) -> function ((e,rules_that_have_matched),relevant_bindings) -> if not(interpret_dependencies rules_that_have_matched - !rules_that_have_ever_matched r.dependencies) + !rules_that_have_ever_matched + r.dependencies) then begin print_dependencies @@ -1064,33 +1266,37 @@ and apply_cocci_rule r rules_that_have_ever_matched es (ccs:file_info list ref) print_dependencies ("dependencies for rule "^r.rulename^" satisfied:") rules_that_have_matched - !rules_that_have_ever_matched r.dependencies; + !rules_that_have_ever_matched + r.dependencies; show_or_not_binding "in" e; show_or_not_binding "relevant in" relevant_bindings; (* applying the rule *) (match r.ruletype with Ast_cocci.Normal -> - let children_e = ref [] in - (* looping over the functions and toplevel elements in .c and .h *) - concat_headers_and_c !ccs +> List.iter (fun (c,f) -> - if c.flow <> None - then - (* does also some side effects on c and r *) - let processed = - process_a_ctl_a_env_a_toplevel r - relevant_bindings c f in - match processed with - | None -> () - | Some newbindings -> - newbindings +> List.iter (fun newbinding -> - children_e := - Common.insert_set newbinding !children_e) - ); (* end iter cs *) - - !children_e + List.rev + (concat_headers_and_c !ccs +> + List.fold_left (fun children_e (c,f) -> + if c.flow <> None + then + (* does also some side effects on c and r *) + let processed = + process_a_ctl_a_env_a_toplevel r + relevant_bindings c f in + match processed with + | None -> children_e + | Some newbindings -> + newbindings +> + List.fold_left + (fun children_e newbinding -> + if List.mem newbinding children_e + then children_e + else newbinding :: children_e) + children_e + else children_e) + []) | Ast_cocci.Generated -> process_a_generated_a_env_a_toplevel r relevant_bindings !ccs; @@ -1107,7 +1313,7 @@ and apply_cocci_rule r rules_that_have_ever_matched es (ccs:file_info list ref) if !Flag_ctl.partial_match then printf - "Empty list of bindings, I will restart from old env"; + "Empty list of bindings, I will restart from old env\n"; [(old_bindings_to_keep,rules_that_have_matched)] end else @@ -1124,9 +1330,12 @@ and apply_cocci_rule r rules_that_have_ever_matched es (ccs:file_info list ref) (new_bindings +> List.map (List.filter - (fun (s,v) -> - List.mem s r.used_after && - not (List.mem s old_variables)))) in + (function + (* see comment before combine_pos *) + (s,Ast_c.MetaPosValList []) -> false + | (s,v) -> + List.mem s r.used_after && + not (List.mem s old_variables)))) in List.map (function new_binding_to_add -> (List.sort compare @@ -1144,82 +1353,7 @@ and apply_cocci_rule r rules_that_have_ever_matched es (ccs:file_info list ref) (* apply the tagged modifs and reparse *) if not !Flag.sgrep_mode2 - then ccs := rebuild_info_c_and_headers !ccs r.isexp - ) - -and merge_env new_e old_e = - List.fold_left - (function old_e -> - function (e,rules) as elem -> - let (same,diff) = List.partition (function (e1,_) -> e = e1) old_e in - match same with - [] -> elem :: old_e - | [(_,old_rules)] -> (e,Common.union_set rules old_rules) :: diff - | _ -> failwith "duplicate environment entries") - old_e new_e - -and bigloop2 rs (ccs: file_info list) = - let es = ref [(Ast_c.emptyMetavarsBinding,[])] in - let ccs = ref ccs in - let rules_that_have_ever_matched = ref [] in - - (* looping over the rules *) - rs +> List.iter (fun r -> - match r with - ScriptRuleCocciInfo r -> - if !Flag_cocci.show_ctl_text then begin - Common.pr_xxxxxxxxxxxxxxxxx (); - pr ("script: " ^ r.language); - Common.pr_xxxxxxxxxxxxxxxxx (); - - adjust_pp_with_indent (fun () -> - Format.force_newline(); - let (l,mv,code) = r.scr_ast_rule in - let deps = r.scr_dependencies in - Pretty_print_cocci.unparse - (Ast_cocci.ScriptRule (l,deps,mv,code))); - end; - - if !Flag.show_misc then print_endline "RESULT ="; - - let (_, newes) = - List.fold_left - (function (cache, newes) -> - function (e, rules_that_have_matched) -> - match r.language with - "python" -> - apply_python_rule r cache newes e rules_that_have_matched - rules_that_have_ever_matched - | "test" -> - concat_headers_and_c !ccs +> List.iter (fun (c,_) -> - if c.flow <> None - then - Printf.printf "Flow: %s\r\nFlow!\r\n%!" c.fullstring); - (cache, newes) - | _ -> - Printf.printf "Unknown language: %s\n" r.language; - (cache, newes) - ) - ([],[]) !es in - - es := newes; - | CocciRuleCocciInfo r -> - apply_cocci_rule r rules_that_have_ever_matched es ccs); - - if !Flag.sgrep_mode2 - then begin - (* sgrep can lead to code that is not parsable, but we must - * still call rebuild_info_c_and_headers to pretty print the - * action (MINUS), so that later the diff will show what was - * matched by sgrep. But we don't want the parsing error message - * hence the following flag setting. So this code propably - * will generate a NotParsedCorrectly for the matched parts - * and the very final pretty print and diff will work - *) - Flag_parsing_c.verbose_parsing := false; - ccs := rebuild_info_c_and_headers !ccs false - end; - !ccs (* return final C asts *) + then ccs := rebuild_info_c_and_headers !ccs r.isexp) and reassociate_positions free_vars negated_pos_vars envs = (* issues: isolate the bindings that are relevant to a given rule. @@ -1270,7 +1404,7 @@ and reassociate_positions free_vars negated_pos_vars envs = (function (other_non_pos,other_pos) -> (* do we want equal? or just somehow compatible? eg non_pos binds only E, but other_non_pos binds both E and E1 *) - non_pos = other_non_pos) + non_pos =*= other_non_pos) splitted_relevant in (non_pos, List.sort compare @@ -1282,60 +1416,82 @@ and reassociate_positions free_vars negated_pos_vars envs = (List.map (function (non_pos,_) -> List.assoc non_pos extended_relevant) splitted_relevant) +(* If the negated posvar is not bound at all, this function will +nevertheless bind it to []. If we get rid of these bindings, then the +matching of the term the position variable with the constraints will fail +because some variables are unbound. So we let the binding be [] and then +we will have to clean these up afterwards. This should be the only way +that a position variable can have an empty binding. *) and combine_pos negated_pos_vars others = List.map (function posvar -> - (posvar, - Ast_c.MetaPosValList - (List.sort compare - (List.fold_left - (function positions -> - function other_list -> - try - match List.assoc posvar other_list with - Ast_c.MetaPosValList l1 -> - Common.union_set l1 positions - | _ -> failwith "bad value for a position variable" - with Not_found -> positions) - [] others)))) + let positions = + List.sort compare + (List.fold_left + (function positions -> + function other_list -> + try + match List.assoc posvar other_list with + Ast_c.MetaPosValList l1 -> + Common.union_set l1 positions + | _ -> failwith "bad value for a position variable" + with Not_found -> positions) + [] others) in + (posvar,Ast_c.MetaPosValList positions)) negated_pos_vars -and bigloop a b = - Common.profile_code "bigloop" (fun () -> bigloop2 a b) - - - +and process_a_generated_a_env_a_toplevel2 r env = function + [cfile] -> + let free_vars = + List.filter + (function + (rule,_) when rule =$= r.rulename -> false + | (_,"ARGS") -> false + | _ -> true) + r.free_vars in + let env_domain = List.map (function (nm,vl) -> nm) env in + let metavars = + List.filter + (function md -> + let (rl,_) = Ast_cocci.get_meta_name md in rl =$= r.rulename) + r.metavars in + if Common.include_set free_vars env_domain + then Unparse_hrule.pp_rule metavars r.ast_rule env cfile.full_fname + | _ -> failwith "multiple files not supported" +and process_a_generated_a_env_a_toplevel rule env ccs = + Common.profile_code "process_a_ctl_a_env_a_toplevel" + (fun () -> process_a_generated_a_env_a_toplevel2 rule env ccs) (* does side effects on C ast and on Cocci info rule *) -and process_a_ctl_a_env_a_toplevel2 r e c f = - indent_do (fun () -> +and process_a_ctl_a_env_a_toplevel2 r e c f = + indent_do (fun () -> show_or_not_celem "trying" c.ast_c; Flag.currentfile := Some (f ^ ":" ^get_celem c.ast_c); - let (trans_info, returned_any_states, inherited_bindings, newbindings) = - Common.save_excursion Flag_ctl.loop_in_src_code (fun () -> + let (trans_info, returned_any_states, inherited_bindings, newbindings) = + Common.save_excursion Flag_ctl.loop_in_src_code (fun () -> Flag_ctl.loop_in_src_code := !Flag_ctl.loop_in_src_code||c.contain_loop; - + (***************************************) (* !Main point! The call to the engine *) (***************************************) let model_ctl = CCI.model_for_ctl r.dropped_isos (Common.some c.flow) e in CCI.mysat model_ctl r.ctl (r.used_after, e) - ) + ) in - if not returned_any_states + if not returned_any_states then None else begin show_or_not_celem "found match in" c.ast_c; show_or_not_trans_info trans_info; - List.iter (show_or_not_binding "out") newbindings; + List.iter (show_or_not_binding "out") newbindings; r.was_matched := true; if not (null trans_info) then begin c.was_modified := true; - try + try (* les "more than one var in a decl" et "already tagged token" * font crasher coccinelle. Si on a 5 fichiers, donc on a 5 * failed. Le try limite le scope des crashes pendant la @@ -1350,162 +1506,299 @@ and process_a_ctl_a_env_a_toplevel2 r e c f = Some (List.map (function x -> x@inherited_bindings) newbindings) end ) - -and process_a_ctl_a_env_a_toplevel a b c f= - Common.profile_code "process_a_ctl_a_env_a_toplevel" + +and process_a_ctl_a_env_a_toplevel a b c f= + Common.profile_code "process_a_ctl_a_env_a_toplevel" (fun () -> process_a_ctl_a_env_a_toplevel2 a b c f) -and process_a_generated_a_env_a_toplevel2 r env = function - [cfile] -> - let free_vars = - List.filter - (function - (rule,_) when rule = r.rulename -> false - | (_,"ARGS") -> false - | _ -> true) - r.free_vars in - let env_domain = List.map (function (nm,vl) -> nm) env in - let metavars = - List.filter - (function md -> - let (rl,_) = Ast_cocci.get_meta_name md in - rl = r.rulename) - r.metavars in - if Common.include_set free_vars env_domain - then Unparse_hrule.pp_rule metavars r.ast_rule env cfile.full_fname - | _ -> failwith "multiple files not supported" - -and process_a_generated_a_env_a_toplevel rule env ccs = - Common.profile_code "process_a_ctl_a_env_a_toplevel" - (fun () -> process_a_generated_a_env_a_toplevel2 rule env ccs) - +let rec bigloop2 rs (ccs: file_info list) = + let init_es = [(Ast_c.emptyMetavarsBinding,[])] in + let es = ref init_es in + let ccs = ref ccs in + let rules_that_have_ever_matched = ref [] in -(*****************************************************************************) -(* The main function *) -(*****************************************************************************) + (* looping over the rules *) + rs +> List.iter (fun r -> + match r with + InitialScriptRuleCocciInfo r | FinalScriptRuleCocciInfo r -> () + | ScriptRuleCocciInfo r -> + if !Flag_cocci.show_ctl_text then begin + Common.pr_xxxxxxxxxxxxxxxxx (); + pr ("script: " ^ r.language); + Common.pr_xxxxxxxxxxxxxxxxx (); -let full_engine2 (coccifile, isofile) cfiles = + adjust_pp_with_indent (fun () -> + Format.force_newline(); + let (l,mv,code) = r.scr_ast_rule in + let deps = r.scr_dependencies in + Pretty_print_cocci.unparse + (Ast_cocci.ScriptRule (l,deps,mv,code))); + end; - show_or_not_cfiles cfiles; - show_or_not_cocci coccifile isofile; - Pycocci.set_coccifile coccifile; + if !Flag.show_misc then print_endline "RESULT ="; - let isofile = - if not (Common.lfile_exists isofile) - then begin - pr2 ("warning: Can't find default iso file: " ^ isofile); - None - end - else Some isofile - in + let (_, newes) = + List.fold_left + (function (cache, newes) -> + function (e, rules_that_have_matched) -> + match r.language with + "python" -> + apply_python_rule r cache newes e rules_that_have_matched + rules_that_have_ever_matched + | "test" -> + concat_headers_and_c !ccs +> List.iter (fun (c,_) -> + if c.flow <> None + then + Printf.printf "Flow: %s\r\nFlow!\r\n%!" c.fullstring); + (cache, newes) + | _ -> + Printf.printf "Unknown language: %s\n" r.language; + (cache, newes) + ) + ([],[]) !es in - (* useful opti when use -dir *) - let (metavars,astcocci,free_var_lists,negated_pos_lists,used_after_lists, - positions_lists,toks,_) = - sp_of_file coccifile isofile - in - let ctls = - Common.memoized _hctl (coccifile, isofile) (fun () -> - ctls_of_ast astcocci used_after_lists positions_lists) - in + es := (if newes = [] then init_es else newes); + | CocciRuleCocciInfo r -> + apply_cocci_rule r rules_that_have_ever_matched + es ccs); - let contain_typedmetavar = sp_contain_typed_metavar astcocci in + if !Flag.sgrep_mode2 + then begin + (* sgrep can lead to code that is not parsable, but we must + * still call rebuild_info_c_and_headers to pretty print the + * action (MINUS), so that later the diff will show what was + * matched by sgrep. But we don't want the parsing error message + * hence the following flag setting. So this code propably + * will generate a NotParsedCorrectly for the matched parts + * and the very final pretty print and diff will work + *) + Flag_parsing_c.verbose_parsing := false; + ccs := rebuild_info_c_and_headers !ccs false + end; + !ccs (* return final C asts *) - (* optimisation allowing to launch coccinelle on all the drivers *) - if !Flag_cocci.worth_trying_opt && not (worth_trying cfiles toks) - then begin - pr2 ("not worth trying:" ^ Common.join " " cfiles); - cfiles +> List.map (fun s -> s, None) - end - else begin +let bigloop a b = + Common.profile_code "bigloop" (fun () -> bigloop2 a b) - if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx(); - if !Flag.show_misc then pr "let's go"; - if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx(); +let initial_final_bigloop2 ty rebuild r = + if !Flag_cocci.show_ctl_text then + begin + Common.pr_xxxxxxxxxxxxxxxxx (); + pr (ty ^ ": " ^ r.language); + Common.pr_xxxxxxxxxxxxxxxxx (); - g_contain_typedmetavar := contain_typedmetavar; + adjust_pp_with_indent (fun () -> + Format.force_newline(); + Pretty_print_cocci.unparse(rebuild r.scr_ast_rule r.scr_dependencies)); + end; - check_macro_in_sp_and_adjust toks; + match r.language with + "python" -> + (* include_match makes no sense in an initial or final rule, although + we have no way to prevent it *) + let _ = apply_python_rule r [] [] [] [] (ref []) in + () + | _ -> + Printf.printf "Unknown language for initial/final script: %s\n" + r.language - +let initial_final_bigloop a b c = + Common.profile_code "initial_final_bigloop" + (fun () -> initial_final_bigloop2 a b c) - let cocci_infos = - prepare_cocci ctls free_var_lists negated_pos_lists - used_after_lists positions_lists metavars astcocci in - let choose_includes = - match !Flag_cocci.include_options with - Flag_cocci.I_UNSPECIFIED -> - if contain_typedmetavar - then Flag_cocci.I_NORMAL_INCLUDES - else Flag_cocci.I_NO_INCLUDES - | x -> x in - let c_infos = prepare_c cfiles choose_includes in +(*****************************************************************************) +(* The main functions *) +(*****************************************************************************) - show_or_not_ctl_tex astcocci ctls; +let pre_engine2 (coccifile, isofile) = + show_or_not_cocci coccifile isofile; + Pycocci.set_coccifile coccifile; - (* ! the big loop ! *) - let c_infos' = bigloop cocci_infos c_infos in + let isofile = + if not (Common.lfile_exists isofile) + then begin + pr2 ("warning: Can't find default iso file: " ^ isofile); + None + end + else Some isofile in - if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx (); - if !Flag.show_misc then pr "Finished"; - if !Flag_ctl.graphical_trace then gen_pdf_graph (); - if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx(); + (* useful opti when use -dir *) + let (metavars,astcocci,free_var_lists,negated_pos_lists,used_after_lists, + positions_lists,toks,_) = + sp_of_file coccifile isofile in + let ctls = ctls_of_ast astcocci used_after_lists positions_lists in + + g_contain_typedmetavar := sp_contain_typed_metavar astcocci; + + check_macro_in_sp_and_adjust toks; + + show_or_not_ctl_tex astcocci ctls; + + let cocci_infos = + prepare_cocci ctls free_var_lists negated_pos_lists + used_after_lists positions_lists metavars astcocci in + + let used_languages = + List.fold_left + (function languages -> + function + ScriptRuleCocciInfo(r) -> + if List.mem r.language languages then + languages + else + r.language::languages + | _ -> languages) + [] cocci_infos in + + let initialized_languages = + List.fold_left + (function languages -> + function + InitialScriptRuleCocciInfo(r) -> + (if List.mem r.language languages + then + failwith + ("double initializer found for "^r.language)); + if interpret_dependencies [] [] r.scr_dependencies + then + begin + initial_final_bigloop "initial" + (fun (x,_,y) -> fun deps -> + Ast_cocci.InitialScriptRule(x,deps,y)) + r; + r.language::languages + end + else languages + | _ -> languages) + [] cocci_infos in + + let uninitialized_languages = + List.filter + (fun used -> not (List.mem used initialized_languages)) + used_languages + in + List.iter (fun lgg -> + initial_final_bigloop "initial" + (fun (x,_,y) -> fun deps -> + Ast_cocci.InitialScriptRule(x,deps,y)) + (make_init (-1) lgg Ast_cocci.NoDep ""); + ) + uninitialized_languages; - c_infos' +> List.map (fun c_or_h -> - if !(c_or_h.was_modified_once) - then begin - let outfile = Common.new_temp_file "cocci-output" ("-" ^ c_or_h.fname) - in + (cocci_infos,toks) - if c_or_h.fkind = Header - then pr2 ("a header file was modified: " ^ c_or_h.fname); +let pre_engine a = + Common.profile_code "pre_engine" (fun () -> pre_engine2 a) - (* and now unparse everything *) - cfile_of_program (for_unparser c_or_h.asts) outfile; +let full_engine2 (cocci_infos,toks) cfiles = - let show_only_minus = !Flag.sgrep_mode2 in - show_or_not_diff c_or_h.fpath outfile show_only_minus; + show_or_not_cfiles cfiles; - (c_or_h.fpath, - if !Flag.sgrep_mode2 then None else Some outfile - ) - end - else - (c_or_h.fpath, None) - ); - end + (* optimisation allowing to launch coccinelle on all the drivers *) + if !Flag_cocci.worth_trying_opt && not (worth_trying cfiles toks) + then + begin + pr2 ("No matches found for " ^ (Common.join " " (Common.union_all toks)) + ^ "\nSkipping:" ^ (Common.join " " cfiles)); + cfiles +> List.map (fun s -> s, None) + end + else + begin -let full_engine a b = - Common.profile_code "full_engine" (fun () -> full_engine2 a b) + if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx(); + if !Flag.show_misc then pr "let's go"; + if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx(); + + let choose_includes = + match !Flag_cocci.include_options with + Flag_cocci.I_UNSPECIFIED -> + if !g_contain_typedmetavar + then Flag_cocci.I_NORMAL_INCLUDES + else Flag_cocci.I_NO_INCLUDES + | x -> x in + let c_infos = prepare_c cfiles choose_includes in + + (* ! the big loop ! *) + let c_infos' = bigloop cocci_infos c_infos in + + if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx (); + if !Flag.show_misc then pr "Finished"; + if !Flag.show_misc then Common.pr_xxxxxxxxxxxxxxxxx (); + if !Flag_ctl.graphical_trace then gen_pdf_graph (); + + c_infos' +> List.map (fun c_or_h -> + if !(c_or_h.was_modified_once) + then + begin + let outfile = + Common.new_temp_file "cocci-output" ("-" ^ c_or_h.fname) in + + if c_or_h.fkind =*= Header + then pr2 ("a header file was modified: " ^ c_or_h.fname); + + (* and now unparse everything *) + cfile_of_program (for_unparser c_or_h.asts) outfile; + + show_or_not_diff c_or_h.fpath outfile; + + (c_or_h.fpath, + if !Flag.sgrep_mode2 then None else Some outfile) + end + else (c_or_h.fpath, None)) + end +let full_engine a b = + Common.profile_code "full_engine" + (fun () -> let res = full_engine2 a b in (*Gc.print_stat stderr; *)res) + +let post_engine2 (cocci_infos,_) = + let _ = + List.fold_left + (function languages -> + function + FinalScriptRuleCocciInfo(r) -> + (if List.mem r.language languages + then failwith ("double finalizer found for "^r.language)); + initial_final_bigloop "final" + (fun (x,_,y) -> fun deps -> Ast_cocci.FinalScriptRule(x,deps,y)) + r; + r.language::languages + | _ -> languages) + [] cocci_infos in + () + +let post_engine a = + Common.profile_code "post_engine" (fun () -> post_engine2 a) (*****************************************************************************) (* check duplicate from result of full_engine *) (*****************************************************************************) -let check_duplicate_modif2 xs = +let check_duplicate_modif2 xs = (* opti: let groups = Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) xs *) - pr2 ("Check duplication for " ^ i_to_s (List.length xs) ^ " files"); + if !Flag_cocci.verbose_cocci + then pr2 ("Check duplication for " ^ i_to_s (List.length xs) ^ " files"); + let groups = Common.group_assoc_bykey_eff xs in - groups +> Common.map_filter (fun (file, xs) -> + groups +> Common.map_filter (fun (file, xs) -> match xs with | [] -> raise Impossible | [res] -> Some (file, res) - | res::xs -> - match res with - | None -> - if not (List.for_all (fun res2 -> res2 = None) xs) + | res::xs -> + match res with + | None -> + if not (List.for_all (fun res2 -> res2 =*= None) xs) then begin pr2 ("different modification result for " ^ file); None end else Some (file, None) - | Some res -> - if not(List.for_all (fun res2 -> + | Some res -> + if not(List.for_all (fun res2 -> match res2 with | None -> false - | Some res2 -> + | Some res2 -> let diff = Common.cmd_to_list ("diff -u -b -B "^res^" "^res2) in null diff @@ -1514,9 +1807,7 @@ let check_duplicate_modif2 xs = None end else Some (file, Some res) - - ) -let check_duplicate_modif a = +let check_duplicate_modif a = Common.profile_code "check_duplicate" (fun () -> check_duplicate_modif2 a)