add AnalysisCallbacks.proc_resolve_attributes

Summary:
Looks like this one was already causing circular dependency issues!
Saves threading the callback through *many* functions.

Reviewed By: ngorogiannis

Differential Revision: D21261636

fbshipit-source-id: 2b23aa07d
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent 0feda26ba1
commit e7ef387dfd

@ -11,7 +11,8 @@ module L = Logging
type callbacks =
{ html_debug_new_node_session_f:
'a. ?kind:[`ComputePre | `ExecNode | `ExecNodeNarrowing | `WTO]
-> pp_name:(Format.formatter -> unit) -> Procdesc.Node.t -> f:(unit -> 'a) -> 'a }
-> pp_name:(Format.formatter -> unit) -> Procdesc.Node.t -> f:(unit -> 'a) -> 'a
; proc_resolve_attributes_f: Procname.t -> ProcAttributes.t option }
let callbacks_ref : callbacks option ref = ref None
@ -27,3 +28,6 @@ let get_callbacks () =
let html_debug_new_node_session ?kind ~pp_name node ~f =
(get_callbacks ()).html_debug_new_node_session_f ?kind ~pp_name node ~f
let proc_resolve_attributes proc_name = (get_callbacks ()).proc_resolve_attributes_f proc_name

@ -17,6 +17,9 @@ val html_debug_new_node_session :
-> 'a
(** set to {!NodePrinter.with_session} *)
val proc_resolve_attributes : Procname.t -> ProcAttributes.t option
(** set to {!Summary.OnDisk.proc_resolve_attributes} *)
(** {2 Callbacks management}*)
(** These callbacks are used to break the dependency cycles between some modules. Specifically, we
@ -25,7 +28,8 @@ val html_debug_new_node_session :
type callbacks =
{ html_debug_new_node_session_f:
'a. ?kind:[`ComputePre | `ExecNode | `ExecNodeNarrowing | `WTO]
-> pp_name:(Format.formatter -> unit) -> Procdesc.Node.t -> f:(unit -> 'a) -> 'a }
-> pp_name:(Format.formatter -> unit) -> Procdesc.Node.t -> f:(unit -> 'a) -> 'a
; proc_resolve_attributes_f: Procname.t -> ProcAttributes.t option }
val set_callbacks : callbacks -> unit
(** make sure this is called before starting any actual analysis *)

@ -160,10 +160,8 @@ let pdesc_return_annot_ends_with pdesc annot =
pdesc_has_return_annot pdesc (fun ia -> ia_ends_with ia annot)
(* note: we would use Summary.proc_resolve_attributes directly instead of requiring [attrs_of_pname],
but doing so creates a circular dependency *)
let pname_has_return_annot pname ~attrs_of_pname predicate =
match attrs_of_pname pname with
let pname_has_return_annot pname predicate =
match AnalysisCallbacks.proc_resolve_attributes pname with
| Some attributes ->
predicate attributes.ProcAttributes.method_annotation.return
| None ->

@ -117,14 +117,8 @@ val ia_is_uithread_equivalent : Annot.Item.t -> bool
val pdesc_has_return_annot : Procdesc.t -> (Annot.Item.t -> bool) -> bool
(** return true if the given predicate evaluates to true on the annotation of [pdesc]'s return value *)
val pname_has_return_annot :
Procname.t
-> attrs_of_pname:(Procname.t -> ProcAttributes.t option)
-> (Annot.Item.t -> bool)
-> bool
(** return true if the given predicate evaluates to true on the annotation of [pname]'s return
value. the function [attrs_of_pname] should resolve the proc attributes of [pname].
Specs.proc_resolve_attributes is a good choice for this resolution function. *)
val pname_has_return_annot : Procname.t -> (Annot.Item.t -> bool) -> bool
(** return true if the given predicate evaluates to true on the annotation of [pname]'s return value *)
val pdesc_return_annot_ends_with : Procdesc.t -> string -> bool
(** return true if [pdesc]'s return value is annotated with a value ending with the given string *)

@ -1687,7 +1687,7 @@ and unknown_or_scan_call ~is_scan ~reason ret_typ ret_annots
let callee_loc_opt =
Option.map
~f:(fun attributes -> attributes.ProcAttributes.loc)
(Summary.OnDisk.proc_resolve_attributes callee_pname)
(AnalysisCallbacks.proc_resolve_attributes callee_pname)
in
let skip_path = Paths.Path.add_skipped_call path callee_pname reason callee_loc_opt in
[(prop_with_undef_attr, skip_path)]
@ -1729,7 +1729,7 @@ and check_variadic_sentinel ?(fails_on_nil = false) n_formals (sentinel, null_po
and check_variadic_sentinel_if_present ({Builtin.prop_; path; proc_name} as builtin_args) =
match Summary.OnDisk.proc_resolve_attributes proc_name with
match AnalysisCallbacks.proc_resolve_attributes proc_name with
| Some callee_attributes -> (
match callee_attributes.ProcAttributes.sentinel_attr with
| Some sentinel ->

@ -771,7 +771,7 @@ let prop_set_exn tenv pname prop se_exn =
(** Include a subtrace for a procedure call if the callee is not a model. *)
let include_subtrace callee_pname =
match Summary.OnDisk.proc_resolve_attributes callee_pname with
match AnalysisCallbacks.proc_resolve_attributes callee_pname with
| Some attrs ->
(not attrs.ProcAttributes.is_biabduction_model)
&& SourceFile.is_under_project_root attrs.ProcAttributes.loc.Location.file

@ -473,7 +473,7 @@ let forward_tabulate summary exe_env tenv proc_cfg wl =
in
while not (Worklist.is_empty wl) do
let curr_node = Worklist.remove wl in
AnalysisData.html_debug_new_node_session ~pp_name curr_node ~f:(fun () ->
AnalysisCallbacks.html_debug_new_node_session ~pp_name curr_node ~f:(fun () ->
do_node_and_handle curr_node )
done ;
L.d_strln ".... Work list empty. Stop ...." ;
@ -704,7 +704,7 @@ let execute_filter_prop summary exe_env tenv proc_cfg
let pdesc = ProcCfg.Exceptional.proc_desc proc_cfg in
let pname = Procdesc.get_proc_name pdesc in
let init_edgeset =
AnalysisData.html_debug_new_node_session ~pp_name init_node ~f:(fun () ->
AnalysisCallbacks.html_debug_new_node_session ~pp_name init_node ~f:(fun () ->
L.d_printfln "#### Start: RE-execution for %a ####" Procname.pp pname ;
L.d_indent 1 ;
L.d_strln "Precond:" ;
@ -720,7 +720,7 @@ let execute_filter_prop summary exe_env tenv proc_cfg
Worklist.add wl init_node ;
ignore (path_set_put_todo wl init_node init_edgeset) ;
forward_tabulate summary exe_env tenv proc_cfg wl ;
AnalysisData.html_debug_new_node_session ~pp_name init_node ~f:(fun () ->
AnalysisCallbacks.html_debug_new_node_session ~pp_name init_node ~f:(fun () ->
L.d_printfln ~color:Green "#### Finished: RE-execution for %a ####" Procname.pp pname ;
L.d_increase_indent () ;
L.d_strln "Precond:" ;
@ -743,7 +743,7 @@ let execute_filter_prop summary exe_env tenv proc_cfg
let spec = BiabductionSummary.{pre; posts; visited} in
L.d_decrease_indent () ; Some spec )
with RE_EXE_ERROR ->
AnalysisData.html_debug_new_node_session ~pp_name init_node ~f:(fun () ->
AnalysisCallbacks.html_debug_new_node_session ~pp_name init_node ~f:(fun () ->
L.d_printfln ~color:Red "#### [FUNCTION %a] ...ERROR" Procname.pp pname ;
L.d_increase_indent () ;
L.d_strln "when starting from pre:" ;
@ -1081,7 +1081,7 @@ let perform_transition proc_cfg tenv proc_name summary =
let with_start_node_session ~f =
match ProcCfg.Exceptional.start_node proc_cfg with
| start_node ->
AnalysisData.html_debug_new_node_session ~pp_name start_node ~f
AnalysisCallbacks.html_debug_new_node_session ~pp_name start_node ~f
| exception exn when SymOp.exn_not_failure exn ->
f ()
in

@ -50,8 +50,7 @@ let is_allocator tenv pname =
let check_attributes check tenv pname =
PatternMatch.check_class_attributes check tenv pname
|| Annotations.pname_has_return_annot pname ~attrs_of_pname:Summary.OnDisk.proc_resolve_attributes
check
|| Annotations.pname_has_return_annot pname check
let method_overrides is_annotated tenv pname =

@ -18,7 +18,11 @@ let () =
()
let () = AnalysisCallbacks.set_callbacks {html_debug_new_node_session_f= NodePrinter.with_session}
let () =
AnalysisCallbacks.set_callbacks
{ html_debug_new_node_session_f= NodePrinter.with_session
; proc_resolve_attributes_f= Summary.OnDisk.proc_resolve_attributes }
type callback_fun =
| Procedure of Callbacks.proc_callback_t

@ -353,8 +353,8 @@ let is_modeled_ui_method =
type annotation_trail = DirectlyAnnotated | Override of Procname.t | SuperClass of Typ.name
[@@deriving compare]
let find_override_or_superclass_annotated ~attrs_of_pname is_annot tenv proc_name =
let is_annotated pn = Annotations.pname_has_return_annot pn ~attrs_of_pname is_annot in
let find_override_or_superclass_annotated is_annot tenv proc_name =
let is_annotated pn = Annotations.pname_has_return_annot pn is_annot in
let is_override = Staged.unstage (PatternMatch.has_same_signature proc_name) in
let rec find_override_or_superclass_aux class_name =
match Tenv.lookup tenv class_name with
@ -376,20 +376,18 @@ let find_override_or_superclass_annotated ~attrs_of_pname is_annot tenv proc_nam
else Procname.get_class_type_name proc_name |> Option.bind ~f:find_override_or_superclass_aux
let annotated_as ~attrs_of_pname predicate tenv pname =
find_override_or_superclass_annotated ~attrs_of_pname predicate tenv pname |> Option.is_some
let annotated_as predicate tenv pname =
find_override_or_superclass_annotated predicate tenv pname |> Option.is_some
let annotated_as_worker_thread ~attrs_of_pname tenv pname =
annotated_as ~attrs_of_pname Annotations.ia_is_worker_thread tenv pname
let annotated_as_worker_thread tenv pname = annotated_as Annotations.ia_is_worker_thread tenv pname
let annotated_as_uithread_equivalent tenv pname =
annotated_as Annotations.ia_is_uithread_equivalent tenv pname
let annotated_as_uithread_equivalent ~attrs_of_pname tenv pname =
annotated_as ~attrs_of_pname Annotations.ia_is_uithread_equivalent tenv pname
let runs_on_ui_thread ~attrs_of_pname tenv pname =
is_modeled_ui_method tenv pname || annotated_as_uithread_equivalent ~attrs_of_pname tenv pname
let runs_on_ui_thread tenv pname =
is_modeled_ui_method tenv pname || annotated_as_uithread_equivalent tenv pname
let is_recursive_lock_type = function

@ -46,18 +46,12 @@ type annotation_trail =
[@@deriving compare]
val find_override_or_superclass_annotated :
attrs_of_pname:(BuiltinDecl.t -> ProcAttributes.t option)
-> (Annot.Item.t -> bool)
-> Tenv.t
-> Procname.t
-> annotation_trail option
(Annot.Item.t -> bool) -> Tenv.t -> Procname.t -> annotation_trail option
(** check if a method's transitive annotations satisfy the given predicate *)
val annotated_as_worker_thread :
attrs_of_pname:(Procname.t -> ProcAttributes.t option) -> Tenv.t -> Procname.t -> bool
val annotated_as_worker_thread : Tenv.t -> Procname.t -> bool
val runs_on_ui_thread :
attrs_of_pname:(Procname.t -> ProcAttributes.t option) -> Tenv.t -> Procname.t -> bool
val runs_on_ui_thread : Tenv.t -> Procname.t -> bool
(** is method not transitively annotated [@WorkerThread] and is modeled or annotated [@UIThread] or
equivalent? *)

@ -11,8 +11,6 @@ module F = Format
module L = Logging
module MF = MarkupFormatter
let attrs_of_pname = Summary.OnDisk.proc_resolve_attributes
module Payload = SummaryPayload.Make (struct
type t = RacerDDomain.summary
@ -496,9 +494,7 @@ let analyze_procedure {Callbacks.exe_env; summary} =
else LockDomain.bottom
in
let threads =
if
runs_on_ui_thread ~attrs_of_pname tenv proc_name
|| RacerDModels.is_thread_confined_method tenv proc_name
if runs_on_ui_thread tenv proc_name || RacerDModels.is_thread_confined_method tenv proc_name
then ThreadsDomain.AnyThreadButSelf
else if
Procdesc.is_java_synchronized proc_desc || RacerDModels.is_marked_thread_safe proc_name tenv

@ -9,8 +9,6 @@ open! IStd
module L = Logging
open ConcurrencyModels
let attrs_of_pname = Summary.OnDisk.proc_resolve_attributes
module AnnotationAliases = struct
let of_json = function
| `List aliases ->
@ -185,7 +183,7 @@ let should_skip =
false
let has_return_annot predicate pn = Annotations.pname_has_return_annot pn ~attrs_of_pname predicate
let has_return_annot predicate pn = Annotations.pname_has_return_annot pn predicate
let is_functional pname =
let is_annotated_functional = has_return_annot Annotations.ia_is_functional in
@ -290,8 +288,8 @@ let is_box = function
completely different classes that don't necessarily run on the same thread as the confined
object. *)
let is_thread_confined_method tenv pname =
ConcurrencyModels.find_override_or_superclass_annotated ~attrs_of_pname
Annotations.ia_is_thread_confined tenv pname
ConcurrencyModels.find_override_or_superclass_annotated Annotations.ia_is_thread_confined tenv
pname
|> Option.is_some
@ -329,8 +327,7 @@ let is_assumed_thread_safe item_annot =
let is_assumed_thread_safe tenv pname =
ConcurrencyModels.find_override_or_superclass_annotated ~attrs_of_pname is_assumed_thread_safe
tenv pname
ConcurrencyModels.find_override_or_superclass_annotated is_assumed_thread_safe tenv pname
|> Option.is_some
@ -357,7 +354,7 @@ let get_current_class_and_threadsafe_superclasses tenv pname =
let is_thread_safe_method pname tenv =
match find_override_or_superclass_annotated ~attrs_of_pname is_thread_safe tenv pname with
match find_override_or_superclass_annotated is_thread_safe tenv pname with
| Some (DirectlyAnnotated | Override _) ->
true
| _ ->
@ -368,8 +365,7 @@ let is_marked_thread_safe pname tenv =
((* current class not marked [@NotThreadSafe] *)
not
(PatternMatch.check_current_class_attributes Annotations.ia_is_not_thread_safe tenv pname))
&& ConcurrencyModels.find_override_or_superclass_annotated ~attrs_of_pname is_thread_safe tenv
pname
&& ConcurrencyModels.find_override_or_superclass_annotated is_thread_safe tenv pname
|> Option.is_some
@ -426,8 +422,8 @@ let should_flag_interface_call tenv exps call_flags pname =
&& (not (is_builder_method java_pname))
(* can't ask anyone to annotate interfaces in library code, and Builders should always be
thread-safe (would be unreasonable to ask everyone to annotate them) *)
&& ConcurrencyModels.find_override_or_superclass_annotated ~attrs_of_pname
thread_safe_or_thread_confined tenv pname
&& ConcurrencyModels.find_override_or_superclass_annotated thread_safe_or_thread_confined tenv
pname
|> Option.is_none
&& receiver_is_not_safe exps tenv
&& not (implements_threadsafe_interface java_pname tenv)

@ -224,16 +224,14 @@ let is_strict_mode_violation tenv pn actuals =
Config.starvation_strict_mode && strict_mode_matcher tenv pn actuals
let is_annotated_nonblocking ~attrs_of_pname tenv pname =
ConcurrencyModels.find_override_or_superclass_annotated ~attrs_of_pname
Annotations.ia_is_nonblocking tenv pname
let is_annotated_nonblocking tenv pname =
ConcurrencyModels.find_override_or_superclass_annotated Annotations.ia_is_nonblocking tenv pname
|> Option.is_some
let is_annotated_lockless ~attrs_of_pname tenv pname =
let is_annotated_lockless tenv pname =
let check annot = Annotations.(ia_ends_with annot lockless) in
ConcurrencyModels.find_override_or_superclass_annotated ~attrs_of_pname check tenv pname
|> Option.is_some
ConcurrencyModels.find_override_or_superclass_annotated check tenv pname |> Option.is_some
let executor_type_str = "java.util.concurrent.Executor"
@ -327,10 +325,10 @@ let get_run_method_from_runnable tenv runnable =
(* Syntactically match for certain methods known to return executors. *)
let get_returned_executor ~attrs_of_pname tenv callee actuals =
let get_returned_executor tenv callee actuals =
let type_check =
lazy
( attrs_of_pname callee
( AnalysisCallbacks.proc_resolve_attributes callee
|> Option.exists ~f:(fun (attrs : ProcAttributes.t) ->
match attrs.ret_type.Typ.desc with
| Tstruct tname | Typ.Tptr ({desc= Tstruct tname}, _) ->

@ -26,12 +26,10 @@ val is_synchronized_library_call : Tenv.t -> Procname.t -> bool
val should_skip_analysis : Tenv.t -> Procname.t -> HilExp.t list -> bool
(** should we treat a method call as skip (eg library methods in guava) to avoid FPs? *)
val is_annotated_nonblocking :
attrs_of_pname:(Procname.t -> ProcAttributes.t option) -> Tenv.t -> Procname.t -> bool
val is_annotated_nonblocking : Tenv.t -> Procname.t -> bool
(** is procedure transitively annotated [@Nonblocking] *)
val is_annotated_lockless :
attrs_of_pname:(Procname.t -> ProcAttributes.t option) -> Tenv.t -> Procname.t -> bool
val is_annotated_lockless : Tenv.t -> Procname.t -> bool
(** is procedure transitively annotated [@Lockless] *)
val schedules_work : Tenv.t -> Procname.t -> bool
@ -51,11 +49,7 @@ val get_run_method_from_runnable : Tenv.t -> HilExp.AccessExpression.t -> Procna
(** given a receiver, find the [run()] method in the appropriate class *)
val get_returned_executor :
attrs_of_pname:(Procname.t -> ProcAttributes.t option)
-> Tenv.t
-> Procname.t
-> HilExp.t list
-> scheduler_thread_constraint option
Tenv.t -> Procname.t -> HilExp.t list -> scheduler_thread_constraint option
(** does the function return an executor and of which thread? *)
val schedules_work_on_ui_thread : Tenv.t -> Procname.t -> bool

@ -12,8 +12,6 @@ module Domain = StarvationDomain
let pname_pp = MF.wrap_monospaced Procname.pp
let attrs_of_pname = Summary.OnDisk.proc_resolve_attributes
module Payload = SummaryPayload.Make (struct
type t = Domain.summary
@ -123,7 +121,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let make_thread thread = {empty_summary with thread} in
let actuals_acc_exps = get_access_expr_list actuals in
let get_returned_executor_summary () =
StarvationModels.get_returned_executor ~attrs_of_pname tenv callee actuals
StarvationModels.get_returned_executor tenv callee actuals
|> Option.map ~f:(fun thread_constraint -> make_ret_attr (WorkScheduler thread_constraint))
in
let get_thread_assert_summary () =
@ -369,17 +367,15 @@ let analyze_procedure {Callbacks.exe_env; summary} =
in
let set_thread_status_by_annotation (astate : Domain.t) =
let thread =
if ConcurrencyModels.annotated_as_worker_thread ~attrs_of_pname tenv procname then
if ConcurrencyModels.annotated_as_worker_thread tenv procname then
Domain.ThreadDomain.BGThread
else if ConcurrencyModels.runs_on_ui_thread ~attrs_of_pname tenv procname then
Domain.ThreadDomain.UIThread
else if ConcurrencyModels.runs_on_ui_thread tenv procname then Domain.ThreadDomain.UIThread
else astate.thread
in
{astate with thread}
in
let filter_blocks =
if StarvationModels.is_annotated_nonblocking ~attrs_of_pname tenv procname then
Domain.filter_blocking_calls
if StarvationModels.is_annotated_nonblocking tenv procname then Domain.filter_blocking_calls
else Fn.id
in
let initial =
@ -695,7 +691,7 @@ let report_on_pair tenv summary (pair : Domain.CriticalPair.t) report_map =
in
let ltr, loc = make_trace_and_loc () in
ReportMap.add_strict_mode_violation tenv pdesc loc ltr error_message report_map
| LockAcquire _ when StarvationModels.is_annotated_lockless ~attrs_of_pname tenv pname ->
| LockAcquire _ when StarvationModels.is_annotated_lockless tenv pname ->
let error_message =
Format.asprintf "Method %a is annotated %s but%a." pname_pp pname
(MF.monospaced_to_string Annotations.lockless)

@ -15,8 +15,6 @@ module InstrCFG = ProcCfg.NormalOneInstrPerNode
module NodeCFG = ProcCfg.Normal
module Node = ProcCfg.DefaultNode
let attrs_of_pname = Summary.OnDisk.proc_resolve_attributes
module Payload = SummaryPayload.Make (struct
type t = CostDomain.summary
@ -378,7 +376,7 @@ let checker {Callbacks.exe_env; summary} : Summary.t =
let bound_map =
compute_bound_map node_cfg inferbo_invariant_map control_dep_invariant_map loop_inv_map
in
let is_on_ui_thread = ConcurrencyModels.runs_on_ui_thread ~attrs_of_pname tenv proc_name in
let is_on_ui_thread = ConcurrencyModels.runs_on_ui_thread tenv proc_name in
let get_node_nb_exec = compute_get_node_nb_exec node_cfg bound_map in
let astate =
let get_summary callee_pname = Payload.read ~caller_summary:summary ~callee_pname in

@ -296,7 +296,9 @@ module Make (T : TransferFunctions.SIL with type CFG.Node.t = Procdesc.Node.t) =
let ai_list = [("ai_rpo", AI_RPO.create_test); ("ai_wto", AI_WTO.create_test)]
let create_tests ?(test_pname = Procname.empty_block) ~initial ?pp_opt make_analysis_data tests =
AnalysisCallbacks.set_callbacks {html_debug_new_node_session_f= NodePrinter.with_session} ;
AnalysisCallbacks.set_callbacks
{ html_debug_new_node_session_f= NodePrinter.with_session
; proc_resolve_attributes_f= Summary.OnDisk.proc_resolve_attributes } ;
let open OUnit2 in
List.concat_map
~f:(fun (name, test_program) ->

Loading…
Cancel
Save