From 34b62fc51c0d119c01861730e75b1c46e172be5d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 4 Feb 2016 18:07:59 -0800 Subject: [PATCH] Inline Java synthetic methods systematically before writing the cfg to disk. Summary: public Java synthetic methods used to be inlined when a procedure was being analyzed. This was done almost everywhere. A missing case was when a cfg is loaded during an existing analysis because on-demand needs access to a procedure. Intead of trying to maintain an invariant in all access paths, we now inline them systematically before saving the cfg to disk. A secondary consequence of this is that in debug mode the cfg dotty file will show the inlined cfg, so there's no difference between that view and what happens during analysis. Reviewed By: jeremydubreil Differential Revision: D2903366 fb-gh-sync-id: 252604c --- infer/src/backend/callbacks.ml | 73 ---------------------------- infer/src/backend/callbacks.mli | 3 -- infer/src/backend/cfg.ml | 85 ++++++++++++++++++++++++++++++++- infer/src/backend/interproc.ml | 1 - 4 files changed, 84 insertions(+), 78 deletions(-) diff --git a/infer/src/backend/callbacks.ml b/infer/src/backend/callbacks.ml index 51dc97054..12ee05775 100644 --- a/infer/src/backend/callbacks.ml +++ b/infer/src/backend/callbacks.ml @@ -12,78 +12,6 @@ module L = Logging (** Module to register and invoke callbacks *) -(** Inline a synthetic (access or bridge) method. *) -let inline_synthetic_method ret_ids etl proc_desc proc_name loc_call : Sil.instr option = - let modified = ref None in - let debug = false in - let found instr instr' = - modified := Some instr'; - if debug then - begin - L.stderr "XX inline_synthetic_method found instr: %a@." (Sil.pp_instr pe_text) instr; - L.stderr "XX inline_synthetic_method instr': %a@." (Sil.pp_instr pe_text) instr' - end in - let do_instr node instr = - match instr, ret_ids, etl with - | Sil.Letderef (id1, Sil.Lfield (Sil.Var id2, fn, ft), bt, loc), [ret_id], [(e1, t1)] -> (* getter for fields *) - let instr' = Sil.Letderef (ret_id, Sil.Lfield (e1, fn, ft), bt, loc_call) in - found instr instr' - | Sil.Letderef (id1, Sil.Lfield (Sil.Lvar pvar, fn, ft), bt, loc), [ret_id], [] - when Sil.pvar_is_global pvar -> (* getter for static fields *) - let instr' = Sil.Letderef (ret_id, Sil.Lfield (Sil.Lvar pvar, fn, ft), bt, loc_call) in - found instr instr' - | Sil.Set (Sil.Lfield (ex1, fn, ft), bt , ex2, loc), _, [(e1, t1); (e2, t2)] -> (* setter for fields *) - let instr' = Sil.Set (Sil.Lfield (e1, fn, ft), bt , e2, loc_call) in - found instr instr' - | Sil.Set (Sil.Lfield (Sil.Lvar pvar, fn, ft), bt , ex2, loc), _, [(e1, t1)] - when Sil.pvar_is_global pvar -> (* setter for static fields *) - let instr' = Sil.Set (Sil.Lfield (Sil.Lvar pvar, fn, ft), bt , e1, loc_call) in - found instr instr' - | Sil.Call (ret_ids', Sil.Const (Sil.Cfun pn), etl', loc', cf), _, _ - when IList.length ret_ids = IList.length ret_ids' - && IList.length etl' = IList.length etl -> - let instr' = Sil.Call (ret_ids, Sil.Const (Sil.Cfun pn), etl, loc_call, cf) in - found instr instr' - | Sil.Call (ret_ids', Sil.Const (Sil.Cfun pn), etl', loc', cf), _, _ - when IList.length ret_ids = IList.length ret_ids' - && IList.length etl' + 1 = IList.length etl -> - let etl1 = match IList.rev etl with (* remove last element *) - | _ :: l -> IList.rev l - | [] -> assert false in - let instr' = Sil.Call (ret_ids, Sil.Const (Sil.Cfun pn), etl1, loc_call, cf) in - found instr instr' - | _ -> () in - Cfg.Procdesc.iter_instrs do_instr proc_desc; - !modified - -(** Find synthetic (access or bridge) methods in the procedure and inline them in the cfg. *) -let proc_inline_synthetic_methods cfg proc_desc : unit = - let instr_inline_synthetic_method = function - | Sil.Call (ret_ids, Sil.Const (Sil.Cfun pn), etl, loc, _) -> - (match Cfg.Procdesc.find_from_name cfg pn with - | Some pd -> - let is_access = Procname.java_is_access_method pn in - let attributes = Cfg.Procdesc.get_attributes pd in - let is_synthetic = attributes.ProcAttributes.is_synthetic_method in - let is_bridge = attributes.ProcAttributes.is_bridge_method in - if is_access || is_bridge || is_synthetic - then inline_synthetic_method ret_ids etl pd pn loc - else None - | None -> None) - | _ -> None in - let node_inline_synthetic_methods node = - let modified = ref false in - let do_instr instr = match instr_inline_synthetic_method instr with - | None -> instr - | Some instr' -> - modified := true; - instr' in - let instrs = Cfg.Node.get_instrs node in - let instrs' = IList.map do_instr instrs in - if !modified then Cfg.Node.replace_instrs node instrs' in - Cfg.Procdesc.iter_nodes node_inline_synthetic_methods proc_desc - - type proc_callback_t = Procname.t list -> (Procname.t -> Cfg.Procdesc.t option) -> @@ -119,7 +47,6 @@ let get_procedure_definition exe_env proc_name = let tenv = Exe_env.get_tenv exe_env proc_name in Option.map (fun proc_desc -> - proc_inline_synthetic_methods cfg proc_desc; let idenv = Idenv.create cfg proc_desc and language = (Cfg.Procdesc.get_attributes proc_desc).ProcAttributes.language in (idenv, tenv, proc_name, proc_desc, language)) diff --git a/infer/src/backend/callbacks.mli b/infer/src/backend/callbacks.mli index f603bb4d5..e578bb9b6 100644 --- a/infer/src/backend/callbacks.mli +++ b/infer/src/backend/callbacks.mli @@ -42,6 +42,3 @@ val unregister_all_callbacks : unit -> unit (** Invoke all the registered callbacks. *) val iterate_callbacks : (Procname.t -> unit) -> Cg.t -> Exe_env.t -> unit - -(** Find synthetic (access or bridge) methods in the procedure and inline them in the cfg. *) -val proc_inline_synthetic_methods: Cfg.cfg -> Cfg.Procdesc.t -> unit diff --git a/infer/src/backend/cfg.ml b/infer/src/backend/cfg.ml index c759c4030..16f803a22 100644 --- a/infer/src/backend/cfg.ml +++ b/infer/src/backend/cfg.ml @@ -586,7 +586,7 @@ module Node = struct match get_succs node with | [n] -> if not (NodeSet.mem n !visited) - && not (equal node dst_node) + && not (equal node dst_node) then do_node n | _ -> () end in @@ -967,8 +967,91 @@ let save_attributes filename cfg = AttributesTable.store_attributes attributes' in IList.iter save_proc (get_all_procs cfg) +(** Inline a synthetic (access or bridge) method. *) +let inline_synthetic_method ret_ids etl proc_desc proc_name loc_call : Sil.instr option = + let modified = ref None in + let debug = false in + let found instr instr' = + modified := Some instr'; + if debug then + begin + L.stderr "XX inline_synthetic_method found instr: %a@." (Sil.pp_instr pe_text) instr; + L.stderr "XX inline_synthetic_method instr': %a@." (Sil.pp_instr pe_text) instr' + end in + let do_instr node instr = + match instr, ret_ids, etl with + | Sil.Letderef (id1, Sil.Lfield (Sil.Var id2, fn, ft), bt, loc), + [ret_id], + [(e1, t1)] -> (* getter for fields *) + let instr' = Sil.Letderef (ret_id, Sil.Lfield (e1, fn, ft), bt, loc_call) in + found instr instr' + | Sil.Letderef (id1, Sil.Lfield (Sil.Lvar pvar, fn, ft), bt, loc), [ret_id], [] + when Sil.pvar_is_global pvar -> (* getter for static fields *) + let instr' = Sil.Letderef (ret_id, Sil.Lfield (Sil.Lvar pvar, fn, ft), bt, loc_call) in + found instr instr' + | Sil.Set (Sil.Lfield (ex1, fn, ft), bt , ex2, loc), + _, + [(e1, t1); (e2, t2)] -> (* setter for fields *) + let instr' = Sil.Set (Sil.Lfield (e1, fn, ft), bt , e2, loc_call) in + found instr instr' + | Sil.Set (Sil.Lfield (Sil.Lvar pvar, fn, ft), bt , ex2, loc), _, [(e1, t1)] + when Sil.pvar_is_global pvar -> (* setter for static fields *) + let instr' = Sil.Set (Sil.Lfield (Sil.Lvar pvar, fn, ft), bt , e1, loc_call) in + found instr instr' + | Sil.Call (ret_ids', Sil.Const (Sil.Cfun pn), etl', loc', cf), _, _ + when IList.length ret_ids = IList.length ret_ids' + && IList.length etl' = IList.length etl -> + let instr' = Sil.Call (ret_ids, Sil.Const (Sil.Cfun pn), etl, loc_call, cf) in + found instr instr' + | Sil.Call (ret_ids', Sil.Const (Sil.Cfun pn), etl', loc', cf), _, _ + when IList.length ret_ids = IList.length ret_ids' + && IList.length etl' + 1 = IList.length etl -> + let etl1 = match IList.rev etl with (* remove last element *) + | _ :: l -> IList.rev l + | [] -> assert false in + let instr' = Sil.Call (ret_ids, Sil.Const (Sil.Cfun pn), etl1, loc_call, cf) in + found instr instr' + | _ -> () in + Procdesc.iter_instrs do_instr proc_desc; + !modified + +(** Find synthetic (access or bridge) Java methods in the procedure and inline them in the cfg. *) +let proc_inline_synthetic_methods cfg proc_desc : unit = + let instr_inline_synthetic_method = function + | Sil.Call (ret_ids, Sil.Const (Sil.Cfun pn), etl, loc, _) -> + (match Procdesc.find_from_name cfg pn with + | Some pd -> + let is_access = Procname.java_is_access_method pn in + let attributes = Procdesc.get_attributes pd in + let is_synthetic = attributes.ProcAttributes.is_synthetic_method in + let is_bridge = attributes.ProcAttributes.is_bridge_method in + if is_access || is_bridge || is_synthetic + then inline_synthetic_method ret_ids etl pd pn loc + else None + | None -> None) + | _ -> None in + let node_inline_synthetic_methods node = + let modified = ref false in + let do_instr instr = match instr_inline_synthetic_method instr with + | None -> instr + | Some instr' -> + modified := true; + instr' in + let instrs = Node.get_instrs node in + let instrs' = IList.map do_instr instrs in + if !modified then Node.replace_instrs node instrs' in + Procdesc.iter_nodes node_inline_synthetic_methods proc_desc + +(** Inline the java synthetic methods in the cfg *) +let inline_java_synthetic_methods cfg = + let f proc_name proc_desc = + if Procname.is_java proc_name + then proc_inline_synthetic_methods cfg proc_desc in + iter_proc_desc cfg f + (** Save a cfg into a file *) let store_cfg_to_file (filename : DB.filename) (save_sources : bool) (cfg : cfg) = + inline_java_synthetic_methods cfg; if save_sources then save_source_files cfg; if !Config.incremental_procs then begin diff --git a/infer/src/backend/interproc.ml b/infer/src/backend/interproc.ml index 9bd2dc594..91acfbb25 100644 --- a/infer/src/backend/interproc.ml +++ b/infer/src/backend/interproc.ml @@ -1258,7 +1258,6 @@ let do_analysis exe_env = { (Cfg.Procdesc.get_attributes pdesc) with ProcAttributes.err_log = static_err_log; } in - Callbacks.proc_inline_synthetic_methods cfg pdesc; Specs.init_summary (dep, nodes, proc_flags, calls, None, attributes) in