From 2047f4c5351ccd87eaa3084dd40204495503b0f5 Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Mon, 2 Mar 2020 08:17:50 -0800 Subject: [PATCH] [preanal] inlining synthetic methods as a pre-analysis Summary: This is so that all pre-analyses are together instead of spread across several modules. PS: this function is the worst. Reviewed By: ngorogiannis Differential Revision: D19973285 fbshipit-source-id: b326e99cd --- infer/src/IR/Cfg.ml | 95 ------------------------------- infer/src/IR/Cfg.mli | 3 - infer/src/IR/SourceFiles.ml | 1 - infer/src/backend/preanal.ml | 107 ++++++++++++++++++++++++++++++++++- 4 files changed, 105 insertions(+), 101 deletions(-) diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index f6e9d068b..bf4bbda46 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -56,101 +56,6 @@ let store source_file cfg = 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 {e= Exp.Lfield (Exp.Var _, fn, ft); root_typ; typ}, [(* getter for fields *) (e1, _)] - -> - let instr' = - Sil.Load {id= ret_id; e= Exp.Lfield (e1, fn, ft); root_typ; typ; loc= loc_call} - in - found instr instr' - | Sil.Load {e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [] when Pvar.is_global pvar - -> - (* getter for static fields *) - let instr' = - Sil.Load {id= ret_id; e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; loc= loc_call} - in - found instr instr' - | ( Sil.Store {e1= Exp.Lfield (_, fn, ft); root_typ; typ} - , [(* setter for fields *) (e1, _); (e2, _)] ) -> - let instr' = Sil.Store {e1= Exp.Lfield (e1, fn, ft); root_typ; typ; e2; loc= loc_call} in - found instr instr' - | Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [(e1, _)] - when Pvar.is_global pvar -> - (* setter for static fields *) - let instr' = - Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; e2= e1; loc= 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 (Procname.Java java_pn as pn)), etl, loc, _) -> ( - match Procname.Hash.find cfg pn with - | pd -> - let is_access = 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 - let is_generated_for_lambda = - String.is_substring ~substring:"$Lambda$" (Procname.get_method pn) - in - (* this is a temporary hack in order to stop synthetic inlining on - methods that are generated for lambda rewritting *) - if is_generated_for_lambda then instr - else 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 | Not_found_s _) -> - 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 Procname.is_java pname then proc_inline_synthetic_methods cfg pdesc in - 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 ; diff --git a/infer/src/IR/Cfg.mli b/infer/src/IR/Cfg.mli index 8aa99e88e..362eae7b5 100644 --- a/infer/src/IR/Cfg.mli +++ b/infer/src/IR/Cfg.mli @@ -31,7 +31,4 @@ val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t val iter_sorted : t -> f:(Procdesc.t -> unit) -> unit (** Iterate over all the proc descs in the cfg in ascending order *) -val inline_java_synthetic_methods : t -> unit -(** Inline the java synthetic methods in the cfg (in-place) *) - val pp_proc_signatures : Format.formatter -> t -> unit diff --git a/infer/src/IR/SourceFiles.ml b/infer/src/IR/SourceFiles.ml index 580b75d26..ed371231b 100644 --- a/infer/src/IR/SourceFiles.ml +++ b/infer/src/IR/SourceFiles.ml @@ -28,7 +28,6 @@ let get_existing_data source_file = let add source_file cfg tenv integer_type_widths = - Cfg.inline_java_synthetic_methods cfg ; let tenv, proc_names = (* The same source file may get captured several times in a single capture event, for instance because it is compiled with different options, or from different symbolic links. This may diff --git a/infer/src/backend/preanal.ml b/infer/src/backend/preanal.ml index 74d7c7cd4..cfacdee75 100644 --- a/infer/src/backend/preanal.ml +++ b/infer/src/backend/preanal.ml @@ -35,6 +35,107 @@ module AddAbstractionInstructions = struct Procdesc.iter_nodes do_node pdesc end +(** Find synthetic (including access and bridge) Java methods in the procedure and inline them in + the cfg. + + This is a horrible hack that inlines only *one* instruction ouf of the callee. This works only + on some synthetic methods that have a particular shape. *) +module InlineJavaSyntheticMethods = struct + (** 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 + "inline_synthetic_method translated the call %a as %a (original instr %a)@\n" Procname.pp + (Procdesc.get_proc_name pdesc) + (Sil.pp_instr ~print_types:true Pp.text) + instr' + (Sil.pp_instr ~print_types:true Pp.text) + instr ; + Some instr' + in + let do_instr instr = + match (instr, etl) with + | ( Sil.Load {e= Exp.Lfield (Exp.Var _, fn, ft); root_typ; typ} + , [(* getter for fields *) (e1, _)] ) -> + let instr' = + Sil.Load {id= ret_id; e= Exp.Lfield (e1, fn, ft); root_typ; typ; loc= loc_call} + in + found instr instr' + | Sil.Load {e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [] when Pvar.is_global pvar + -> + (* getter for static fields *) + let instr' = + Sil.Load + {id= ret_id; e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; loc= loc_call} + in + found instr instr' + | ( Sil.Store {e1= Exp.Lfield (_, fn, ft); root_typ; typ} + , [(* setter for fields *) (e1, _); (e2, _)] ) -> + let instr' = Sil.Store {e1= Exp.Lfield (e1, fn, ft); root_typ; typ; e2; loc= loc_call} in + found instr instr' + | Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [(e1, _)] + when Pvar.is_global pvar -> + (* setter for static fields *) + let instr' = + Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; e2= e1; loc= 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 + + + let process pdesc = + let is_generated_for_lambda proc_name = + String.is_substring ~substring:"$Lambda$" (Procname.get_method proc_name) + in + let should_inline proc_name = + (not (is_generated_for_lambda proc_name)) + && + match Attributes.load proc_name with + | None -> + false + | Some attributes -> + let is_access = + match proc_name with + | Procname.Java java_proc_name -> + Procname.Java.is_access_method java_proc_name + | _ -> + false + in + let is_synthetic = attributes.is_synthetic_method in + let is_bridge = attributes.is_bridge_method in + is_access || is_bridge || is_synthetic + in + let instr_inline_synthetic_method _node (instr : Sil.instr) = + match instr with + | Call (ret_id_typ, Const (Cfun pn), etl, loc, _) when should_inline pn -> + Option.bind (Procdesc.load pn) ~f:(fun proc_desc_callee -> + inline_synthetic_method ret_id_typ etl proc_desc_callee loc ) + |> Option.value ~default:instr + | _ -> + instr + in + Procdesc.replace_instrs pdesc ~f:instr_inline_synthetic_method |> ignore +end + (** perform liveness analysis and insert Nullify/Remove_temps instructions into the IR to make it easy for analyses to do abstract garbage collection *) module Liveness = struct @@ -236,8 +337,10 @@ end let do_preanalysis exe_env pdesc = let summary = Summary.OnDisk.reset pdesc in let tenv = Exe_env.get_tenv exe_env (Procdesc.get_proc_name pdesc) in - if Config.function_pointer_specialization && not (Procname.is_java (Procdesc.get_proc_name pdesc)) - then FunctionPointerSubstitution.process summary tenv ; + let proc_name = Procdesc.get_proc_name pdesc in + if Procname.is_java proc_name then InlineJavaSyntheticMethods.process pdesc ; + if Config.function_pointer_specialization && not (Procname.is_java proc_name) then + FunctionPointerSubstitution.process summary tenv ; Liveness.process summary tenv ; AddAbstractionInstructions.process pdesc ; NoReturn.process pdesc ;