[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
master
Jules Villard 5 years ago committed by Facebook Github Bot
parent e27e07603a
commit 2047f4c535

@ -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 "@[<v>METHOD SIGNATURES@;" ;
iter_over_sorted_procs ~f:(Procdesc.pp_signature fmt) cfg ;

@ -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

@ -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

@ -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 ;

Loading…
Cancel
Save