[racerd] refactor transition function

Summary: The transition function was using a complicated-looking and possibly-inefficient pattern of options for everything, when simple pattern matching with guards is sufficient.

Reviewed By: skcho

Differential Revision: D21179606

fbshipit-source-id: a37f97594
master
Nikos Gorogiannis 5 years ago committed by Facebook GitHub Bot
parent 5d8b4947fe
commit ab79b2966a

@ -34,8 +34,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
None
let add_access formals loc ~is_write_access locks threads ownership
(proc_data : extras ProcData.t) access_domain exp =
let get_first_actual actuals = List.hd actuals |> Option.bind ~f:get_access_exp
let apply_to_first_actual ~f astate actuals =
get_first_actual actuals |> Option.value_map ~default:astate ~f
let add_access formals loc ~is_write_access locks threads ownership tenv access_domain exp =
let open Domain in
let rec add_field_accesses prefix_path acc = function
| [] ->
@ -44,7 +49,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let prefix_path' = Option.value_exn (AccessExpression.add_access prefix_path access) in
if
(not (HilExp.Access.is_field_or_array_access access))
|| RacerDModels.is_safe_access access prefix_path proc_data.tenv
|| RacerDModels.is_safe_access access prefix_path tenv
then add_field_accesses prefix_path' acc access_list
else
let is_write = List.is_empty access_list && is_write_access in
@ -60,13 +65,14 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
add_field_accesses base acc accesses )
let make_container_access formals ret_base callee_pname ~is_write receiver_ap callee_loc tenv
let make_container_access proc_data ret_base callee_pname ~is_write receiver_ap callee_loc
(astate : Domain.t) =
let open Domain in
let ProcData.{extras= formals; tenv} = proc_data in
if
AttributeMapDomain.is_synchronized astate.attribute_map receiver_ap
|| RacerDModels.is_synchronized_container callee_pname receiver_ap tenv
then None
then astate
else
let ownership_pre = OwnershipDomain.get_owned receiver_ap astate.ownership in
let callee_access =
@ -77,16 +83,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
OwnershipDomain.add (AccessExpression.base ret_base) ownership_pre astate.ownership
in
let accesses = AccessDomain.add_opt callee_access astate.accesses in
Some {astate with accesses; ownership}
{astate with accesses; ownership}
let add_reads formals exps loc ({accesses; locks; threads; ownership} as astate : Domain.t)
proc_data =
let accesses' =
let add_reads formals exps loc ({accesses; locks; threads; ownership} as astate : Domain.t) tenv =
let accesses =
List.fold exps ~init:accesses
~f:(add_access formals loc ~is_write_access:false locks threads ownership proc_data)
~f:(add_access formals loc ~is_write_access:false locks threads ownership tenv)
in
{astate with accesses= accesses'}
{astate with accesses}
let expand_actuals formals actuals accesses pdesc =
@ -153,28 +158,24 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let open RacerDModels in
let open RacerDDomain in
if RacerDModels.is_synchronized_container_constructor tenv callee_pname actuals then
List.hd actuals |> Option.bind ~f:get_access_exp
|> Option.value_map ~default:astate ~f:(fun receiver ->
let attribute_map =
AttributeMapDomain.add receiver Synchronized astate.attribute_map
in
{astate with attribute_map} )
apply_to_first_actual astate actuals ~f:(fun receiver ->
let attribute_map = AttributeMapDomain.add receiver Synchronized astate.attribute_map in
{astate with attribute_map} )
else if RacerDModels.is_converter_to_synchronized_container tenv callee_pname actuals then
let attribute_map =
AttributeMapDomain.add (AccessExpression.base ret_base) Synchronized astate.attribute_map
in
{astate with attribute_map}
else if is_box callee_pname then
match actuals with
| HilExp.AccessExpression actual_access_expr :: _
when AttributeMapDomain.is_functional astate.attribute_map actual_access_expr ->
(* TODO: check for constants, which are functional? *)
let attribute_map =
AttributeMapDomain.add (AccessExpression.base ret_base) Functional astate.attribute_map
in
{astate with attribute_map}
| _ ->
astate
apply_to_first_actual astate actuals ~f:(fun actual_access_expr ->
if AttributeMapDomain.is_functional astate.attribute_map actual_access_expr then
(* TODO: check for constants, which are functional? *)
let attribute_map =
AttributeMapDomain.add (AccessExpression.base ret_base) Functional
astate.attribute_map
in
{astate with attribute_map}
else astate )
else
let ownership =
OwnershipDomain.add (AccessExpression.base ret_base) OwnershipAbstractValue.owned
@ -183,37 +184,27 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{astate with ownership}
let treat_call_acquiring_ownership ret_base procname actuals loc
({ProcData.tenv; extras} as proc_data) astate () =
let do_call_acquiring_ownership ret_base astate =
let open Domain in
if RacerDModels.acquires_ownership procname tenv then
let astate = add_reads extras actuals loc astate proc_data in
let ownership =
OwnershipDomain.add (AccessExpression.base ret_base) OwnershipAbstractValue.owned
astate.ownership
in
Some {astate with ownership}
else None
let ownership =
OwnershipDomain.add (AccessExpression.base ret_base) OwnershipAbstractValue.owned
astate.ownership
in
{astate with ownership}
let treat_container_accesses ret_base callee_pname actuals loc {ProcData.tenv; extras} astate () =
let open RacerDModels in
Option.bind (get_container_access callee_pname tenv) ~f:(fun container_access ->
match List.hd actuals with
| Some (HilExp.AccessExpression receiver_expr) ->
let is_write =
match container_access with ContainerWrite -> true | ContainerRead -> false
in
make_container_access extras ret_base callee_pname ~is_write receiver_expr loc tenv
astate
| _ ->
L.internal_error "Call to %a is marked as a container write, but has no receiver"
Procname.pp callee_pname ;
None )
let do_container_access ~is_write ret_base callee_pname actuals loc proc_data astate =
match get_first_actual actuals with
| Some receiver_expr ->
make_container_access proc_data ret_base callee_pname ~is_write receiver_expr loc astate
| None ->
L.internal_error "Call to %a is marked as a container access, but has no receiver"
Procname.pp callee_pname ;
astate
let do_proc_call ret_base callee_pname actuals call_flags loc {ProcData.tenv; summary; extras}
(astate : Domain.t) () =
(astate : Domain.t) =
let open Domain in
let open RacerDModels in
let open ConcurrencyModels in
@ -317,12 +308,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{astate_callee with ownership; attribute_map}
let do_assignment lhs_access_exp rhs_exp loc ({ProcData.tenv; extras} as proc_data)
(astate : Domain.t) =
let do_assignment lhs_access_exp rhs_exp loc {ProcData.tenv; extras} (astate : Domain.t) =
let open Domain in
let rhs_accesses =
add_access extras loc ~is_write_access:false astate.locks astate.threads astate.ownership
proc_data astate.accesses rhs_exp
add_access extras loc ~is_write_access:false astate.locks astate.threads astate.ownership tenv
astate.accesses rhs_exp
in
let rhs_access_exprs = HilExp.get_access_exprs rhs_exp in
let is_functional =
@ -345,7 +335,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
rhs_accesses
else
add_access extras loc ~is_write_access:true astate.locks astate.threads astate.ownership
proc_data rhs_accesses (HilExp.AccessExpression lhs_access_exp)
tenv rhs_accesses (HilExp.AccessExpression lhs_access_exp)
in
let ownership = OwnershipDomain.propagate_assignment lhs_access_exp rhs_exp astate.ownership in
let attribute_map =
@ -354,7 +344,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{astate with accesses; ownership; attribute_map}
let do_assume formals assume_exp loc proc_data (astate : Domain.t) =
let do_assume formals assume_exp loc tenv (astate : Domain.t) =
let open Domain in
let apply_choice bool_value (acc : Domain.t) = function
| Attribute.LockHeld ->
@ -373,7 +363,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in
let accesses =
add_access formals loc ~is_write_access:false astate.locks astate.threads astate.ownership
proc_data astate.accesses assume_exp
tenv astate.accesses assume_exp
in
let astate' =
match HilExp.get_access_exprs assume_exp with
@ -390,16 +380,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{astate' with accesses}
let exec_instr (astate : Domain.t) ({ProcData.summary; extras} as proc_data) _
(instr : HilInstr.t) =
match instr with
let exec_instr astate ({ProcData.summary; extras; tenv} as proc_data) _ instr =
match (instr : HilInstr.t) with
| Call (ret_base, Direct callee_pname, actuals, call_flags, loc) ->
let astate = add_reads extras actuals loc astate proc_data in
treat_call_acquiring_ownership ret_base callee_pname actuals loc proc_data astate ()
|> IOption.if_none_evalopt
~f:(treat_container_accesses ret_base callee_pname actuals loc proc_data astate)
|> IOption.if_none_eval
~f:(do_proc_call ret_base callee_pname actuals call_flags loc proc_data astate)
let astate = add_reads extras actuals loc astate tenv in
if RacerDModels.acquires_ownership callee_pname tenv then
do_call_acquiring_ownership ret_base astate
else if RacerDModels.is_container_write tenv callee_pname then
do_container_access ~is_write:true ret_base callee_pname actuals loc proc_data astate
else if RacerDModels.is_container_read tenv callee_pname then
do_container_access ~is_write:false ret_base callee_pname actuals loc proc_data astate
else do_proc_call ret_base callee_pname actuals call_flags loc proc_data astate
| Call (_, Indirect _, _, _, _) ->
if Procname.is_java (Summary.get_proc_name summary) then
L.(die InternalError) "Unexpected indirect call instruction %a" HilInstr.pp instr
@ -407,7 +398,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Assign (lhs_access_expr, rhs_exp, loc) ->
do_assignment lhs_access_expr rhs_exp loc proc_data astate
| Assume (assume_exp, _, _, loc) ->
do_assume extras assume_exp loc proc_data astate
do_assume extras assume_exp loc tenv astate
| Metadata _ ->
astate

@ -20,8 +20,6 @@ module AnnotationAliases = struct
"Couldn't parse thread-safety annotation aliases; expected list of strings"
end
type container_access = ContainerRead | ContainerWrite
let make_android_support_template suffix methods =
let open MethodMatcher in
[ {default with classname= "android.support.v4.util." ^ suffix; methods}
@ -137,23 +135,27 @@ let is_cpp_container_write =
fun pname -> QualifiedCppName.Match.match_qualifiers matcher (Procname.get_qualifiers pname)
let get_container_access pn tenv =
let is_container_write tenv pn =
match pn with
| Procname.Java _ when is_java_container_write tenv pn [] ->
Some ContainerWrite
| Procname.Java _ when is_java_container_read tenv pn [] ->
Some ContainerRead
true
| (Procname.ObjC_Cpp _ | C _) when is_cpp_container_write pn ->
true
| _ ->
false
let is_container_read tenv pn =
match pn with
| Procname.Java _ ->
None
is_java_container_read tenv pn []
(* The following order matters: we want to check if pname is a container write
before we check if pname is a container read. This is due to a different
treatment between std::map::operator[] and all other operator[]. *)
| (Procname.ObjC_Cpp _ | C _) when is_cpp_container_write pn ->
Some ContainerWrite
| (Procname.ObjC_Cpp _ | C _) when is_cpp_container_read pn ->
Some ContainerRead
| Procname.ObjC_Cpp _ | C _ ->
(not (is_cpp_container_write pn)) && is_cpp_container_read pn
| _ ->
None
false
(** holds of procedure names which should not be analyzed in order to avoid known sources of

@ -7,9 +7,9 @@
open! IStd
type container_access = ContainerRead | ContainerWrite
val is_container_read : Tenv.t -> Procname.t -> bool
val get_container_access : Procname.t -> Tenv.t -> container_access option
val is_container_write : Tenv.t -> Procname.t -> bool
(** return Some (access) if this procedure accesses the contents of a container (e.g., Map.get) *)
val has_return_annot : (Annot.Item.t -> bool) -> Procname.t -> bool

Loading…
Cancel
Save