|
|
|
@ -1774,193 +1774,197 @@ let prop_reset_inst inst_map prop =
|
|
|
|
|
replace_sigma_footprint sigma_fp' (replace_sigma sigma' prop)
|
|
|
|
|
|
|
|
|
|
(** {2 Attributes} *)
|
|
|
|
|
module Attribute = struct
|
|
|
|
|
|
|
|
|
|
(** Return the exp and attribute marked in the atom if any, and return None otherwise *)
|
|
|
|
|
let atom_get_attribute atom =
|
|
|
|
|
match atom with
|
|
|
|
|
| Sil.Apred _ | Anpred _ -> Some atom
|
|
|
|
|
| _ -> None
|
|
|
|
|
(** Return the exp and attribute marked in the atom if any, and return None otherwise *)
|
|
|
|
|
let atom_get atom =
|
|
|
|
|
match atom with
|
|
|
|
|
| Sil.Apred _ | Anpred _ -> Some atom
|
|
|
|
|
| _ -> None
|
|
|
|
|
|
|
|
|
|
(** Check whether an atom is used to mark an attribute *)
|
|
|
|
|
let atom_is a =
|
|
|
|
|
atom_get a <> None
|
|
|
|
|
|
|
|
|
|
(** Add an attribute associated to the argument expressions *)
|
|
|
|
|
let add ?(footprint = false) ?(polarity = true) prop attr args =
|
|
|
|
|
prop_atom_and ~footprint prop
|
|
|
|
|
(if polarity then Sil.Apred (attr, args) else Sil.Anpred (attr, args))
|
|
|
|
|
|
|
|
|
|
let attributes_in_same_category attr1 attr2 =
|
|
|
|
|
let cat1 = PredSymb.to_category attr1 in
|
|
|
|
|
let cat2 = PredSymb.to_category attr2 in
|
|
|
|
|
PredSymb.category_equal cat1 cat2
|
|
|
|
|
|
|
|
|
|
(** Replace an attribute associated to the expression *)
|
|
|
|
|
let add_or_replace_check_changed check_attribute_change prop atom0 =
|
|
|
|
|
match atom0 with
|
|
|
|
|
| Sil.Apred (att0, ((_ :: _) as exps0)) | Anpred (att0, ((_ :: _) as exps0)) ->
|
|
|
|
|
let nexps = IList.map (fun e -> exp_normalize_prop prop e) exps0 in
|
|
|
|
|
let nexp = IList.hd nexps in (* len nexps = len exps0 > 0 by match *)
|
|
|
|
|
let natom = Sil.atom_replace_exp (IList.combine exps0 nexps) atom0 in
|
|
|
|
|
let atom_map = function
|
|
|
|
|
| Sil.Apred (att, exp :: _) | Anpred (att, exp :: _)
|
|
|
|
|
when Exp.equal nexp exp && attributes_in_same_category att att0 ->
|
|
|
|
|
check_attribute_change att att0;
|
|
|
|
|
natom
|
|
|
|
|
| atom ->
|
|
|
|
|
atom in
|
|
|
|
|
let pi = get_pi prop in
|
|
|
|
|
let pi' = IList.map_changed atom_map pi in
|
|
|
|
|
if pi == pi'
|
|
|
|
|
then prop_atom_and prop natom
|
|
|
|
|
else replace_pi pi' prop
|
|
|
|
|
| _ ->
|
|
|
|
|
prop
|
|
|
|
|
|
|
|
|
|
let add_or_replace prop atom =
|
|
|
|
|
(* wrapper for the most common case: do nothing *)
|
|
|
|
|
let check_attr_changed = (fun _ _ -> ()) in
|
|
|
|
|
add_or_replace_check_changed check_attr_changed prop atom
|
|
|
|
|
|
|
|
|
|
(** Get all the attributes of the prop *)
|
|
|
|
|
let get_all prop =
|
|
|
|
|
let res = ref [] in
|
|
|
|
|
let do_atom a = match atom_get a with
|
|
|
|
|
| Some attr -> res := attr :: !res
|
|
|
|
|
| None -> () in
|
|
|
|
|
IList.iter do_atom prop.pi;
|
|
|
|
|
IList.rev !res
|
|
|
|
|
|
|
|
|
|
(** Get all the attributes of the prop *)
|
|
|
|
|
let get_for_symb prop att =
|
|
|
|
|
IList.filter (function
|
|
|
|
|
| Sil.Apred (att', _) | Anpred (att', _) -> PredSymb.equal att' att
|
|
|
|
|
| _ -> false
|
|
|
|
|
) (get_pi prop)
|
|
|
|
|
|
|
|
|
|
(** Get the attribute associated to the expression, if any *)
|
|
|
|
|
let get_for_exp prop exp =
|
|
|
|
|
let nexp = exp_normalize_prop prop exp in
|
|
|
|
|
let atom_get_attr attributes atom =
|
|
|
|
|
match atom with
|
|
|
|
|
| Sil.Apred (_, es) | Anpred (_, es) when IList.mem Exp.equal nexp es -> atom :: attributes
|
|
|
|
|
| _ -> attributes in
|
|
|
|
|
IList.fold_left atom_get_attr [] prop.pi
|
|
|
|
|
|
|
|
|
|
let get prop exp category =
|
|
|
|
|
let atts = get_for_exp prop exp in
|
|
|
|
|
try
|
|
|
|
|
Some
|
|
|
|
|
(IList.find (function
|
|
|
|
|
| Sil.Apred (att, _) | Anpred (att, _) ->
|
|
|
|
|
PredSymb.category_equal (PredSymb.to_category att) category
|
|
|
|
|
| _ -> false
|
|
|
|
|
) atts)
|
|
|
|
|
with Not_found -> None
|
|
|
|
|
|
|
|
|
|
(** Check whether an atom is used to mark an attribute *)
|
|
|
|
|
let atom_is_attribute a =
|
|
|
|
|
atom_get_attribute a <> None
|
|
|
|
|
let get_undef prop exp =
|
|
|
|
|
get prop exp ACundef
|
|
|
|
|
|
|
|
|
|
(** Get the attribute associated to the expression, if any *)
|
|
|
|
|
let get_attributes prop exp =
|
|
|
|
|
let nexp = exp_normalize_prop prop exp in
|
|
|
|
|
let atom_get_attr attributes atom =
|
|
|
|
|
match atom with
|
|
|
|
|
| Sil.Apred (_, es) | Anpred (_, es) when IList.mem Exp.equal nexp es -> atom :: attributes
|
|
|
|
|
| _ -> attributes in
|
|
|
|
|
IList.fold_left atom_get_attr [] prop.pi
|
|
|
|
|
|
|
|
|
|
let attributes_in_same_category attr1 attr2 =
|
|
|
|
|
let cat1 = PredSymb.to_category attr1 in
|
|
|
|
|
let cat2 = PredSymb.to_category attr2 in
|
|
|
|
|
PredSymb.category_equal cat1 cat2
|
|
|
|
|
|
|
|
|
|
let get_attribute prop exp category =
|
|
|
|
|
let atts = get_attributes prop exp in
|
|
|
|
|
try
|
|
|
|
|
Some
|
|
|
|
|
(IList.find (function
|
|
|
|
|
| Sil.Apred (att, _) | Anpred (att, _) ->
|
|
|
|
|
PredSymb.category_equal (PredSymb.to_category att) category
|
|
|
|
|
| _ -> false
|
|
|
|
|
) atts)
|
|
|
|
|
with Not_found -> None
|
|
|
|
|
|
|
|
|
|
let get_undef_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACundef
|
|
|
|
|
|
|
|
|
|
let get_resource_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACresource
|
|
|
|
|
|
|
|
|
|
let get_taint_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACtaint
|
|
|
|
|
|
|
|
|
|
let get_autorelease_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACautorelease
|
|
|
|
|
|
|
|
|
|
let get_objc_null_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACobjc_null
|
|
|
|
|
|
|
|
|
|
let get_div0_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACdiv0
|
|
|
|
|
|
|
|
|
|
let get_observer_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACobserver
|
|
|
|
|
|
|
|
|
|
let get_retval_attribute prop exp =
|
|
|
|
|
get_attribute prop exp PredSymb.ACretval
|
|
|
|
|
|
|
|
|
|
let has_dangling_uninit_attribute prop exp =
|
|
|
|
|
let la = get_attributes prop exp in
|
|
|
|
|
IList.exists (function
|
|
|
|
|
| Sil.Apred (a, _) -> PredSymb.equal a (Adangling DAuninit)
|
|
|
|
|
| _ -> false
|
|
|
|
|
) la
|
|
|
|
|
|
|
|
|
|
(** Get all the attributes of the prop *)
|
|
|
|
|
let get_all_attributes prop =
|
|
|
|
|
let res = ref [] in
|
|
|
|
|
let do_atom a = match atom_get_attribute a with
|
|
|
|
|
| Some attr -> res := attr :: !res
|
|
|
|
|
| None -> () in
|
|
|
|
|
IList.iter do_atom prop.pi;
|
|
|
|
|
IList.rev !res
|
|
|
|
|
|
|
|
|
|
(** Set an attribute associated to the argument expressions *)
|
|
|
|
|
let set_attribute ?(footprint = false) ?(polarity = true) prop attr args =
|
|
|
|
|
prop_atom_and ~footprint prop
|
|
|
|
|
(if polarity then Sil.Apred (attr, args) else Sil.Anpred (attr, args))
|
|
|
|
|
|
|
|
|
|
(** Replace an attribute associated to the expression *)
|
|
|
|
|
let add_or_replace_attribute_check_changed check_attribute_change prop atom0 =
|
|
|
|
|
match atom0 with
|
|
|
|
|
| Sil.Apred (att0, ((_ :: _) as exps0)) | Anpred (att0, ((_ :: _) as exps0)) ->
|
|
|
|
|
let nexps = IList.map (fun e -> exp_normalize_prop prop e) exps0 in
|
|
|
|
|
let nexp = IList.hd nexps in (* len nexps = len exps0 > 0 by match *)
|
|
|
|
|
let natom = Sil.atom_replace_exp (IList.combine exps0 nexps) atom0 in
|
|
|
|
|
let atom_map = function
|
|
|
|
|
| Sil.Apred (att, exp :: _) | Anpred (att, exp :: _)
|
|
|
|
|
when Exp.equal nexp exp && attributes_in_same_category att att0 ->
|
|
|
|
|
check_attribute_change att att0;
|
|
|
|
|
natom
|
|
|
|
|
| atom ->
|
|
|
|
|
atom in
|
|
|
|
|
let pi = get_pi prop in
|
|
|
|
|
let pi' = IList.map_changed atom_map pi in
|
|
|
|
|
if pi == pi'
|
|
|
|
|
then prop_atom_and prop natom
|
|
|
|
|
else replace_pi pi' prop
|
|
|
|
|
| _ ->
|
|
|
|
|
prop
|
|
|
|
|
let get_resource prop exp =
|
|
|
|
|
get prop exp ACresource
|
|
|
|
|
|
|
|
|
|
let add_or_replace_attribute prop atom =
|
|
|
|
|
(* wrapper for the most common case: do nothing *)
|
|
|
|
|
let check_attr_changed = (fun _ _ -> ()) in
|
|
|
|
|
add_or_replace_attribute_check_changed check_attr_changed prop atom
|
|
|
|
|
let get_taint prop exp =
|
|
|
|
|
get prop exp ACtaint
|
|
|
|
|
|
|
|
|
|
(** mark Exp.Var's or Exp.Lvar's as undefined *)
|
|
|
|
|
let mark_vars_as_undefined prop vars_to_mark callee_pname ret_annots loc path_pos =
|
|
|
|
|
let att_undef = PredSymb.Aundef (callee_pname, ret_annots, loc, path_pos) in
|
|
|
|
|
let mark_var_as_undefined exp prop =
|
|
|
|
|
match exp with
|
|
|
|
|
| Exp.Var _ | Lvar _ -> add_or_replace_attribute prop (Apred (att_undef, [exp]))
|
|
|
|
|
| _ -> prop in
|
|
|
|
|
IList.fold_left (fun prop id -> mark_var_as_undefined id prop) prop vars_to_mark
|
|
|
|
|
let get_autorelease prop exp =
|
|
|
|
|
get prop exp ACautorelease
|
|
|
|
|
|
|
|
|
|
let filter_atoms ~f prop =
|
|
|
|
|
replace_pi (IList.filter f (get_pi prop)) prop
|
|
|
|
|
let get_objc_null prop exp =
|
|
|
|
|
get prop exp ACobjc_null
|
|
|
|
|
|
|
|
|
|
(** Remove an attribute from all the atoms in the heap *)
|
|
|
|
|
let remove_attribute prop att0 =
|
|
|
|
|
let f = function
|
|
|
|
|
| Sil.Apred (att, _) | Anpred (att, _) -> not (PredSymb.equal att0 att)
|
|
|
|
|
| _ -> true in
|
|
|
|
|
filter_atoms ~f prop
|
|
|
|
|
let get_div0 prop exp =
|
|
|
|
|
get prop exp ACdiv0
|
|
|
|
|
|
|
|
|
|
let remove_resource_attribute ra_kind ra_res =
|
|
|
|
|
let f = function
|
|
|
|
|
| Sil.Apred (Aresource res_action, _) ->
|
|
|
|
|
PredSymb.res_act_kind_compare res_action.ra_kind ra_kind <> 0
|
|
|
|
|
|| PredSymb.resource_compare res_action.ra_res ra_res <> 0
|
|
|
|
|
| _ -> true in
|
|
|
|
|
filter_atoms ~f
|
|
|
|
|
|
|
|
|
|
let remove_attribute_from_exp prop atom =
|
|
|
|
|
match atom with
|
|
|
|
|
| Sil.Apred (_, exps) | Anpred (_, exps) ->
|
|
|
|
|
let nexps = IList.map (fun e -> exp_normalize_prop prop e) exps in
|
|
|
|
|
let natom = Sil.atom_replace_exp (IList.combine exps nexps) atom in
|
|
|
|
|
let f a = not (Sil.atom_equal natom a) in
|
|
|
|
|
filter_atoms ~f prop
|
|
|
|
|
| _ ->
|
|
|
|
|
replace_pi (get_pi prop) prop
|
|
|
|
|
|
|
|
|
|
(* Replace an attribute OBJC_NULL($n1) with OBJC_NULL(var) when var = $n1, and also sets $n1 = 0 *)
|
|
|
|
|
let replace_objc_null prop lhs_exp rhs_exp =
|
|
|
|
|
match get_objc_null_attribute prop rhs_exp, rhs_exp with
|
|
|
|
|
| Some atom, Exp.Var _ ->
|
|
|
|
|
let prop = remove_attribute_from_exp prop atom in
|
|
|
|
|
let prop = conjoin_eq rhs_exp Exp.zero prop in
|
|
|
|
|
let natom = Sil.atom_replace_exp [(rhs_exp, lhs_exp)] atom in
|
|
|
|
|
add_or_replace_attribute prop natom
|
|
|
|
|
| _ -> prop
|
|
|
|
|
|
|
|
|
|
let rec nullify_exp_with_objc_null prop exp =
|
|
|
|
|
match exp with
|
|
|
|
|
| Exp.BinOp (_, exp1, exp2) ->
|
|
|
|
|
let prop' = nullify_exp_with_objc_null prop exp1 in
|
|
|
|
|
nullify_exp_with_objc_null prop' exp2
|
|
|
|
|
| Exp.UnOp (_, exp, _) ->
|
|
|
|
|
nullify_exp_with_objc_null prop exp
|
|
|
|
|
| Exp.Var _ ->
|
|
|
|
|
(match get_objc_null_attribute prop exp with
|
|
|
|
|
| Some atom ->
|
|
|
|
|
let prop' = remove_attribute_from_exp prop atom in
|
|
|
|
|
conjoin_eq exp Exp.zero prop'
|
|
|
|
|
| _ -> prop)
|
|
|
|
|
| _ -> prop
|
|
|
|
|
|
|
|
|
|
(** Get all the attributes of the prop *)
|
|
|
|
|
let get_atoms_with_attribute prop att =
|
|
|
|
|
IList.filter (function
|
|
|
|
|
| Sil.Apred (att', _) | Anpred (att', _) -> PredSymb.equal att' att
|
|
|
|
|
| _ -> false
|
|
|
|
|
) (get_pi prop)
|
|
|
|
|
|
|
|
|
|
(** Apply f to every resource attribute in the prop *)
|
|
|
|
|
let attribute_map_resource prop f =
|
|
|
|
|
let attribute_map e = function
|
|
|
|
|
| PredSymb.Aresource ra -> PredSymb.Aresource (f e ra)
|
|
|
|
|
| att -> att in
|
|
|
|
|
let atom_map = function
|
|
|
|
|
| Sil.Apred (att, ([e] as es)) -> Sil.Apred (attribute_map e att, es)
|
|
|
|
|
| Sil.Anpred (att, ([e] as es)) -> Sil.Anpred (attribute_map e att, es)
|
|
|
|
|
| atom -> atom in
|
|
|
|
|
replace_pi (IList.map atom_map (get_pi prop)) prop
|
|
|
|
|
let get_observer prop exp =
|
|
|
|
|
get prop exp ACobserver
|
|
|
|
|
|
|
|
|
|
let get_retval prop exp =
|
|
|
|
|
get prop exp ACretval
|
|
|
|
|
|
|
|
|
|
let has_dangling_uninit prop exp =
|
|
|
|
|
let la = get_for_exp prop exp in
|
|
|
|
|
IList.exists (function
|
|
|
|
|
| Sil.Apred (a, _) -> PredSymb.equal a (Adangling DAuninit)
|
|
|
|
|
| _ -> false
|
|
|
|
|
) la
|
|
|
|
|
|
|
|
|
|
let filter_atoms ~f prop =
|
|
|
|
|
replace_pi (IList.filter f (get_pi prop)) prop
|
|
|
|
|
|
|
|
|
|
let remove prop atom =
|
|
|
|
|
match atom with
|
|
|
|
|
| Sil.Apred (_, exps) | Anpred (_, exps) ->
|
|
|
|
|
let nexps = IList.map (fun e -> exp_normalize_prop prop e) exps in
|
|
|
|
|
let natom = Sil.atom_replace_exp (IList.combine exps nexps) atom in
|
|
|
|
|
let f a = not (Sil.atom_equal natom a) in
|
|
|
|
|
filter_atoms ~f prop
|
|
|
|
|
| _ ->
|
|
|
|
|
replace_pi (get_pi prop) prop
|
|
|
|
|
|
|
|
|
|
(** Remove an attribute from all the atoms in the heap *)
|
|
|
|
|
let remove_for_attr prop att0 =
|
|
|
|
|
let f = function
|
|
|
|
|
| Sil.Apred (att, _) | Anpred (att, _) -> not (PredSymb.equal att0 att)
|
|
|
|
|
| _ -> true in
|
|
|
|
|
filter_atoms ~f prop
|
|
|
|
|
|
|
|
|
|
let remove_resource ra_kind ra_res =
|
|
|
|
|
let f = function
|
|
|
|
|
| Sil.Apred (Aresource res_action, _) ->
|
|
|
|
|
PredSymb.res_act_kind_compare res_action.ra_kind ra_kind <> 0
|
|
|
|
|
|| PredSymb.resource_compare res_action.ra_res ra_res <> 0
|
|
|
|
|
| _ -> true in
|
|
|
|
|
filter_atoms ~f
|
|
|
|
|
|
|
|
|
|
(** Apply f to every resource attribute in the prop *)
|
|
|
|
|
let map_resource prop f =
|
|
|
|
|
let attribute_map e = function
|
|
|
|
|
| PredSymb.Aresource ra -> PredSymb.Aresource (f e ra)
|
|
|
|
|
| att -> att in
|
|
|
|
|
let atom_map = function
|
|
|
|
|
| Sil.Apred (att, ([e] as es)) -> Sil.Apred (attribute_map e att, es)
|
|
|
|
|
| Sil.Anpred (att, ([e] as es)) -> Sil.Anpred (attribute_map e att, es)
|
|
|
|
|
| atom -> atom in
|
|
|
|
|
replace_pi (IList.map atom_map (get_pi prop)) prop
|
|
|
|
|
|
|
|
|
|
(* Replace an attribute OBJC_NULL($n1) with OBJC_NULL(var) when var = $n1, and also sets $n1 =
|
|
|
|
|
0 *)
|
|
|
|
|
let replace_objc_null prop lhs_exp rhs_exp =
|
|
|
|
|
match get_objc_null prop rhs_exp, rhs_exp with
|
|
|
|
|
| Some atom, Exp.Var _ ->
|
|
|
|
|
let prop = remove prop atom in
|
|
|
|
|
let prop = conjoin_eq rhs_exp Exp.zero prop in
|
|
|
|
|
let natom = Sil.atom_replace_exp [(rhs_exp, lhs_exp)] atom in
|
|
|
|
|
add_or_replace prop natom
|
|
|
|
|
| _ -> prop
|
|
|
|
|
|
|
|
|
|
let rec nullify_exp_with_objc_null prop exp =
|
|
|
|
|
match exp with
|
|
|
|
|
| Exp.BinOp (_, exp1, exp2) ->
|
|
|
|
|
let prop' = nullify_exp_with_objc_null prop exp1 in
|
|
|
|
|
nullify_exp_with_objc_null prop' exp2
|
|
|
|
|
| Exp.UnOp (_, exp, _) ->
|
|
|
|
|
nullify_exp_with_objc_null prop exp
|
|
|
|
|
| Exp.Var _ ->
|
|
|
|
|
(match get_objc_null prop exp with
|
|
|
|
|
| Some atom ->
|
|
|
|
|
let prop' = remove prop atom in
|
|
|
|
|
conjoin_eq exp Exp.zero prop'
|
|
|
|
|
| _ -> prop)
|
|
|
|
|
| _ -> prop
|
|
|
|
|
|
|
|
|
|
(** mark Exp.Var's or Exp.Lvar's as undefined *)
|
|
|
|
|
let mark_vars_as_undefined prop vars_to_mark callee_pname ret_annots loc path_pos =
|
|
|
|
|
let att_undef = PredSymb.Aundef (callee_pname, ret_annots, loc, path_pos) in
|
|
|
|
|
let mark_var_as_undefined exp prop =
|
|
|
|
|
match exp with
|
|
|
|
|
| Exp.Var _ | Lvar _ -> add_or_replace prop (Apred (att_undef, [exp]))
|
|
|
|
|
| _ -> prop in
|
|
|
|
|
IList.fold_left (fun prop id -> mark_var_as_undefined id prop) prop vars_to_mark
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
(** type for arithmetic problems *)
|
|
|
|
|
type arith_problem =
|
|
|
|
@ -1979,7 +1983,7 @@ let find_arithmetic_problem proc_node_session prop exp =
|
|
|
|
|
match exp_normalize_prop prop e with
|
|
|
|
|
| Exp.Const c when iszero_int_float c -> true
|
|
|
|
|
| _ ->
|
|
|
|
|
res := add_or_replace_attribute !res (Apred (Adiv0 proc_node_session, [e]));
|
|
|
|
|
res := Attribute.add_or_replace !res (Apred (Adiv0 proc_node_session, [e]));
|
|
|
|
|
false in
|
|
|
|
|
let rec walk = function
|
|
|
|
|
| Exp.Var _ -> ()
|
|
|
|
@ -2038,7 +2042,7 @@ let deallocate_stack_vars p pvars =
|
|
|
|
|
begin
|
|
|
|
|
stack_vars_address_in_post := v :: !stack_vars_address_in_post;
|
|
|
|
|
let pred = Sil.Apred (Adangling DAaddr_stack_var, [Exp.Var freshv]) in
|
|
|
|
|
res := add_or_replace_attribute !res pred
|
|
|
|
|
res := Attribute.add_or_replace !res pred
|
|
|
|
|
end in
|
|
|
|
|
IList.iter do_var !fresh_address_vars;
|
|
|
|
|
!res in
|
|
|
|
@ -2837,7 +2841,7 @@ let find_equal_formal_path e prop =
|
|
|
|
|
match find_in_sigma e [] with
|
|
|
|
|
| Some vfs -> Some vfs
|
|
|
|
|
| None ->
|
|
|
|
|
match get_objc_null_attribute prop e with
|
|
|
|
|
match Attribute.get_objc_null prop e with
|
|
|
|
|
| Some (Apred (Aobjc_null, [_; vfs])) -> Some vfs
|
|
|
|
|
| _ -> None
|
|
|
|
|
|
|
|
|
|