diff --git a/infer/src/IR/Instrs.ml b/infer/src/IR/Instrs.ml index 886a6b025..5b37fa4e5 100644 --- a/infer/src/IR/Instrs.ml +++ b/infer/src/IR/Instrs.ml @@ -91,6 +91,15 @@ let map_changed = if phys_equal instrs instrs' then t else NotReversed instrs' +let concat_map_changed ~equal (NotReversed instrs as t) ~f = + let instrs' = Array.concat_map ~f instrs in + if + Int.equal (Array.length instrs) (Array.length instrs') + && Array.for_all2_exn ~f:equal instrs instrs' + then t + else NotReversed instrs' + + let reverse_order (NotReversed instrs) = Reversed (RevArray.of_rev_array instrs) (* Functions on both reversed and non-reversed arrays *) diff --git a/infer/src/IR/Instrs.mli b/infer/src/IR/Instrs.mli index 80cd12c8f..a973d819a 100644 --- a/infer/src/IR/Instrs.mli +++ b/infer/src/IR/Instrs.mli @@ -33,6 +33,12 @@ val map_changed : -> f:(Sil.instr -> Sil.instr) -> not_reversed_t +val concat_map_changed : + equal:(Sil.instr -> Sil.instr -> bool) + -> not_reversed_t + -> f:(Sil.instr -> Sil.instr array) + -> not_reversed_t + val reverse_order : not_reversed_t -> reversed t val is_empty : _ t -> bool diff --git a/infer/src/IR/Procdesc.ml b/infer/src/IR/Procdesc.ml index f856acc82..bb0e19d5b 100644 --- a/infer/src/IR/Procdesc.ml +++ b/infer/src/IR/Procdesc.ml @@ -228,6 +228,15 @@ module Node = struct true ) + (** Like [replace_instrs], but 1 instr gets replaced by 0, 1, or more instructions. *) + let replace_instrs_by node ~f = + let instrs' = Instrs.concat_map_changed ~equal:phys_equal node.instrs ~f:(f node) in + if phys_equal instrs' node.instrs then false + else ( + node.instrs <- instrs' ; + true ) + + let pp_stmt fmt = function | AssertionFailure -> F.pp_print_string fmt "Assertion failure" @@ -508,13 +517,22 @@ let find_map_instrs pdesc ~f = find_map_nodes ~f:find_map_node pdesc -let replace_instrs pdesc ~f = - let f updated node = - Node.replace_instrs ~f node || (* do not short-circuit [Node.replace_instrs] *) updated - in +let update_nodes pdesc ~(update : Node.t -> bool) : bool = + let f acc node = update node || acc in + (* do not shortcut call to [update] *) fold_nodes pdesc ~init:false ~f +let replace_instrs pdesc ~f = + let update node = Node.replace_instrs ~f node in + update_nodes pdesc ~update + + +let replace_instrs_by pdesc ~f = + let update node = Node.replace_instrs_by ~f node in + update_nodes pdesc ~update + + (** fold between two nodes or until we reach a branching structure *) let fold_slope_range = let rec aux node visited acc ~f = diff --git a/infer/src/IR/Procdesc.mli b/infer/src/IR/Procdesc.mli index 309490ff7..1b06d0ee0 100644 --- a/infer/src/IR/Procdesc.mli +++ b/infer/src/IR/Procdesc.mli @@ -261,6 +261,10 @@ val replace_instrs : t -> f:(Node.t -> Sil.instr -> Sil.instr) -> bool (** Map and replace the instructions to be executed. Returns true if at least one substitution occured. *) +val replace_instrs_by : t -> f:(Node.t -> Sil.instr -> Sil.instr array) -> bool +(** Like [replace_instrs], but slower, and each instruction may be replaced +by 0, 1, or more instructions. *) + val iter_nodes : (Node.t -> unit) -> t -> unit (** iterate over all the nodes of a procedure *) diff --git a/infer/src/IR/Sil.mli b/infer/src/IR/Sil.mli index 31ff0468e..d0ca41eea 100644 --- a/infer/src/IR/Sil.mli +++ b/infer/src/IR/Sil.mli @@ -48,7 +48,7 @@ type instr = [*lexp1:typ = exp2] where [lexp1] is an expression denoting a heap address [typ] is the root type of [lexp1] - [exp2] is the expression whose value is store. *) + [exp2] is the expression whose value is stored. *) | Prune of Exp.t * Location.t * bool * if_kind (** prune the state based on [exp=1], the boolean indicates whether true branch *) | Call of (Ident.t * Typ.t) * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index fda61124c..07df413c9 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -982,6 +982,7 @@ module Procname = struct | Block of Block.t | ObjC_Cpp of ObjC_Cpp.t | WithBlockParameters of t * Block.block_name list + | Topl_method of Java.t [@@deriving compare] let equal = [%compare.equal: t] @@ -1024,6 +1025,8 @@ module Procname = struct match t with | Java j -> Java {j with class_name= new_class} + | Topl_method j -> + Topl_method {j with class_name= new_class} | ObjC_Cpp osig -> ObjC_Cpp {osig with class_name= new_class} | WithBlockParameters (base, blocks) -> @@ -1060,7 +1063,7 @@ module Procname = struct ObjC_Cpp {osig with method_name= new_method_name} | WithBlockParameters (base, blocks) -> WithBlockParameters (objc_cpp_replace_method_name base new_method_name, blocks) - | C _ | Block _ | Linters_dummy_method | Java _ -> + | C _ | Block _ | Linters_dummy_method | Java _ | Topl_method _ -> t @@ -1074,7 +1077,7 @@ module Procname = struct QualifiedCppName.to_qual_string name | Block {name} -> name - | Java j -> + | Java j | Topl_method j -> j.method_name | Linters_dummy_method -> "Linters_dummy_method" @@ -1097,6 +1100,8 @@ module Procname = struct Language.Clang | Java _ -> Language.Java + | Topl_method _ -> + Language.Java (** [is_constructor pname] returns true if [pname] is a constructor *) @@ -1141,7 +1146,7 @@ module Procname = struct (** Very verbose representation of an existing Procname.t *) let rec to_unique_id pn = match pn with - | Java j -> + | Java j | Topl_method j -> Java.to_string j Verbose | C osig -> C.to_string osig Verbose @@ -1158,7 +1163,7 @@ module Procname = struct (** Convert a proc name to a string for the user to see *) let rec to_string p = match p with - | Java j -> + | Java j | Topl_method j -> Java.to_string j Non_verbose | C osig -> C.to_string osig Non_verbose @@ -1175,7 +1180,7 @@ module Procname = struct (** Convenient representation of a procname for external tools (e.g. eclipse plugin) *) let rec to_simplified_string ?(withclass = false) p = match p with - | Java j -> + | Java j | Topl_method j -> Java.to_string ~withclass j Simple | C osig -> C.to_string osig Simple @@ -1213,7 +1218,7 @@ module Procname = struct List.map ~f:(fun par -> Parameter.ClangParameter par) clang_params in match procname with - | Java j -> + | Java j | Topl_method j -> List.map ~f:(fun par -> Parameter.JavaParameter par) (Java.get_parameters j) | C osig -> clang_param_to_param (C.get_parameters osig) @@ -1253,6 +1258,8 @@ module Procname = struct match procname with | Java j -> Java (Java.replace_parameters (params_to_java_params new_parameters) j) + | Topl_method j -> + Topl_method (Java.replace_parameters (params_to_java_params new_parameters) j) | C osig -> C (C.replace_parameters (params_to_clang_params new_parameters) osig) | ObjC_Cpp osig -> diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index f0b084604..b3b960a1c 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -503,6 +503,7 @@ being the name of the struct, [None] means the parameter is of some other type. | Block of Block.t | ObjC_Cpp of ObjC_Cpp.t | WithBlockParameters of t * Block.block_name list + | Topl_method of Java.t [@@deriving compare] val block_name_of_procname : t -> Block.block_name diff --git a/infer/src/backend/exe_env.ml b/infer/src/backend/exe_env.ml index 8f5f117b7..ce1071d89 100644 --- a/infer/src/backend/exe_env.ml +++ b/infer/src/backend/exe_env.ml @@ -85,7 +85,7 @@ let java_global_tenv = let get_column_value ~value_on_java ~file_data_to_value ~column_name exe_env proc_name = match proc_name with - | Typ.Procname.Java _ -> + | Typ.Procname.Java _ | Typ.Procname.Topl_method _ -> Lazy.force value_on_java | _ -> ( match get_file_data exe_env proc_name with diff --git a/infer/src/backend/ondemand.ml b/infer/src/backend/ondemand.ml index ec3610792..9b4e0346c 100644 --- a/infer/src/backend/ondemand.ml +++ b/infer/src/backend/ondemand.ml @@ -59,8 +59,13 @@ let should_be_analyzed proc_attributes = (not (is_active proc_name)) (* avoid infinite loops *) && not (already_analyzed proc_name) +let get_proc_attr proc_name = + IList.pick_first_some + [lazy (Topl.get_proc_attr proc_name); lazy (Summary.proc_resolve_attributes proc_name)] + + let procedure_should_be_analyzed proc_name = - match Summary.proc_resolve_attributes proc_name with + match get_proc_attr proc_name with | Some proc_attributes when Config.reactive_capture && not proc_attributes.is_defined -> (* try to capture procedure first *) let defined_proc_attributes = OndemandCapture.try_capture proc_attributes in @@ -270,11 +275,10 @@ let analyze_proc_desc ~caller_pdesc callee_pdesc = (** Find a proc desc for the procedure, perhaps loading it from disk. *) let get_proc_desc callee_pname = - match Procdesc.load callee_pname with - | Some _ as pdesc_opt -> - pdesc_opt - | None -> - Option.map ~f:Summary.get_proc_desc (Summary.get callee_pname) + IList.pick_first_some + [ lazy (Procdesc.load callee_pname) + ; lazy (Topl.get_proc_desc callee_pname) + ; lazy (Option.map ~f:Summary.get_proc_desc (Summary.get callee_pname)) ] (** analyze_proc_name ?caller_pdesc proc_name performs an on-demand analysis of proc_name triggered diff --git a/infer/src/backend/printer.ml b/infer/src/backend/printer.ml index fea014c5f..aa66f1174 100644 --- a/infer/src/backend/printer.ml +++ b/infer/src/backend/printer.ml @@ -210,9 +210,10 @@ let node_finish_session node = let write_proc_html pdesc = if Config.write_html then ( let pname = Procdesc.get_proc_name pdesc in - let source = (Procdesc.get_loc pdesc).file in + let loc = Procdesc.get_loc pdesc in + let source = loc.file in let nodes = List.sort ~compare:Procdesc.Node.compare (Procdesc.get_nodes pdesc) in - let linenum = (Procdesc.Node.get_loc (List.hd_exn nodes)).Location.line in + let linenum = loc.Location.line in let fd, fmt = Io_infer.Html.create source [Typ.Procname.to_filename pname] in F.fprintf fmt "