(* * Copyright (c) 2009-2013, Monoidics ltd. * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! IStd module L = Logging module F = Format (** data type for the control flow graph *) type t = Procdesc.t Typ.Procname.Hash.t let create () = Typ.Procname.Hash.create 16 let iter_over_sorted_procs cfg ~f = let compare_proc_desc_by_proc_name pdesc1 pdesc2 = Typ.Procname.compare (Procdesc.get_proc_name pdesc1) (Procdesc.get_proc_name pdesc2) in Typ.Procname.Hash.fold (fun _ pdesc acc -> pdesc :: acc) cfg [] |> List.sort ~compare:compare_proc_desc_by_proc_name |> List.iter ~f let get_all_defined_proc_names cfg = let procs = ref [] in let f pname pdesc = if Procdesc.is_defined pdesc then procs := pname :: !procs in Typ.Procname.Hash.iter f cfg ; !procs (** Create a new procdesc *) let create_proc_desc cfg (proc_attributes : ProcAttributes.t) = let pdesc = Procdesc.from_proc_attributes proc_attributes in Typ.Procname.Hash.add cfg proc_attributes.proc_name pdesc ; pdesc let iter_sorted cfg ~f = iter_over_sorted_procs cfg ~f let store source_file cfg = let save_proc _ proc_desc = let attributes = Procdesc.get_attributes proc_desc in let loc = attributes.loc in let attributes' = let loc' = if Location.equal loc Location.dummy then {loc with file= source_file} else loc in {attributes with loc= loc'; translation_unit= source_file} in Procdesc.set_attributes proc_desc attributes' ; Attributes.store ~proc_desc:(Some proc_desc) attributes' in Typ.Procname.Hash.iter save_proc cfg (** Inline a synthetic (access or bridge) method. *) let inline_synthetic_method ((ret_id, _) as ret) etl pdesc loc_call : Sil.instr option = let found instr instr' = L.(debug Analysis Verbose) "XX inline_synthetic_method found instr: %a@." (Sil.pp_instr ~print_types:true Pp.text) instr ; L.(debug Analysis Verbose) "XX inline_synthetic_method instr': %a@." (Sil.pp_instr ~print_types:true Pp.text) instr' ; Some instr' in let do_instr instr = match (instr, etl) with | Sil.Load (_, Exp.Lfield (Exp.Var _, fn, ft), bt, _), [(* getter for fields *) (e1, _)] -> let instr' = Sil.Load (ret_id, Exp.Lfield (e1, fn, ft), bt, loc_call) in found instr instr' | Sil.Load (_, Exp.Lfield (Exp.Lvar pvar, fn, ft), bt, _), [] when Pvar.is_global pvar -> (* getter for static fields *) let instr' = Sil.Load (ret_id, Exp.Lfield (Exp.Lvar pvar, fn, ft), bt, loc_call) in found instr instr' | Sil.Store (Exp.Lfield (_, fn, ft), bt, _, _), [(* setter for fields *) (e1, _); (e2, _)] -> let instr' = Sil.Store (Exp.Lfield (e1, fn, ft), bt, e2, loc_call) in found instr instr' | Sil.Store (Exp.Lfield (Exp.Lvar pvar, fn, ft), bt, _, _), [(e1, _)] when Pvar.is_global pvar -> (* setter for static fields *) let instr' = Sil.Store (Exp.Lfield (Exp.Lvar pvar, fn, ft), bt, e1, loc_call) in found instr instr' | Sil.Call (_, Exp.Const (Const.Cfun pn), etl', _, cf), _ when Int.equal (List.length etl') (List.length etl) -> let instr' = Sil.Call (ret, Exp.Const (Const.Cfun pn), etl, loc_call, cf) in found instr instr' | Sil.Call (_, Exp.Const (Const.Cfun pn), etl', _, cf), _ when Int.equal (List.length etl' + 1) (List.length etl) -> let etl1 = match List.rev etl with (* remove last element *) | _ :: l -> List.rev l | [] -> assert false in let instr' = Sil.Call (ret, Exp.Const (Const.Cfun pn), etl1, loc_call, cf) in found instr instr' | _ -> None in Procdesc.find_map_instrs ~f:do_instr pdesc (** Find synthetic (access or bridge) Java methods in the procedure and inline them in the cfg. *) let proc_inline_synthetic_methods cfg pdesc : unit = let instr_inline_synthetic_method _node instr = match instr with | Sil.Call (ret_id_typ, Exp.Const (Const.Cfun (Typ.Procname.Java java_pn as pn)), etl, loc, _) -> ( match Typ.Procname.Hash.find cfg pn with | pd -> let is_access = Typ.Procname.Java.is_access_method java_pn in let attributes = Procdesc.get_attributes pd in let is_synthetic = attributes.is_synthetic_method in let is_bridge = attributes.is_bridge_method in if is_access || is_bridge || is_synthetic then inline_synthetic_method ret_id_typ etl pd loc |> Option.value ~default:instr else instr | exception Caml.Not_found -> instr ) | _ -> instr in let (_updated : bool) = Procdesc.replace_instrs pdesc ~f:instr_inline_synthetic_method in () let inline_java_synthetic_methods cfg = let f pname pdesc = if Typ.Procname.is_java pname then proc_inline_synthetic_methods cfg pdesc in Typ.Procname.Hash.iter f cfg let pp_proc_signatures fmt cfg = F.fprintf fmt "@[METHOD SIGNATURES@;" ; iter_over_sorted_procs ~f:(Procdesc.pp_signature fmt) cfg ; F.fprintf fmt "@]"