You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2045 lines
86 KiB

(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
open AbsLoc
open! AbstractDomain.Types
module L = Logging
module BoField = BufferOverrunField
module BoUtils = BufferOverrunUtils
module Dom = BufferOverrunDomain
module PO = BufferOverrunProofObligations
module Sem = BufferOverrunSemantics
module Trace = BufferOverrunTrace
open BoUtils.ModelEnv
open ProcnameDispatcher.Call.FuncArg
type exec_fun = model_env -> ret:Ident.t * Typ.t -> Dom.Mem.t -> Dom.Mem.t
type check_fun = model_env -> Dom.Mem.t -> PO.ConditionSet.checked_t -> PO.ConditionSet.checked_t
type model = {exec: exec_fun; check: check_fun}
let no_check _model_env _mem cond_set = cond_set
let no_model =
let exec {pname; location} ~ret mem =
L.d_printfln_escaped "No model for %a" Procname.pp pname ;
Dom.Mem.add_unknown_from ret ~callee_pname:pname ~location mem
in
{exec; check= no_check}
let at ?(size = Int64.zero) array_exp index_exp =
(* TODO? use size *)
let exec {integer_type_widths} ~ret:(id, _) mem =
L.d_printfln_escaped "Using model std::array<_, %Ld>::at" size ;
Dom.Mem.add_stack (Loc.of_id id)
(Sem.eval_lindex integer_type_widths array_exp index_exp mem)
mem
and check {location; integer_type_widths} mem cond_set =
BoUtils.Check.lindex integer_type_widths ~array_exp ~index_exp ~last_included:false mem location
cond_set
in
{exec; check}
let eval_binop ~f e1 e2 =
let exec {integer_type_widths} ~ret:(id, _) mem =
let i1 = Sem.eval integer_type_widths e1 mem |> Dom.Val.get_itv in
let i2 = Sem.eval integer_type_widths e2 mem |> Dom.Val.get_itv in
let v = f i1 i2 |> Dom.Val.of_itv in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}
let get_malloc_info_opt = function
| Exp.BinOp (Binop.Mult _, Exp.Sizeof {typ; nbytes}, length)
| Exp.BinOp (Binop.Mult _, length, Exp.Sizeof {typ; nbytes}) ->
Some (typ, nbytes, length, None)
(* In Java all arrays are dynamically allocated *)
| Exp.Sizeof {typ; nbytes; dynamic_length= Some arr_length} when Language.curr_language_is Java ->
Some (typ, nbytes, arr_length, Some arr_length)
| Exp.Sizeof {typ; nbytes; dynamic_length} ->
Some (typ, nbytes, Exp.one, dynamic_length)
| _ ->
None
let get_malloc_info : Exp.t -> Typ.t * Int.t option * Exp.t * Exp.t option =
fun x ->
get_malloc_info_opt x
|> IOption.if_none_eval ~f:(fun () -> (Typ.mk (Typ.Tint Typ.IChar), Some 1, x, None))
let check_alloc_size ~can_be_zero size_exp {location; integer_type_widths} mem cond_set =
let _, _, length0, _ = get_malloc_info size_exp in
let v_length = Sem.eval integer_type_widths length0 mem in
match Dom.Val.get_itv v_length with
| Bottom ->
cond_set
| NonBottom length ->
let traces = Dom.Val.get_traces v_length in
let latest_prune = Dom.Mem.get_latest_prune mem in
PO.ConditionSet.add_alloc_size location ~can_be_zero ~length traces latest_prune cond_set
let fgets str_exp num_exp =
let exec {integer_type_widths} ~ret:(id, _) mem =
let str_v = Sem.eval integer_type_widths str_exp mem in
let num_v = Sem.eval integer_type_widths num_exp mem in
let traces = Trace.Set.join (Dom.Val.get_traces str_v) (Dom.Val.get_traces num_v) in
let update_strlen1 allocsite arrinfo acc =
let strlen =
let offset = ArrayBlk.ArrInfo.get_offset arrinfo in
let num = Dom.Val.get_itv num_v in
Itv.plus offset (Itv.set_lb_zero (Itv.decr num))
in
Dom.Mem.set_first_idx_of_null (Loc.of_allocsite allocsite) (Dom.Val.of_itv ~traces strlen) acc
in
mem
|> Dom.Mem.update_mem (Sem.eval_locs str_exp mem) Dom.Val.Itv.zero_255
|> ArrayBlk.fold update_strlen1 (Dom.Val.get_array_blk str_v)
|> Dom.Mem.add_stack (Loc.of_id id) {str_v with itv= Itv.zero}
|> Dom.Mem.fgets_alias id (Dom.Val.get_all_locs str_v)
and check {location; integer_type_widths} mem cond_set =
BoUtils.Check.lindex_byte integer_type_widths ~array_exp:str_exp ~byte_index_exp:num_exp
~last_included:true mem location cond_set
in
{exec; check}
let malloc ~can_be_zero size_exp =
let exec ({pname; node_hash; location; tenv; integer_type_widths} as model_env) ~ret:(id, _) mem =
let size_exp = Prop.exp_normalize_noabs tenv Predicates.sub_empty size_exp in
let typ, stride, length0, dyn_length = get_malloc_info size_exp in
let length = Sem.eval integer_type_widths length0 mem in
let traces = Trace.(Set.add_elem location ArrayDeclaration) (Dom.Val.get_traces length) in
let path =
Dom.Mem.find_simple_alias id mem
|> List.find_map ~f:(fun (rhs, i) -> if IntLit.iszero i then Loc.get_path rhs else None)
in
let offset, size = (Itv.zero, Dom.Val.get_itv length) in
let represents_multiple_values = not (Itv.is_one size) in
let allocsite =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path ~represents_multiple_values
in
if Language.curr_language_is Java then
let internal_arr =
let allocsite =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values
in
Dom.Val.of_java_array_alloc allocsite ~length:size ~traces
in
let arr_loc = Loc.of_allocsite allocsite in
mem
|> Dom.Mem.add_heap arr_loc internal_arr
|> Dom.Mem.add_stack (Loc.of_id id) (Dom.Val.of_pow_loc ~traces (PowLoc.singleton arr_loc))
else
let v = Dom.Val.of_c_array_alloc allocsite ~stride ~offset ~size ~traces in
mem
|> Dom.Mem.add_stack (Loc.of_id id) v
|> BoUtils.Exec.init_c_array_fields model_env path typ (Dom.Val.get_array_locs v) ?dyn_length
and check = check_alloc_size ~can_be_zero size_exp in
{exec; check}
let calloc size_exp stride_exp =
let byte_size_exp = Exp.BinOp (Binop.Mult (Some Typ.size_t), size_exp, stride_exp) in
malloc byte_size_exp
let memcpy dest_exp src_exp size_exp =
let exec _ ~ret:_ mem =
let dest_loc = Sem.eval_locs dest_exp mem in
let v = Dom.Mem.find_set (Sem.eval_locs src_exp mem) mem in
Dom.Mem.update_mem dest_loc v mem
and check {location; integer_type_widths} mem cond_set =
BoUtils.Check.lindex_byte integer_type_widths ~array_exp:dest_exp ~byte_index_exp:size_exp
~last_included:true mem location cond_set
|> BoUtils.Check.lindex_byte integer_type_widths ~array_exp:src_exp ~byte_index_exp:size_exp
~last_included:true mem location
in
{exec; check}
let memset arr_exp size_exp =
let exec _ ~ret:_ mem = mem
and check {location; integer_type_widths} mem cond_set =
BoUtils.Check.lindex_byte integer_type_widths ~array_exp:arr_exp ~byte_index_exp:size_exp
~last_included:true mem location cond_set
in
{exec; check}
let strlen arr_exp =
let exec _ ~ret:(id, _) mem =
let v = Sem.eval_string_len arr_exp mem in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}
let strcpy dest_exp src_exp =
let exec {integer_type_widths} ~ret:(id, _) mem =
let src_loc = Sem.eval_locs src_exp mem in
let dest_loc = Sem.eval_locs dest_exp mem in
mem
|> Dom.Mem.update_mem dest_loc (Dom.Mem.find_set src_loc mem)
|> Dom.Mem.update_mem (PowLoc.of_c_strlen dest_loc) (Dom.Mem.get_c_strlen src_loc mem)
|> Dom.Mem.add_stack (Loc.of_id id) (Sem.eval integer_type_widths dest_exp mem)
and check {integer_type_widths; location} mem cond_set =
let access_last_char =
let idx = Dom.Mem.get_c_strlen (Sem.eval_locs src_exp mem) mem in
let latest_prune = Dom.Mem.get_latest_prune mem in
fun arr cond_set ->
BoUtils.Check.array_access ~arr ~idx ~is_plus:true ~last_included:false ~latest_prune
location cond_set
in
cond_set
|> access_last_char (Sem.eval integer_type_widths dest_exp mem)
|> access_last_char (Sem.eval integer_type_widths src_exp mem)
in
{exec; check}
let strncpy dest_exp src_exp size_exp =
let {exec= memcpy_exec; check= memcpy_check} = memcpy dest_exp src_exp size_exp in
let exec model_env ~ret mem =
let dest_strlen_loc = PowLoc.of_c_strlen (Sem.eval_locs dest_exp mem) in
let strlen = Dom.Mem.find_set (PowLoc.of_c_strlen (Sem.eval_locs src_exp mem)) mem in
mem |> memcpy_exec model_env ~ret |> Dom.Mem.update_mem dest_strlen_loc strlen
in
{exec; check= memcpy_check}
let strcat dest_exp src_exp =
let exec {integer_type_widths} ~ret:(id, _) mem =
let src_loc = Sem.eval_locs src_exp mem in
let dest_loc = Sem.eval_locs dest_exp mem in
let new_contents =
let src_contents = Dom.Mem.find_set src_loc mem in
let dest_contents = Dom.Mem.find_set dest_loc mem in
Dom.Val.join dest_contents src_contents
in
let src_strlen = Dom.Mem.get_c_strlen src_loc mem in
let new_strlen =
let dest_strlen = Dom.Mem.get_c_strlen dest_loc mem in
Dom.Val.plus_a dest_strlen src_strlen
in
mem
|> Dom.Mem.update_mem dest_loc new_contents
|> Dom.Mem.update_mem (PowLoc.of_c_strlen dest_loc) new_strlen
|> Dom.Mem.add_stack (Loc.of_id id) (Sem.eval integer_type_widths dest_exp mem)
and check {integer_type_widths; location} mem cond_set =
let access_last_char arr idx cond_set =
let latest_prune = Dom.Mem.get_latest_prune mem in
BoUtils.Check.array_access ~arr ~idx ~is_plus:true ~last_included:false ~latest_prune location
cond_set
in
let src_strlen =
let str_loc = Sem.eval_locs src_exp mem in
Dom.Mem.get_c_strlen str_loc mem
in
let new_strlen =
let dest_strlen =
let dest_loc = Sem.eval_locs dest_exp mem in
Dom.Mem.get_c_strlen dest_loc mem
in
Dom.Val.plus_a dest_strlen src_strlen
in
cond_set
|> access_last_char (Sem.eval integer_type_widths dest_exp mem) new_strlen
|> access_last_char (Sem.eval integer_type_widths src_exp mem) src_strlen
in
{exec; check}
let realloc src_exp size_exp =
let exec ({location; tenv; integer_type_widths} as model_env) ~ret:(id, _) mem =
let size_exp = Prop.exp_normalize_noabs tenv Predicates.sub_empty size_exp in
let typ, _, length0, dyn_length = get_malloc_info size_exp in
let length = Sem.eval integer_type_widths length0 mem in
let v = Sem.eval integer_type_widths src_exp mem |> Dom.Val.set_array_length location ~length in
let mem = Dom.Mem.add_stack (Loc.of_id id) v mem in
Option.value_map dyn_length ~default:mem ~f:(fun dyn_length ->
let dyn_length = Dom.Val.get_itv (Sem.eval integer_type_widths dyn_length mem) in
BoUtils.Exec.set_dyn_length model_env typ (Dom.Val.get_array_locs v) dyn_length mem )
and check = check_alloc_size ~can_be_zero:false size_exp in
{exec; check}
let placement_new size_exp {exp= src_exp1; typ= t1} src_arg2_opt =
match (t1.Typ.desc, src_arg2_opt) with
| Tint _, None | Tint _, Some {typ= {Typ.desc= Tint _}} ->
malloc ~can_be_zero:true (Exp.BinOp (Binop.PlusA (Some Typ.size_t), size_exp, src_exp1))
| Tstruct (CppClass {name}), None
when [%compare.equal: string list] (QualifiedCppName.to_list name) ["std"; "nothrow_t"] ->
malloc ~can_be_zero:true size_exp
| _, _ ->
let exec {integer_type_widths} ~ret:(id, _) mem =
let src_exp =
if Typ.is_pointer_to_void t1 then src_exp1
else
match src_arg2_opt with
| Some {exp= src_exp2; typ= t2} when Typ.is_pointer_to_void t2 ->
src_exp2
| _ ->
(* TODO: Raise an exception when given unexpected arguments. Before that, we need
to fix the frontend to parse user defined `new` correctly. *)
L.d_error "Unexpected types of arguments for __placement_new" ;
src_exp1
in
let v = Sem.eval integer_type_widths src_exp mem in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}
let strndup src_exp length_exp =
let exec ({pname; node_hash; location; integer_type_widths} as model_env) ~ret:((id, _) as ret)
mem =
let v =
let src_strlen = Dom.Mem.get_c_strlen (Sem.eval_locs src_exp mem) mem in
let length = Sem.eval integer_type_widths length_exp mem in
let size = Itv.incr (Itv.min_sem (Dom.Val.get_itv src_strlen) (Dom.Val.get_itv length)) in
let allocsite =
let represents_multiple_values = not (Itv.is_one size) in
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path:None
~represents_multiple_values
in
let traces =
Trace.Set.join (Dom.Val.get_traces src_strlen) (Dom.Val.get_traces length)
|> Trace.Set.add_elem location Trace.Through
|> Trace.Set.add_elem location ArrayDeclaration
in
Dom.Val.of_c_array_alloc allocsite
~stride:(Some (integer_type_widths.char_width / 8))
~offset:Itv.zero ~size ~traces
in
mem
|> Dom.Mem.add_stack (Loc.of_id id) v
|> (strncpy (Exp.Var id) src_exp length_exp).exec model_env ~ret
in
{exec; check= no_check}
let set_size {integer_type_widths; location} array_v size_exp mem =
let locs = Dom.Val.get_pow_loc array_v in
let length = Sem.eval integer_type_widths size_exp mem in
Dom.Mem.transform_mem ~f:(Dom.Val.set_array_length location ~length) locs mem
let model_by_value value id mem = Dom.Mem.add_stack (Loc.of_id id) value mem
let cast exp size_exp =
let exec {integer_type_widths} ~ret:(ret_id, _) mem =
let v = Sem.eval integer_type_widths exp mem in
let v = match size_exp with Exp.Sizeof {typ} -> Dom.Val.cast typ v | _ -> v in
model_by_value v ret_id mem
in
{exec; check= no_check}
let id exp =
let exec {integer_type_widths} ~ret:(ret_id, _) mem =
let v = Sem.eval integer_type_widths exp mem in
model_by_value v ret_id mem
in
{exec; check= no_check}
let by_value =
let exec ~value _ ~ret:(ret_id, _) mem = model_by_value value ret_id mem in
fun value -> {exec= exec ~value; check= no_check}
let bottom =
let exec _model_env ~ret:_ _mem = Dom.Mem.unreachable in
{exec; check= no_check}
let infer_print e =
let exec {location; integer_type_widths} ~ret:_ mem =
L.(debug BufferOverrun Medium)
"@[<v>=== Infer Print === at %a@,%a@]%!" Location.pp location Dom.Val.pp
(Sem.eval integer_type_widths e mem) ;
mem
in
{exec; check= no_check}
let load_size_alias id arr_locs mem =
match PowLoc.is_singleton_or_more arr_locs with
| IContainer.Singleton loc ->
Dom.Mem.load_size_alias id loc mem
| IContainer.Empty | IContainer.More ->
mem
(* Java only *)
let get_array_length array_exp =
let exec _ ~ret:(ret_id, _) mem =
let arr_locs = Sem.eval_locs array_exp mem in
let result = Sem.eval_array_locs_length arr_locs mem in
model_by_value result ret_id mem |> load_size_alias ret_id arr_locs
in
{exec; check= no_check}
(* Clang only *)
let set_array_length {exp; typ} length_exp =
let exec {pname; node_hash; location; integer_type_widths} ~ret:_ mem =
match (exp, typ) with
| Exp.Lvar array_pvar, {Typ.desc= Typ.Tarray {stride}} ->
let length = Sem.eval integer_type_widths length_exp mem in
let stride = Option.map ~f:IntLit.to_int_exn stride in
let path = Some (Symb.SymbolPath.of_pvar array_pvar) in
let traces = Trace.(Set.add_elem location ArrayDeclaration) (Dom.Val.get_traces length) in
let size = Dom.Val.get_itv length in
let allocsite =
let represents_multiple_values = not (Itv.is_one size) in
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path ~represents_multiple_values
in
let v = Dom.Val.of_c_array_alloc allocsite ~stride ~offset:Itv.zero ~size ~traces in
Dom.Mem.add_stack (Loc.of_pvar array_pvar) v mem
| _ ->
L.(die InternalError) "Unexpected type of first argument for __set_array_length() "
and check = check_alloc_size ~can_be_zero:false length_exp in
{exec; check}
let copy array_v ret_id mem =
let dest_loc = Loc.of_id ret_id |> PowLoc.singleton in
Dom.Mem.update_mem dest_loc array_v mem
(** Returns a fixed-size array with a given length backed by the specified array. *)
let copyOf array_exp length_exp =
let exec ({integer_type_widths} as model) ~ret:(id, _) mem =
let array_v = Sem.eval integer_type_widths array_exp mem in
copy array_v id mem |> set_size model array_v length_exp
in
{exec; check= no_check}
(** Creates a new array with the values from the given array.*)
let create_copy_array src_exp =
let exec {integer_type_widths} ~ret:(id, _) mem =
let array_v = Sem.eval integer_type_widths src_exp mem in
copy array_v id mem
in
{exec; check= no_check}
module StdArray = struct
let constructor _size =
let exec _model_env ~ret:_ mem = mem (* initialize? *) in
{exec; check= no_check}
let at size {exp= array_exp} {exp= index_exp} = at ~size array_exp index_exp
let begin_ _size {exp= array_exp} =
let exec {location; integer_type_widths} ~ret:(id, _) mem =
let v =
Sem.eval integer_type_widths array_exp mem |> Dom.Val.set_array_offset location Itv.zero
in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}
let end_ size {exp= array_exp} =
let exec {location; integer_type_widths} ~ret:(id, _) mem =
let v =
let offset = Itv.of_int_lit (IntLit.of_int64 size) in
Sem.eval integer_type_widths array_exp mem |> Dom.Val.set_array_offset location offset
in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}
let back size {exp= array_exp} =
let exec {location; integer_type_widths} ~ret:(id, _) mem =
let v =
let offset = Itv.of_int_lit (IntLit.of_int64 Int64.(size - one)) in
Sem.eval integer_type_widths array_exp mem |> Dom.Val.set_array_offset location offset
in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}
end
module ArrObjCommon = struct
let deref_of {integer_type_widths} exp ~fn ?fn_typ mem =
let typ = Option.map fn_typ ~f:Typ.mk_ptr in
Dom.Val.get_all_locs (Sem.eval_arr integer_type_widths exp mem) |> PowLoc.append_field ?typ ~fn
let eval_size model_env exp ~fn mem =
Sem.eval_array_locs_length (deref_of model_env exp ~fn mem) mem
let size_exec exp ~fn ?fn_typ ({integer_type_widths} as model_env) ~ret:(id, _) mem =
let locs = Sem.eval integer_type_widths exp mem |> Dom.Val.get_all_locs in
match PowLoc.is_singleton_or_more locs with
| Singleton (BoField.Prim (Loc.Allocsite (Allocsite.LiteralString s))) ->
model_by_value (Dom.Val.of_int (String.length s)) id mem
| _ ->
let arr_locs = deref_of model_env exp ~fn ?fn_typ mem in
let mem = Dom.Mem.add_stack (Loc.of_id id) (Sem.eval_array_locs_length arr_locs mem) mem in
load_size_alias id arr_locs mem
let at arr_exp ~fn ?fn_typ index_exp =
let exec ({pname; location} as model_env) ~ret:(id, typ) mem =
let array_v =
let locs = deref_of model_env arr_exp ~fn ?fn_typ mem in
if PowLoc.is_bot locs then Dom.Val.unknown_from typ ~callee_pname:(Some pname) ~location
else Dom.Mem.find_set locs mem
in
Dom.Mem.add_stack (Loc.of_id id) array_v mem
and check ({location; integer_type_widths} as model_env) mem cond_set =
let idx = Sem.eval integer_type_widths index_exp mem in
let arr = Dom.Mem.find_set (deref_of model_env arr_exp ~fn mem) mem in
let latest_prune = Dom.Mem.get_latest_prune mem in
BoUtils.Check.array_access ~arr ~idx ~is_plus:true ~last_included:false ~latest_prune location
cond_set
in
{exec; check}
let copy_constructor model_env deref_of_tgt ~fn ?fn_typ src_exp mem =
let deref_of_src = deref_of model_env src_exp ~fn ?fn_typ mem in
Dom.Mem.update_mem deref_of_tgt (Dom.Mem.find_set deref_of_src mem) mem
let constructor_from_char_ptr ({integer_type_widths} as model_env) tgt_deref ~fn ?char_typ src mem
=
let elem_locs = PowLoc.append_field ?typ:(Option.map char_typ ~f:Typ.mk_ptr) tgt_deref ~fn in
match src with
| Exp.Const (Const.Cstr s) ->
BoUtils.Exec.decl_string model_env ~do_alloc:true elem_locs s mem
| _ ->
let v = Sem.eval integer_type_widths src mem in
Dom.Mem.update_mem elem_locs v mem
let check_index ~last_included get_array_locs arr_id index_exp {location; integer_type_widths} mem
cond_set =
let arr =
let arr_locs = get_array_locs arr_id mem in
Dom.Mem.find_set arr_locs mem
in
let idx = Sem.eval integer_type_widths index_exp mem in
let latest_prune = Dom.Mem.get_latest_prune mem in
BoUtils.Check.array_access ~arr ~idx ~is_plus:true ~last_included ~latest_prune location
cond_set
end
module StdVector = struct
let append_field loc ~vec_typ ~elt_typ =
Loc.append_field ~typ:(Typ.mk_ptr elt_typ) loc (BufferOverrunField.cpp_vector_elem ~vec_typ)
let append_fields locs ~vec_typ ~elt_typ =
PowLoc.append_field ~typ:(Typ.mk_ptr elt_typ) locs
~fn:(BufferOverrunField.cpp_vector_elem ~vec_typ)
let deref_of model_env elt_typ {exp= vec_exp; typ= vec_typ} mem =
let fn = BufferOverrunField.cpp_vector_elem ~vec_typ in
ArrObjCommon.deref_of model_env vec_exp ~fn ~fn_typ:(Typ.mk_ptr elt_typ) mem
(* The (3) constructor in https://en.cppreference.com/w/cpp/container/vector/vector *)
let constructor_size elt_typ {exp= vec_exp; typ= vec_typ} size_exp =
let {exec= malloc_exec; check} = malloc ~can_be_zero:true size_exp in
let exec ({pname; node_hash; integer_type_widths; location} as model_env) ~ret:((id, _) as ret)
mem =
let mem = malloc_exec model_env ~ret mem in
let vec_locs = Sem.eval_locs vec_exp mem in
let deref_of_vec =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values:false
|> Loc.of_allocsite
in
let array_v =
Sem.eval integer_type_widths (Exp.Var id) mem
|> Dom.Val.add_assign_trace_elem location vec_locs
in
mem
|> Dom.Mem.update_mem vec_locs (Dom.Val.of_loc deref_of_vec)
|> Dom.Mem.add_heap (append_field deref_of_vec ~vec_typ ~elt_typ) array_v
in
{exec; check}
(* The (1) constructor in https://en.cppreference.com/w/cpp/container/vector/vector *)
let constructor_empty elt_typ vec = constructor_size elt_typ vec Exp.zero
(* The (5) constructor in https://en.cppreference.com/w/cpp/container/vector/vector *)
let constructor_copy elt_typ {exp= vec_exp; typ= vec_typ} src_exp =
let exec ({integer_type_widths} as model_env) ~ret:_ mem =
let vec_locs, traces =
let v = Sem.eval integer_type_widths vec_exp mem in
(Dom.Val.get_all_locs v, Dom.Val.get_traces v)
in
let deref_of_vec = append_fields vec_locs ~vec_typ ~elt_typ in
let fn = BufferOverrunField.cpp_vector_elem ~vec_typ in
mem
|> Dom.Mem.update_mem vec_locs (Dom.Val.of_pow_loc ~traces deref_of_vec)
|> ArrObjCommon.copy_constructor model_env deref_of_vec ~fn ~fn_typ:(Typ.mk_ptr elt_typ)
src_exp
in
{exec; check= no_check}
let at elt_typ {exp= vec_exp; typ= vec_typ} index_exp =
ArrObjCommon.at vec_exp
~fn:(BufferOverrunField.cpp_vector_elem ~vec_typ)
~fn_typ:(Typ.mk_ptr elt_typ) index_exp
let set_size {location} locs new_size mem =
Dom.Mem.transform_mem locs mem ~f:(fun v ->
Dom.Val.set_array_length location ~length:new_size v )
let empty elt_typ vec_arg =
let exec model_env ~ret:(id, _) mem =
let deref_of_vec = deref_of model_env elt_typ vec_arg mem in
let array_v = Dom.Mem.find_set deref_of_vec mem in
let traces = Dom.Val.get_traces array_v in
let size = ArrayBlk.get_size (Dom.Val.get_array_blk array_v) in
let empty = Dom.Val.of_itv ~traces (Itv.of_bool (Itv.le_sem size Itv.zero)) in
let mem = model_by_value empty id mem in
match PowLoc.is_singleton_or_more deref_of_vec with
| IContainer.Singleton loc ->
Dom.Mem.load_empty_alias id loc mem
| IContainer.(Empty | More) ->
mem
in
{exec; check= no_check}
let data elt_typ vec_arg =
let exec model_env ~ret:(id, _) mem =
let arr = Dom.Mem.find_set (deref_of model_env elt_typ vec_arg mem) mem in
model_by_value arr id mem
in
{exec; check= no_check}
let push_back elt_typ vec_arg elt_exp =
let exec model_env ~ret:_ mem =
let arr_locs = deref_of model_env elt_typ vec_arg mem in
let mem =
let new_size =
Dom.Val.plus_a (Sem.eval_array_locs_length arr_locs mem) (Dom.Val.of_int 1)
in
set_size model_env arr_locs new_size mem
in
let elt_locs = Dom.Val.get_all_locs (Dom.Mem.find_set arr_locs mem) in
let elt_v = Dom.Mem.find_set (Sem.eval_locs elt_exp mem) mem in
Dom.Mem.update_mem elt_locs elt_v mem
in
{exec; check= no_check}
let size elt_typ {exp= vec_exp; typ= vec_typ} =
let exec =
ArrObjCommon.size_exec vec_exp
~fn:(BufferOverrunField.cpp_vector_elem ~vec_typ)
~fn_typ:(Typ.mk_ptr elt_typ)
in
{exec; check= no_check}
let resize elt_typ vec_arg size_exp =
let exec ({integer_type_widths} as model_env) ~ret:_ mem =
let arr_locs = deref_of model_env elt_typ vec_arg mem in
let new_size = Sem.eval integer_type_widths size_exp mem in
set_size model_env arr_locs new_size mem
in
{exec; check= no_check}
end
module Split = struct
let std_vector model_env ~adds_at_least_one ({typ= vector_typ} as vec_arg) mem =
match vector_typ with
| Typ.
{ desc=
Tptr
( {desc= Tstruct (CppClass {template_spec_info= Template {args= TType elt_typ :: _}})}
, _ ) } ->
let arr_locs = StdVector.deref_of model_env elt_typ vec_arg mem in
let size = if adds_at_least_one then Dom.Val.Itv.pos else Dom.Val.Itv.nat in
StdVector.set_size model_env arr_locs size mem
| _ ->
Logging.d_printfln_escaped "Could not find element type of vector" ;
mem
end
module Boost = struct
module Split = struct
let std_vector vector_arg =
let exec model_env ~ret:_ mem =
Split.std_vector model_env ~adds_at_least_one:true vector_arg mem
in
{exec; check= no_check}
end
end
module Folly = struct
module Split = struct
let std_vector vector_arg ignore_empty_opt =
let exec ({integer_type_widths} as model_env) ~ret:_ mem =
let adds_at_least_one =
match ignore_empty_opt with
| Some ignore_empty_exp ->
Sem.eval integer_type_widths ignore_empty_exp mem |> Dom.Val.get_itv |> Itv.is_false
| None ->
(* default: ignore_empty is false *)
true
in
Split.std_vector model_env ~adds_at_least_one vector_arg mem
in
{exec; check= no_check}
end
end
module StdBasicString = struct
let constructor_from_char_ptr char_typ {exp= tgt_exp; typ= tgt_typ} src ~len_opt =
let exec ({pname; node_hash} as model_env) ~ret mem =
let mem =
Option.value_map len_opt ~default:mem ~f:(fun len ->
let {exec= malloc_exec} = malloc ~can_be_zero:true len in
malloc_exec model_env ~ret mem )
in
let tgt_locs = Sem.eval_locs tgt_exp mem in
let tgt_deref =
let allocsite =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values:false
in
PowLoc.singleton (Loc.of_allocsite allocsite)
in
let mem =
Dom.Mem.update_mem tgt_locs (Dom.Val.of_pow_loc ~traces:Trace.Set.bottom tgt_deref) mem
in
let fn = BufferOverrunField.cpp_vector_elem ~vec_typ:tgt_typ in
ArrObjCommon.constructor_from_char_ptr model_env tgt_deref src ~fn ~char_typ mem
in
let check ({location; integer_type_widths} as model_env) mem cond_set =
Option.value_map len_opt ~default:cond_set ~f:(fun len ->
let {check= malloc_check} = malloc ~can_be_zero:true len in
let cond_set = malloc_check model_env mem cond_set in
BoUtils.Check.lindex integer_type_widths ~array_exp:src ~index_exp:len ~last_included:true
mem location cond_set )
in
{exec; check}
(* The (4) constructor in https://en.cppreference.com/w/cpp/string/basic_string/basic_string *)
let constructor_from_char_ptr_with_len char_typ tgt_arg src len =
constructor_from_char_ptr char_typ tgt_arg src ~len_opt:(Some len)
(* The (5) constructor in https://en.cppreference.com/w/cpp/string/basic_string/basic_string *)
let constructor_from_char_ptr_without_len char_typ tgt_arg src =
constructor_from_char_ptr char_typ tgt_arg src ~len_opt:None
(* The (7) constructor in https://en.cppreference.com/w/cpp/string/basic_string/basic_string *)
let copy_constructor = StdVector.constructor_copy
let empty = StdVector.empty
let length = StdVector.size
end
(** Java's integers are modeled with an indirection to a memory location that holds the actual
integer value *)
module JavaInteger = struct
let intValue exp =
let exec _ ~ret:(id, _) mem =
let powloc = Sem.eval_locs exp mem in
let v = if PowLoc.is_bot powloc then Dom.Val.Itv.top else Dom.Mem.find_set powloc mem in
model_by_value v id mem
in
{exec; check= no_check}
let valueOf exp =
let exec {pname; node_hash; location; integer_type_widths} ~ret:(id, _) mem =
let represents_multiple_values = false in
let int_allocsite =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:0 ~path:None
~represents_multiple_values
in
let v = Sem.eval integer_type_widths exp mem in
let int_loc = Loc.of_allocsite int_allocsite in
mem |> Dom.Mem.add_heap int_loc v
|> Dom.Mem.add_stack (Loc.of_id id)
( int_loc |> PowLoc.singleton
|> Dom.Val.of_pow_loc ~traces:Trace.(Set.singleton location JavaIntDecleration) )
in
{exec; check= no_check}
end
module type Lang = sig
val collection_internal_array_field : Fieldname.t
end
(* Abstract Collections are represented like arrays. But we don't care about the elements.
- when they are constructed, we set the size to 0
- each time we add an element, we increase the length of the array
- each time we delete an element, we decrease the length of the array *)
module AbstractCollection (Lang : Lang) = struct
let create_collection {pname; node_hash; location} ~ret:(id, _) mem ~length =
let represents_multiple_values = true in
let traces = Trace.(Set.singleton location ArrayDeclaration) in
let coll_allocsite =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path:None
~represents_multiple_values
in
let internal_array =
let allocsite =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values
in
Dom.Val.of_java_array_alloc allocsite ~length ~traces
in
let coll_loc = Loc.of_allocsite coll_allocsite in
let internal_array_loc = Loc.append_field coll_loc Lang.collection_internal_array_field in
mem
|> Dom.Mem.add_heap internal_array_loc internal_array
|> Dom.Mem.add_stack (Loc.of_id id) (coll_loc |> PowLoc.singleton |> Dom.Val.of_pow_loc ~traces)
let new_collection =
let exec = create_collection ~length:Itv.zero in
{exec; check= no_check}
let allocate size_exp =
let exec ({integer_type_widths} as env) ~ret mem =
let length = Sem.eval integer_type_widths size_exp mem |> Dom.Val.get_itv in
create_collection env ~ret mem ~length
in
{exec; check= no_check}
let eval_collection_internal_array_locs coll_exp mem =
Sem.eval_locs coll_exp mem |> PowLoc.append_field ~fn:Lang.collection_internal_array_field
let get_collection_internal_array_locs coll_id mem =
let coll = Dom.Mem.find (Loc.of_id coll_id) mem in
Dom.Val.get_all_locs coll |> PowLoc.append_field ~fn:Lang.collection_internal_array_field
let get_collection_internal_elements_locs coll_id mem =
let arr_locs = get_collection_internal_array_locs coll_id mem in
Dom.Mem.find_set arr_locs mem |> Dom.Val.get_all_locs
let eval_collection_length coll_exp mem =
let arr_locs = eval_collection_internal_array_locs coll_exp mem in
Sem.eval_array_locs_length arr_locs mem
let change_size_by ~size_f coll_id {location} ~ret:_ mem =
let arr_locs = get_collection_internal_array_locs coll_id mem in
let mem = Dom.Mem.forget_size_alias arr_locs mem in
Dom.Mem.transform_mem ~f:(Dom.Val.transform_array_length location ~f:size_f) arr_locs mem
let change_size_by_incr coll_id {location} ~ret:_ mem =
let arr_locs = get_collection_internal_array_locs coll_id mem in
Dom.Mem.transform_mem ~f:(Dom.Val.transform_array_length location ~f:Itv.incr) arr_locs mem
|> Dom.Mem.incr_size_alias arr_locs
let change_size_by_incr_or_not coll_id {location} ~ret:_ mem =
let arr_locs = get_collection_internal_array_locs coll_id mem in
Dom.Mem.transform_mem
~f:(Dom.Val.transform_array_length location ~f:(Itv.plus Itv.zero_one))
arr_locs mem
|> Dom.Mem.incr_or_not_size_alias arr_locs
let set_elem_exec {integer_type_widths} coll_id elem_exp mem =
let locs = get_collection_internal_elements_locs coll_id mem in
let v = Sem.eval integer_type_widths elem_exp mem in
Dom.Mem.update_mem locs v mem
let add coll_id elem_exp =
let exec env ~ret mem =
change_size_by_incr coll_id env ~ret mem |> set_elem_exec env coll_id elem_exp
in
{exec; check= no_check}
let of_list list =
let exec env ~ret:((id, _) as ret) mem =
let mem = new_collection.exec env ~ret mem in
List.fold_left list ~init:mem ~f:(fun acc {exp= elem_exp} ->
(add id elem_exp).exec env ~ret acc )
in
{exec; check= no_check}
let singleton_collection =
let exec env ~ret:((id, _) as ret) mem =
let {exec= new_exec; check= _} = new_collection in
let mem = new_exec env ~ret mem in
change_size_by_incr id ~ret env mem
in
{exec; check= no_check}
(** increase the size by [0, 1] because put replaces the value rather than add a new one when the
key is found in the map *)
let put coll_id = {exec= change_size_by_incr_or_not coll_id; check= no_check}
let put_with_elem coll_id elem_exp =
let put_model = put coll_id in
let exec env ~ret mem = put_model.exec env ~ret mem |> set_elem_exec env coll_id elem_exp in
{put_model with exec}
(* The return value is set by [set_itv_updated_by_addition] in order to be sure that it can be
used as a control variable value in the cost checker. *)
let size coll_exp =
let exec _ ~ret:(ret_id, _) mem =
let result = eval_collection_length coll_exp mem |> Dom.Val.set_itv_updated_by_addition in
let mem = model_by_value result ret_id mem in
load_size_alias ret_id (eval_collection_internal_array_locs coll_exp mem) mem
in
{exec; check= no_check}
let iterator coll_exp =
let exec {integer_type_widths} ~ret:(ret_id, _) mem =
let itr = Sem.eval integer_type_widths coll_exp mem in
model_by_value itr ret_id mem |> Dom.Mem.add_iterator_alias ret_id
in
{exec; check= no_check}
let init_with_capacity size_exp =
let exec _ ~ret:_ mem = mem and check = check_alloc_size ~can_be_zero:true size_exp in
{exec; check}
let init_with_arg lhs_id rhs_exp =
let exec {integer_type_widths} ~ret:_ mem =
let itr = Sem.eval integer_type_widths rhs_exp mem in
model_by_value itr lhs_id mem
and check = check_alloc_size ~can_be_zero:true rhs_exp in
{exec; check}
(* The return value is set by [set_itv_updated_by_addition] in order to be sure that it can be
used as a control variable value in the cost checker. *)
let hasNext iterator =
let exec _ ~ret:(ret_id, _) mem =
(* Set the size of the iterator to be [0, size], so that range
will be size of the collection. *)
let collection_size =
let arr_locs = eval_collection_internal_array_locs iterator mem in
Sem.conservative_array_length arr_locs mem
in
model_by_value collection_size ret_id mem
|> Dom.Mem.add_iterator_has_next_alias ret_id iterator
in
{exec; check= no_check}
let next iterator =
let exec {integer_type_widths} ~ret:(id, _) mem =
let traces = Sem.eval integer_type_widths iterator mem |> Dom.Val.get_traces in
let locs = eval_collection_internal_array_locs iterator mem in
model_by_value (Dom.Val.of_pow_loc ~traces locs) id mem
|> Dom.Mem.incr_iterator_offset_alias iterator
in
{exec; check= no_check}
let addAll coll_id coll_to_add =
let exec model_env ~ret mem =
let to_add_length = eval_collection_length coll_to_add mem |> Dom.Val.get_itv in
change_size_by ~size_f:(Itv.plus to_add_length) coll_id model_env ~ret mem
in
{exec; check= no_check}
(** Returns a view of the portion of this list between the specified fromIndex, inclusive, and
toIndex, exclusive. Simply model it as creating a new list with length toIndex - fromIndex. *)
let subList from_exp to_exp =
let exec ({integer_type_widths} as model) ~ret mem =
let from_idx = Sem.eval integer_type_widths from_exp mem in
let to_idx = Sem.eval integer_type_widths to_exp mem in
let length = Itv.minus (Dom.Val.get_itv to_idx) (Dom.Val.get_itv from_idx) in
create_collection model ~ret mem ~length
in
{exec; check= no_check}
(** increase the size by [0, |collection_to_add|] because put replaces the value rather than add a
new one when the key is found in the map *)
let putAll coll_id coll_to_add =
let exec model_env ~ret mem =
let to_add_length =
eval_collection_length coll_to_add mem |> Dom.Val.get_itv |> Itv.set_lb_zero
in
change_size_by ~size_f:(Itv.plus to_add_length) coll_id model_env ~ret mem
in
{exec; check= no_check}
let check_index ~last_included arr_id index_exp =
ArrObjCommon.check_index ~last_included get_collection_internal_array_locs arr_id index_exp
let add_at_index (coll_id : Ident.t) index_exp =
{exec= change_size_by_incr coll_id; check= check_index ~last_included:true coll_id index_exp}
let remove_at_index coll_id index_exp =
{ exec= change_size_by ~size_f:Itv.decr coll_id
; check= check_index ~last_included:false coll_id index_exp }
let addAll_at_index coll_id index_exp coll_to_add =
let exec model_env ~ret mem =
let to_add_length = eval_collection_length coll_to_add mem |> Dom.Val.get_itv in
change_size_by ~size_f:(Itv.plus to_add_length) coll_id model_env ~ret mem
in
{exec; check= check_index ~last_included:true coll_id index_exp}
let set_at_index coll_id index_exp elem_exp =
let exec env ~ret:_ mem = set_elem_exec env coll_id elem_exp mem in
{exec; check= check_index ~last_included:false coll_id index_exp}
let get_any_index coll_id =
let exec _model_env ~ret:(ret_id, _) mem =
let locs = get_collection_internal_elements_locs coll_id mem in
let v = Dom.Mem.find_set locs mem in
model_by_value v ret_id mem
in
{exec; check= no_check}
let get_at_index coll_id index_exp =
let {exec} = get_any_index coll_id in
{exec; check= check_index ~last_included:false coll_id index_exp}
end
module Collection = AbstractCollection (struct
let collection_internal_array_field = BufferOverrunField.java_collection_internal_array
end)
module NSCollection = struct
include AbstractCollection (struct
let collection_internal_array_field = BufferOverrunField.objc_collection_internal_array
end)
let create_collection {pname; node_hash; location; integer_type_widths} ~ret:(coll_id, _) mem
~size_exp =
let represents_multiple_values = true in
let _, stride, length0, _ = get_malloc_info size_exp in
let length = Sem.eval integer_type_widths length0 mem in
let traces = Trace.(Set.add_elem location ArrayDeclaration) (Dom.Val.get_traces length) in
let internal_array =
let allocsite =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values
in
let offset, size = (Itv.zero, Dom.Val.get_itv length) in
Dom.Val.of_c_array_alloc allocsite ~stride ~offset ~size ~traces
in
let coll_allocsite =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path:None
~represents_multiple_values
in
let coll_loc = Loc.of_allocsite coll_allocsite in
let internal_array_loc =
Loc.append_field coll_loc BufferOverrunField.objc_collection_internal_array
in
mem
|> Dom.Mem.add_heap internal_array_loc internal_array
|> Dom.Mem.add_stack (Loc.of_id coll_id)
(coll_loc |> PowLoc.singleton |> Dom.Val.of_pow_loc ~traces)
(** Creates a new array from the given c array by copying the first X elements. *)
let create_from_array src_exp size_exp =
let exec model_env ~ret:((id, _) as ret) mem =
let src_arr_v = Dom.Mem.find_set (Sem.eval_locs src_exp mem) mem in
let mem = create_collection model_env ~ret mem ~size_exp in
let dest_arr_loc = get_collection_internal_elements_locs id mem in
Dom.Mem.update_mem dest_arr_loc src_arr_v mem
and check {location; integer_type_widths} mem cond_set =
BoUtils.Check.lindex integer_type_widths ~array_exp:src_exp ~index_exp:size_exp
~last_included:true mem location cond_set
in
{exec; check}
let new_collection =
let exec = create_collection ~size_exp:Exp.zero in
{exec; check= no_check}
let new_collection_of_size coll_id size_exp =
let exec ({integer_type_widths} as model_env) ~ret:((id, _) as ret) mem =
let coll = Dom.Mem.find_stack (Loc.of_id coll_id) mem in
let to_add_length = Sem.eval integer_type_widths size_exp mem |> Dom.Val.get_itv in
change_size_by ~size_f:(fun _ -> to_add_length) coll_id model_env ~ret mem
|> model_by_value coll id
in
{exec; check= no_check}
let new_collection_by_add_all coll_exp1 coll_exp2 =
let exec model_env ~ret:((ret_id, _) as ret) mem =
create_collection model_env ~ret mem ~size_exp:Exp.zero
|> (addAll ret_id coll_exp1).exec model_env ~ret
|> (addAll ret_id coll_exp2).exec model_env ~ret
in
{exec; check= no_check}
let get_first coll_id = get_at_index coll_id Exp.zero
let remove_last coll_id = {exec= change_size_by ~size_f:Itv.decr coll_id; check= no_check}
let remove_all coll_id =
let exec model_env ~ret mem =
change_size_by ~size_f:(fun _ -> Itv.zero) coll_id model_env ~ret mem
in
{exec; check= no_check}
let of_list list =
let exec env ~ret:((id, _) as ret) mem =
let mem = new_collection.exec env ~ret mem in
List.fold_left list ~init:mem ~f:(fun acc {exp= elem_exp} ->
(add id elem_exp).exec env ~ret acc )
in
{exec; check= no_check}
let copy coll_id src_exp =
let exec model_env ~ret:((id, _) as ret) mem =
let coll = Dom.Mem.find_stack (Loc.of_id coll_id) mem in
let set_length = eval_collection_length src_exp mem |> Dom.Val.get_itv in
change_size_by ~size_f:(fun _ -> set_length) coll_id model_env ~ret mem
|> model_by_value coll id
in
{exec; check= no_check}
let iterator coll_exp =
let exec {integer_type_widths= _; location} ~ret:(id, _) mem =
let elements_locs = eval_collection_internal_array_locs coll_exp mem in
let v = Dom.Mem.find_set elements_locs mem |> Dom.Val.set_array_offset location Itv.zero in
model_by_value v id mem
in
{exec; check= no_check}
let next_object iterator =
let exec _ ~ret:(ret_id, _) mem =
let iterator_locs =
Dom.Mem.find_simple_alias iterator mem
|> List.filter_map ~f:(fun (l, i) -> if IntLit.iszero i then Some l else None)
|> PowLoc.of_list
in
let iterator_v = Dom.Mem.find_set iterator_locs mem in
let arrayblk = ArrayBlk.plus_offset (Dom.Val.get_array_blk iterator_v) Itv.one in
let iterator_v' = {iterator_v with arrayblk} in
let return_v =
(* NOTE: It joins zero to avoid unreachable nodes due to the following hack. The
[nextObject] returns the offset of the enumerator instead of an object elements,
which helps the cost checker select the offset as a control variable, for example,
[while(obj = [enumerator nextObject]) { ... }]. *)
ArrayBlk.get_offset ~cost_mode:true arrayblk |> Itv.join Itv.zero |> Dom.Val.of_itv
in
model_by_value return_v ret_id mem
|> Dom.Mem.add_heap_set iterator_locs iterator_v'
|> Dom.Mem.add_iterator_next_object_alias ~ret_id ~iterator
in
{exec; check= no_check}
end
module NSURL = struct
let get_resource_value =
let exec _model_env ~ret:(id, _) mem = model_by_value (Dom.Val.of_itv Itv.zero_one) id mem in
{exec; check= no_check}
end
module JavaClass = struct
let decl_array {pname; node_hash; location} ~ret:(ret_id, _) length mem =
let loc =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path:None
~represents_multiple_values:true
|> Loc.of_allocsite
in
let arr_v =
let allocsite =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values:true
in
let traces = Trace.(Set.singleton location ArrayDeclaration) in
Dom.Val.of_java_array_alloc allocsite ~length ~traces
in
Dom.Mem.add_heap loc arr_v mem |> model_by_value (Dom.Val.of_loc loc) ret_id
let get_fields class_name_exp =
let exec ({tenv} as model_env) ~ret mem =
match class_name_exp with
| Exp.Const (Const.Cclass name) -> (
let typ_name = Typ.Name.Java.from_string (Ident.name_to_string name) in
match Tenv.lookup tenv typ_name with
| Some {fields} ->
decl_array model_env ~ret (List.length fields |> Itv.of_int) mem
| None ->
Logging.d_printfln_escaped "Could not find class from tenv" ;
mem )
| _ ->
Logging.d_printfln_escaped "Parameter is not a class name constant" ;
mem
in
{exec; check= no_check}
let get_enum_constants class_name_exp =
let exec ({get_summary} as model_env) ~ret mem =
match class_name_exp with
| Exp.Const (Const.Cclass name) -> (
let enum_values_pname =
let class_name_str = Ident.name_to_string name in
let class_name = Typ.JavaClass (JavaClassName.from_string class_name_str) in
Procname.make_java
~class_name:(Typ.Name.Java.from_string class_name_str)
~return_type:(Some Typ.(mk_ptr (mk_array (mk_ptr (mk_struct class_name)))))
~method_name:"values" ~parameters:[] ~kind:Procname.Java.Static ()
in
match get_summary enum_values_pname with
| Some enum_values_mem ->
let length =
let ret_loc = Loc.of_pvar (Pvar.get_ret_pvar enum_values_pname) in
let ret_v = Dom.Mem.find ret_loc enum_values_mem in
Dom.Mem.find_set (Dom.Val.get_all_locs ret_v) enum_values_mem
|> Dom.Val.array_sizeof
in
decl_array model_env ~ret length mem
| None ->
Logging.d_printfln_escaped "Summary of Enum.values not found" ;
mem )
| _ ->
Logging.d_printfln_escaped "Parameter is not a class name constant" ;
mem
in
{exec; check= no_check}
end
module JavaLinkedList = struct
let next {exp; typ} =
let exec _model_env ~ret:(ret_id, _) mem =
match exp with
| Exp.Var id -> (
match Dom.Mem.find_simple_alias id mem with
| [(l, zero)] when IntLit.iszero zero ->
let fn = BufferOverrunField.java_linked_list_next typ in
model_by_value (Loc.append_field l fn |> Dom.Val.of_loc) ret_id mem
| _ ->
mem )
| _ ->
mem
in
{exec; check= no_check}
end
module JavaString = struct
let fn = BufferOverrunField.java_collection_internal_array
let deref_of = ArrObjCommon.deref_of ~fn
let get_char_range s =
let min_max =
String.fold s ~init:None ~f:(fun acc c ->
let i = Char.to_int c in
match acc with None -> Some (i, i) | Some (lb, ub) -> Some (min lb i, max ub i) )
in
match min_max with None -> Itv.bot | Some (min, max) -> Itv.(join (of_int min) (of_int max))
let get_length_and_elem model_env exp mem =
match exp with
| Exp.Const (Const.Cstr s) ->
(Dom.Val.of_int (String.length s), Dom.Val.of_itv (get_char_range s))
| _ ->
let arr_locs = deref_of model_env exp mem in
let length = Sem.eval_array_locs_length arr_locs mem in
let elem =
let arr_locs = Dom.Val.get_all_locs (Dom.Mem.find_set arr_locs mem) in
Dom.Mem.find_set arr_locs mem
in
(length, elem)
let get_length model_env exp mem = get_length_and_elem model_env exp mem |> fst
let concat exp1 exp2 =
let exec ({pname; node_hash} as model_env) ~ret:(id, _) mem =
let length_v, elem =
let length1, elem1 = get_length_and_elem model_env exp1 mem in
let length2, elem2 = get_length_and_elem model_env exp2 mem in
(Dom.Val.plus_a length1 length2, Dom.Val.join elem1 elem2)
in
let length, traces = (Dom.Val.get_itv length_v, Dom.Val.get_traces length_v) in
let arr_loc =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path:None
~represents_multiple_values:false
|> Loc.of_allocsite
in
let elem_alloc =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values:true
in
Dom.Mem.add_stack (Loc.of_id id) (Dom.Val.of_loc arr_loc) mem
|> Dom.Mem.add_heap (Loc.append_field arr_loc fn)
(Dom.Val.of_java_array_alloc elem_alloc ~length ~traces)
|> Dom.Mem.add_heap (Loc.of_allocsite elem_alloc) elem
in
{exec; check= no_check}
let length exp = {exec= ArrObjCommon.size_exec exp ~fn ?fn_typ:None; check= no_check}
(** Given a string of length n, return itv [-1, n_u-1]. *)
let range_itv_mone model_env exp mem =
ArrObjCommon.eval_size model_env exp ~fn mem
|> BufferOverrunDomain.Val.get_itv |> Itv.set_lb_zero |> Itv.decr
(** Given a string of length n, return itv [1, max(1, n_u -1)]. *)
let range_itv_one_max_one_mone v =
let itv_mone = BufferOverrunDomain.Val.get_itv v |> Itv.decr in
let max_length_mone = Itv.max_sem ~use_minmax_bound:true itv_mone (Itv.of_int 1) in
Itv.set_lb Itv.Bound.one max_length_mone
let indexOf exp =
let exec model_env ~ret:(ret_id, _) mem =
(* if not found, indexOf returns -1. *)
let v = range_itv_mone model_env exp mem |> Dom.Val.of_itv in
model_by_value v ret_id mem
in
{exec; check= no_check}
let charAt string_exp idx = ArrObjCommon.at ~fn string_exp idx
let constructor_from_char_ptr model_env tgt_deref src mem =
ArrObjCommon.constructor_from_char_ptr model_env tgt_deref ~fn src mem
let malloc_and_set_length exp ({location} as model_env) ~ret:((id, _) as ret) length mem =
let {exec} = malloc ~can_be_zero:false exp in
let mem = exec model_env ~ret mem in
let underlying_arr_loc = Dom.Mem.find_stack (Loc.of_id id) mem |> Dom.Val.get_pow_loc in
Dom.Mem.transform_mem ~f:(Dom.Val.set_array_length location ~length) underlying_arr_loc mem
(** If the expression does not match any part of the input then the resulting array has just one
element. *)
let split exp =
let exec model_env ~ret mem =
let length =
ArrObjCommon.eval_size model_env exp ~fn mem |> range_itv_one_max_one_mone |> Dom.Val.of_itv
in
malloc_and_set_length exp model_env ~ret length mem
in
{exec; check= no_check}
let split_with_limit limit_exp =
let exec ({integer_type_widths} as model_env) ~ret mem =
let dummy_exp = Exp.zero in
let length =
Sem.eval integer_type_widths dummy_exp mem |> range_itv_one_max_one_mone |> Dom.Val.of_itv
in
malloc_and_set_length limit_exp model_env ~ret length mem
in
{exec; check= no_check}
(* https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#String(java.lang.String) *)
let copy_constructor tgt_exp src_exp =
let exec ({integer_type_widths} as model_env) ~ret:_ mem =
let tgt_deref = Sem.eval integer_type_widths tgt_exp mem |> Dom.Val.get_all_locs in
let elem_locs = PowLoc.append_field tgt_deref ~fn in
match src_exp with
| Exp.Const (Const.Cstr s) ->
BoUtils.Exec.decl_string model_env ~do_alloc:true elem_locs s mem
| _ ->
ArrObjCommon.copy_constructor model_env elem_locs ~fn src_exp mem
in
{exec; check= no_check}
let empty_constructor tgt_exp = copy_constructor tgt_exp (Exp.Const (Const.Cstr ""))
(* We model Enum.name or Class.getCanonicalName as returning an arbitrary constant name, rather
than getting real names. We did this because we couldn't think of any big gains from getting
the real names in terms of analysis precision. *)
let inferbo_constant_string =
let constant_string_val =
Loc.of_allocsite (Allocsite.literal_string "__constant_string") |> Dom.Val.of_loc
in
let exec _model_env ~ret:(ret_id, _) mem = model_by_value constant_string_val ret_id mem in
{exec; check= no_check}
let create_with_length {pname; node_hash; location} ~ret:(id, _) ~length_itv mem =
let arr_loc =
Allocsite.make pname ~node_hash ~inst_num:0 ~dimension:1 ~path:None
~represents_multiple_values:false
|> Loc.of_allocsite
in
let elem_alloc =
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
~represents_multiple_values:true
in
let traces = Trace.(Set.singleton location ArrayDeclaration) in
Dom.Mem.add_stack (Loc.of_id id) (Dom.Val.of_loc arr_loc) mem
|> Dom.Mem.add_heap (Loc.append_field arr_loc fn)
(Dom.Val.of_java_array_alloc elem_alloc ~length:length_itv ~traces)
let substring_no_end exp begin_idx =
let exec ({integer_type_widths} as model_env) ~ret mem =
let begin_itv = Sem.eval integer_type_widths begin_idx mem |> Dom.Val.get_itv in
let end_itv = get_length model_env exp mem |> Dom.Val.get_itv in
let length_itv = Itv.minus end_itv begin_itv in
create_with_length model_env ~ret ~length_itv mem
in
{exec; check= no_check}
let substring begin_idx end_idx =
let exec ({integer_type_widths} as model_env) ~ret mem =
let begin_itv = Sem.eval integer_type_widths begin_idx mem |> Dom.Val.get_itv in
let end_itv = Sem.eval integer_type_widths end_idx mem |> Dom.Val.get_itv in
let length_itv = Itv.minus end_itv begin_itv in
create_with_length model_env ~ret ~length_itv mem
in
{exec; check= no_check}
let create_from_short_arr exp =
let exec ({integer_type_widths} as model_env) ~ret mem =
let mem = create_with_length model_env ~ret ~length_itv:Itv.zero mem in
let deref_of_tgt =
Dom.Mem.find_stack (Loc.of_id (fst ret)) mem
|> Dom.Val.get_pow_loc |> PowLoc.append_field ~fn
in
let src_arr_locs = Dom.Val.get_all_locs (Sem.eval_arr integer_type_widths exp mem) in
Dom.Mem.update_mem deref_of_tgt (Dom.Mem.find_set src_arr_locs mem) mem
in
{exec; check= no_check}
let replace = id
end
module NSString = struct
let fn = JavaString.fn
let create_with_c_string src_exp =
let exec model_env ~ret mem =
let length_itv = Sem.eval_string_len src_exp mem |> Dom.Val.get_itv in
JavaString.create_with_length model_env ~ret ~length_itv mem
in
{exec; check= no_check}
let init_with_string tgt_exp src_exp =
let {exec= copy_exec; check= _} = JavaString.copy_constructor tgt_exp src_exp in
let exec ({integer_type_widths} as model_env) ~ret:((id, _) as ret) mem =
let mem = copy_exec model_env ~ret mem in
let v = Sem.eval integer_type_widths tgt_exp mem in
model_by_value v id mem
in
{exec; check= no_check}
let init_with_c_string_with_len tgt_exp src_exp len_exp =
let exec ({integer_type_widths; location} as model_env) ~ret:(id, _) mem =
let len_v = Sem.eval integer_type_widths len_exp mem in
let tgt_v = Sem.eval integer_type_widths tgt_exp mem in
let tgt_deref = Dom.Val.get_all_locs tgt_v in
let tgt_arr_loc = JavaString.deref_of model_env tgt_exp mem in
ArrObjCommon.constructor_from_char_ptr model_env tgt_deref ~fn src_exp mem
|> Dom.Mem.transform_mem ~f:(Dom.Val.set_array_length location ~length:len_v) tgt_arr_loc
|> model_by_value tgt_v id
in
{exec; check= no_check}
let substring_from_index = JavaString.substring_no_end
let length = JavaString.length
(** For cost analysis *)
let get_length = JavaString.get_length
let concat = JavaString.concat
let split exp =
let exec model_env ~ret:((coll_id, _) as ret) mem =
let to_add_length =
ArrObjCommon.eval_size model_env exp ~fn mem |> JavaString.range_itv_one_max_one_mone
in
NSCollection.create_collection ~size_exp:Exp.zero model_env ~ret mem
|> NSCollection.change_size_by ~size_f:(Itv.plus to_add_length) coll_id model_env ~ret
in
{exec; check= no_check}
let append_string str1_exp str2_exp =
let exec ({location} as model_env) ~ret:_ mem =
let to_add_len = JavaString.get_length model_env str2_exp mem |> Dom.Val.get_itv in
let arr_locs = JavaString.deref_of model_env str1_exp mem in
let mem = Dom.Mem.forget_size_alias arr_locs mem in
Dom.Mem.transform_mem
~f:(Dom.Val.transform_array_length location ~f:(Itv.plus to_add_len))
arr_locs mem
in
{exec; check= no_check}
end
let objc_malloc exp =
let can_be_zero = true in
let exec ({tenv} as model) ~ret mem =
match exp with
| Exp.Sizeof {typ} when PatternMatch.ObjectiveC.implements_collection tenv (Typ.to_string typ)
->
NSCollection.new_collection.exec model ~ret mem
| Exp.Sizeof {typ} when PatternMatch.ObjectiveC.implements "NSString" tenv (Typ.to_string typ)
->
(NSString.create_with_c_string (Exp.Const (Const.Cstr ""))).exec model ~ret mem
| _ ->
(malloc ~can_be_zero exp).exec model ~ret mem
in
{exec; check= check_alloc_size ~can_be_zero exp}
module Preconditions = struct
let check_argument exp =
let exec {integer_type_widths; location} ~ret:_ mem =
Sem.Prune.prune location integer_type_widths exp mem
in
{exec; check= no_check}
end
let unmodifiable _ s =
String.is_prefix ~prefix:"unmodifiable" s
&& List.exists ~f:(fun suffix -> String.is_suffix ~suffix s) ["Set"; "Collection"; "Map"; "List"]
module InputStream = struct
(* https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read(byte[],%20int,%20int) *)
let read exp =
let exec {integer_type_widths} ~ret:(ret_id, _) mem =
let max = Sem.eval integer_type_widths exp mem in
let traces = Dom.Val.get_traces max in
let v = Dom.Val.of_itv ~traces (Itv.set_lb Itv.Bound.mone (Dom.Val.get_itv max)) in
model_by_value v ret_id mem
in
{exec; check= no_check}
end
module File = struct
let list_files exp =
let exec ({integer_type_widths} as model_env) ~ret mem =
let locs = Sem.eval integer_type_widths exp mem |> Dom.Val.get_pow_loc in
match PowLoc.is_singleton_or_more locs with
| Singleton loc ->
Loc.append_field loc BufferOverrunField.java_list_files_length
|> Loc.get_path
|> Option.value_map ~default:mem ~f:(fun path ->
let length = Itv.of_normal_path ~unsigned:true path in
JavaClass.decl_array model_env ~ret length mem )
| Empty | More ->
mem
in
{exec; check= no_check}
end
module FileChannel = struct
(* https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read(byte[],%20int,%20int) *)
let read buf_exp =
let exec {pname; location} ~ret:(ret_id, ret_typ) mem =
let buf_locs = Sem.eval_locs buf_exp mem in
let buf_v = Dom.Mem.find_set buf_locs mem in
let range =
Symb.SymbolPath.of_callsite ~ret_typ (CallSite.make pname location)
|> Bounds.Bound.of_modeled_path Symb.Symbol.make_onevalue
|> Dom.ModeledRange.of_modeled_function pname location
in
let mem = Dom.Mem.add_heap_set buf_locs (Dom.Val.set_modeled_range range buf_v) mem in
let v =
Dom.Val.of_itv (Itv.set_lb Itv.Bound.mone Itv.nat) |> Dom.Val.set_modeled_range range
in
model_by_value v ret_id mem
in
{exec; check= no_check}
end
module Buffer = struct
let get buf_exp =
let exec _ ~ret:(ret_id, _) mem =
let range = Dom.Mem.find_set (Sem.eval_locs buf_exp mem) mem |> Dom.Val.get_modeled_range in
let v = Dom.Val.of_itv Itv.nat |> Dom.Val.set_modeled_range range in
model_by_value v ret_id mem
in
{exec; check= no_check}
end
module InferAnnotation = struct
let assert_get index_exp coll_exp =
match coll_exp with
| Exp.Var coll_id ->
Collection.get_at_index coll_id index_exp
| _ ->
no_model
end
module Call = struct
let dispatch : (Tenv.t, model, unit) ProcnameDispatcher.Call.dispatcher =
let open ProcnameDispatcher.Call in
let int_typ = Typ.mk (Typ.Tint Typ.IInt) in
let char_typ = Typ.mk (Typ.Tint Typ.IChar) in
let short_typ = Typ.mk (Typ.Tint Typ.IUShort) in
let char_ptr = Typ.mk (Typ.Tptr (char_typ, Pk_pointer)) in
let short_array = Typ.mk (Typ.Tptr (Typ.mk_array short_typ, Pk_pointer)) in
let match_builtin builtin _ s = String.equal s (Procname.get_method builtin) in
make_dispatcher
[ (* Clang common models *)
+match_builtin BuiltinDecl.__cast <>$ capt_exp $+ capt_exp $+...$--> cast
; +match_builtin BuiltinDecl.__exit <>--> bottom
; +match_builtin BuiltinDecl.__get_array_length <>$ capt_exp $!--> get_array_length
; +match_builtin BuiltinDecl.objc_cpp_throw <>--> bottom
; +match_builtin BuiltinDecl.__new
<>$ any_arg_of_typ (+PatternMatch.Java.implements_collection)
$+...$--> Collection.new_collection
; +match_builtin BuiltinDecl.__new
<>$ any_arg_of_typ (+PatternMatch.Java.implements_map)
$+...$--> Collection.new_collection
; +match_builtin BuiltinDecl.__new
<>$ any_arg_of_typ (+PatternMatch.Java.implements_org_json "JSONArray")
$+...$--> Collection.new_collection
; +match_builtin BuiltinDecl.__new
<>$ any_arg_of_typ (+PatternMatch.Java.implements_pseudo_collection)
$+...$--> Collection.new_collection
; +match_builtin BuiltinDecl.__new <>$ capt_exp $+...$--> malloc ~can_be_zero:true
; +match_builtin BuiltinDecl.__new_array <>$ capt_exp $+...$--> malloc ~can_be_zero:true
; +match_builtin BuiltinDecl.__placement_new
<>$ capt_exp $+ capt_arg $+? capt_arg $!--> placement_new
; +match_builtin BuiltinDecl.__set_array_length
<>$ capt_arg $+ capt_exp $!--> set_array_length
; -"calloc" <>$ capt_exp $+ capt_exp $!--> calloc ~can_be_zero:false
; -"exit" <>--> bottom
; -"fgetc" <>--> by_value Dom.Val.Itv.m1_255
; -"fgets" <>$ capt_exp $+ capt_exp $+...$--> fgets
; -"infer_print" <>$ capt_exp $!--> infer_print
; -"malloc" <>$ capt_exp $+...$--> malloc ~can_be_zero:false
; -"memcpy" <>$ capt_exp $+ capt_exp $+ capt_exp $+...$--> memcpy
; -"memmove" <>$ capt_exp $+ capt_exp $+ capt_exp $+...$--> memcpy
; -"memset" <>$ capt_exp $+ any_arg $+ capt_exp $!--> memset
; -"realloc" <>$ capt_exp $+ capt_exp $+...$--> realloc
; -"snprintf" <>--> by_value Dom.Val.Itv.nat
; -"strcat" <>$ capt_exp $+ capt_exp $+...$--> strcat
; -"strcpy" <>$ capt_exp $+ capt_exp $+...$--> strcpy
; -"strlen" <>$ capt_exp $!--> strlen
; -"strncpy" <>$ capt_exp $+ capt_exp $+ capt_exp $+...$--> strncpy
; -"strndup" <>$ capt_exp $+ capt_exp $+...$--> strndup
; -"vsnprintf" <>--> by_value Dom.Val.Itv.nat
; (* ObjC models *)
+match_builtin BuiltinDecl.__objc_alloc_no_fail <>$ capt_exp $+...$--> objc_malloc
; -"CFArrayCreate" <>$ any_arg $+ capt_exp $+ capt_exp
$+...$--> NSCollection.create_from_array
; -"CFArrayCreateCopy" <>$ any_arg $+ capt_exp $!--> create_copy_array
; -"CFArrayGetCount" <>$ capt_exp $!--> NSCollection.size
; -"CFArrayGetValueAtIndex" <>$ capt_var_exn $+ capt_exp $!--> NSCollection.get_at_index
; -"CFDictionaryGetCount" <>$ capt_exp $!--> NSCollection.size
; -"MCFArrayGetCount" <>$ capt_exp $!--> NSCollection.size
; +PatternMatch.ObjectiveC.implements "NSObject" &:: "init" <>$ capt_exp $--> id
; +PatternMatch.ObjectiveC.implements "NSObject" &:: "copy" <>$ capt_exp $--> id
; +PatternMatch.ObjectiveC.implements "NSObject" &:: "mutableCopy" <>$ capt_exp $--> id
; +PatternMatch.ObjectiveC.implements "NSArray" &:: "array" <>--> NSCollection.new_collection
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "firstObject" <>$ capt_var_exn $!--> NSCollection.get_first
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "initWithDictionary:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.copy
; +PatternMatch.ObjectiveC.implements "NSSet"
&:: "initWithArray:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.copy
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "initWithArray:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.copy
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "initWithArray:copyItems:" <>$ capt_var_exn $+ capt_exp $+ any_arg
$--> NSCollection.copy
; +PatternMatch.ObjectiveC.implements_collection
&:: "count" <>$ capt_exp $!--> NSCollection.size
; +PatternMatch.ObjectiveC.implements_collection
&:: "objectEnumerator" <>$ capt_exp $--> NSCollection.iterator
; +PatternMatch.ObjectiveC.conforms_to ~protocol:"NSFastEnumeration"
&:: "objectEnumerator" <>$ capt_exp $--> NSCollection.iterator
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "objectAtIndexedSubscript:" <>$ capt_var_exn $+ capt_exp $!--> NSCollection.get_at_index
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "arrayWithObjects:count:" <>$ capt_exp $+ capt_exp $--> NSCollection.create_from_array
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "arrayWithObjects:" &++> NSCollection.of_list
; +PatternMatch.ObjectiveC.implements "NSArray"
&:: "arrayByAddingObjectsFromArray:" <>$ capt_exp $+ capt_exp
$--> NSCollection.new_collection_by_add_all
; +PatternMatch.ObjectiveC.implements "NSEnumerator"
&:: "nextObject" <>$ capt_var_exn $--> NSCollection.next_object
; +PatternMatch.ObjectiveC.implements "NSFileManager"
&:: "contentsOfDirectoryAtURL:includingPropertiesForKeys:options:error:"
&--> NSCollection.new_collection
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "decodeObjectForKey:" $ capt_var_exn $+...$--> NSCollection.get_any_index
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "initWithCapacity:" <>$ capt_var_exn $+ capt_exp
$--> NSCollection.new_collection_of_size
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "addObject:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.add
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "removeLastObject" <>$ capt_var_exn $--> NSCollection.remove_last
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "insertObject:atIndex:" <>$ capt_var_exn $+ any_arg $+ capt_exp
$--> NSCollection.add_at_index
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "removeObjectAtIndex:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.remove_at_index
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "removeAllObjects:" <>$ capt_var_exn $--> NSCollection.remove_all
; +PatternMatch.ObjectiveC.implements "NSMutableArray"
&:: "addObjectsFromArray:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.addAll
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "dictionary" <>--> NSCollection.new_collection
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "dictionaryWithObjects:forKeys:count:" <>$ any_arg $+ capt_exp $+ capt_exp
$--> NSCollection.create_from_array
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "objectForKeyedSubscript:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.get_at_index
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "objectForKey:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.get_at_index
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "allKeys" <>$ capt_exp $--> create_copy_array
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "allValues" <>$ capt_exp $--> create_copy_array
; +PatternMatch.ObjectiveC.implements "NSDictionary"
&:: "keyEnumerator" <>$ capt_exp $--> NSCollection.iterator
; +PatternMatch.ObjectiveC.implements "NSOrderedSet"
&:: "orderedSet" $$--> NSCollection.new_collection
; +PatternMatch.ObjectiveC.implements "NSOrderedSet"
&:: "orderedSetWithArray:" $ capt_exp $--> id
; +PatternMatch.ObjectiveC.implements "NSOrderedSet"
&:: "reverseObjectEnumerator" <>$ capt_exp $--> NSCollection.iterator
; +PatternMatch.ObjectiveC.implements "NSNumber" &:: "numberWithInt:" <>$ capt_exp $--> id
; +PatternMatch.ObjectiveC.implements "NSNumber" &:: "integerValue" <>$ capt_exp $--> id
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "stringWithUTF8String:" <>$ capt_exp $!--> NSString.create_with_c_string
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "length" <>$ capt_exp $--> NSString.length
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "stringByAppendingString:" <>$ capt_exp $+ capt_exp $!--> NSString.concat
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "substringFromIndex:" <>$ capt_exp $+ capt_exp $--> NSString.substring_from_index
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "appendString:" <>$ capt_exp $+ capt_exp $--> NSString.append_string
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "componentsSeparatedByString:" <>$ capt_exp $+ any_arg $--> NSString.split
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "initWithString:" <>$ capt_exp $+ capt_exp $--> NSString.init_with_string
; +PatternMatch.ObjectiveC.implements "NSString"
&:: "initWithBytes:length:encoding:" <>$ capt_exp $+ capt_exp $+ capt_exp $+ any_arg
$!--> NSString.init_with_c_string_with_len
; +PatternMatch.ObjectiveC.implements "NSURL"
&:: "getResourceValue:forKey:error:" &--> NSURL.get_resource_value
; +PatternMatch.ObjectiveC.implements "NSURL" &:: "path" $ capt_exp $--> id
; (* C++ models *)
-"boost" &:: "split"
$ capt_arg_of_typ (-"std" &:: "vector")
$+ any_arg $+ any_arg $+? any_arg $--> Boost.Split.std_vector
; -"folly" &:: "split" $ any_arg $+ any_arg
$+ capt_arg_of_typ (-"std" &:: "vector")
$+? capt_exp $--> Folly.Split.std_vector
; -"std" &:: "__shared_ptr_access" &:: "operator->" $ capt_exp $--> id
; -"std" &:: "array" < any_typ &+ capt_int >:: "array" &--> StdArray.constructor
; -"std" &:: "array" < any_typ &+ capt_int >:: "at" $ capt_arg $+ capt_arg $!--> StdArray.at
; -"std" &:: "array" < any_typ &+ capt_int >:: "back" $ capt_arg $!--> StdArray.back
; -"std" &:: "array" < any_typ &+ capt_int >:: "begin" $ capt_arg $!--> StdArray.begin_
; -"std" &:: "array" < any_typ &+ capt_int >:: "cbegin" $ capt_arg $!--> StdArray.begin_
; -"std" &:: "array" < any_typ &+ capt_int >:: "cend" $ capt_arg $!--> StdArray.end_
; -"std" &:: "array" < any_typ &+ capt_int >:: "end" $ capt_arg $!--> StdArray.end_
; -"std" &:: "array" < any_typ &+ capt_int >:: "front" $ capt_arg $!--> StdArray.begin_
; -"std" &:: "array" < any_typ &+ capt_int >:: "operator[]" $ capt_arg $+ capt_arg
$!--> StdArray.at
; -"std" &:: "array" &::.*--> no_model
; -"std" &:: "basic_string" &:: "compare" &--> by_value Dom.Val.Itv.top
; -"std" &:: "basic_string" < capt_typ &+...>:: "basic_string" $ capt_arg
$+ capt_exp_of_prim_typ char_ptr
$+ capt_exp_of_prim_typ (Typ.mk (Typ.Tint Typ.size_t))
$--> StdBasicString.constructor_from_char_ptr_with_len
; -"std" &:: "basic_string" < capt_typ &+...>:: "basic_string" $ capt_arg
$+ capt_exp_of_prim_typ char_ptr $--> StdBasicString.constructor_from_char_ptr_without_len
; -"std" &:: "basic_string" < capt_typ &+...>:: "basic_string" $ capt_arg
$+ capt_exp_of_typ (-"std" &:: "basic_string")
$--> StdBasicString.copy_constructor
; -"std" &:: "basic_string" < capt_typ &+...>:: "empty" $ capt_arg $--> StdBasicString.empty
; -"std" &:: "basic_string" < capt_typ &+...>:: "length" $ capt_arg $--> StdBasicString.length
; -"std" &:: "basic_string" < capt_typ &+...>:: "size" $ capt_arg $--> StdBasicString.length
; -"std" &:: "basic_string" &::.*--> no_model
; -"std" &:: "operator!=" $ any_arg_of_prim_typ char_ptr
$+ any_arg_of_typ (-"std" &:: "basic_string")
$--> by_value Dom.Val.Itv.unknown_bool
; -"std" &:: "operator!="
$ any_arg_of_typ (-"std" &:: "basic_string")
$+ any_arg_of_prim_typ char_ptr $--> by_value Dom.Val.Itv.unknown_bool
; -"std" &:: "operator!="
$ any_arg_of_typ (-"std" &:: "basic_string")
$+ any_arg_of_typ (-"std" &:: "basic_string")
$--> by_value Dom.Val.Itv.unknown_bool
; -"std" &:: "operator==" $ any_arg_of_prim_typ char_ptr
$+ any_arg_of_typ (-"std" &:: "basic_string")
$--> by_value Dom.Val.Itv.unknown_bool
; -"std" &:: "operator=="
$ any_arg_of_typ (-"std" &:: "basic_string")
$+ any_arg_of_prim_typ char_ptr $--> by_value Dom.Val.Itv.unknown_bool
; -"std" &:: "operator=="
$ any_arg_of_typ (-"std" &:: "basic_string")
$+ any_arg_of_typ (-"std" &:: "basic_string")
$--> by_value Dom.Val.Itv.unknown_bool
; -"std" &:: "shared_ptr" &:: "operator->" $ capt_exp $--> id
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "data" $ capt_arg $--> StdVector.data
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "emplace_back" $ capt_arg $+ capt_exp
$--> StdVector.push_back
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "empty" $ capt_arg $--> StdVector.empty
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "operator[]"
$ capt_arg_of_typ (-"std" &:: "vector")
$+ capt_exp $--> StdVector.at
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "push_back" $ capt_arg $+ capt_exp
$--> StdVector.push_back
; -"std" &:: "vector" < any_typ &+ any_typ >:: "reserve" $ any_arg $+ any_arg $--> no_model
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "resize" $ capt_arg $+ capt_exp
$--> StdVector.resize
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "size" $ capt_arg $--> StdVector.size
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "vector"
$ capt_arg_of_typ (-"std" &:: "vector")
$+ capt_exp_of_prim_typ (Typ.mk (Typ.Tint Typ.size_t))
$+? any_arg $--> StdVector.constructor_size
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "vector"
$ capt_arg_of_typ (-"std" &:: "vector")
$+ capt_exp_of_typ (-"std" &:: "vector")
$+? any_arg $--> StdVector.constructor_copy
; -"std" &:: "vector" < capt_typ &+ any_typ >:: "vector"
$ capt_arg_of_typ (-"std" &:: "vector")
$--> StdVector.constructor_empty
; -"google" &:: "StrLen" <>$ capt_exp $--> strlen
; (* Java models *)
-"java.lang.Object" &:: "clone" <>$ capt_exp $--> id
; +PatternMatch.Java.implements_arrays &:: "asList" <>$ capt_exp $!--> create_copy_array
; +PatternMatch.Java.implements_arrays &:: "copyOf" <>$ capt_exp $+ capt_exp $+...$--> copyOf
; (* model sets and maps as lists *)
+PatternMatch.Java.implements_collection
&:: "<init>" <>$ capt_var_exn
$+ capt_exp_of_typ (+PatternMatch.Java.implements_collection)
$--> Collection.init_with_arg
; +PatternMatch.Java.implements_collection
&:: "<init>" <>$ any_arg $+ capt_exp $--> Collection.init_with_capacity
; +PatternMatch.Java.implements_collection
&:: "add" <>$ capt_var_exn $+ capt_exp $+ any_arg $--> Collection.add_at_index
; +PatternMatch.Java.implements_collection
&:: "add" <>$ capt_var_exn $+ capt_exp $--> Collection.add
; +PatternMatch.Java.implements_collection
&:: "addAll" <>$ capt_var_exn $+ capt_exp $+ capt_exp $--> Collection.addAll_at_index
; +PatternMatch.Java.implements_collection
&:: "addAll" <>$ capt_var_exn $+ capt_exp $--> Collection.addAll
; +PatternMatch.Java.implements_collection
&:: "get" <>$ capt_var_exn $+ capt_exp $--> Collection.get_at_index
; +PatternMatch.Java.implements_collection
&:: "remove" <>$ capt_var_exn $+ capt_exp $--> Collection.remove_at_index
; +PatternMatch.Java.implements_collection
&:: "set" <>$ capt_var_exn $+ capt_exp $+ capt_exp $--> Collection.set_at_index
; +PatternMatch.Java.implements_collection &:: "size" <>$ capt_exp $!--> Collection.size
; +PatternMatch.Java.implements_collection
&:: "toArray" <>$ capt_exp $+...$--> create_copy_array
; +PatternMatch.Java.implements_collections &:: "emptyList" <>--> Collection.new_collection
; +PatternMatch.Java.implements_collections &:: "emptyMap" <>--> Collection.new_collection
; +PatternMatch.Java.implements_collections &:: "emptySet" <>--> Collection.new_collection
; +PatternMatch.Java.implements_collections
&:: "singleton" <>--> Collection.singleton_collection
; +PatternMatch.Java.implements_collections
&:: "singletonList" <>--> Collection.singleton_collection
; +PatternMatch.Java.implements_collections
&:: "singletonList" <>--> Collection.singleton_collection
; +PatternMatch.Java.implements_collections
&:: "singletonMap" <>--> Collection.singleton_collection
; +PatternMatch.Java.implements_collections
&:: "singletonMap" <>--> Collection.singleton_collection
; +PatternMatch.Java.implements_collections
&::+ unmodifiable <>$ capt_exp $--> Collection.iterator
; +PatternMatch.Java.implements_set &:: "of" &++> Collection.of_list
; +PatternMatch.Java.implements_google "common.collect.ImmutableSet"
&:: "of" &++> Collection.of_list
; +PatternMatch.Java.implements_google "common.base.Preconditions"
&:: "checkArgument" <>$ capt_exp $+...$--> Preconditions.check_argument
; +PatternMatch.Java.implements_google "common.base.Preconditions"
&:: "checkNotNull" <>$ capt_exp $+...$--> id
; +PatternMatch.Java.implements_google "common.base.Preconditions"
&:: "checkState" <>$ capt_exp $+...$--> Preconditions.check_argument
; +PatternMatch.Java.implements_infer_annotation "Assertions"
&:: "assertGet" <>$ capt_exp $+ capt_exp $--> InferAnnotation.assert_get
; +PatternMatch.Java.implements_infer_annotation "Assertions"
&:: "assertNotNull" <>$ capt_exp $+...$--> id
; +PatternMatch.Java.implements_infer_annotation "Assertions"
&:: "assumeNotNull" <>$ capt_exp $+...$--> id
; +PatternMatch.Java.implements_infer_annotation "Assertions"
&:: "nullsafeFIXME" <>$ capt_exp $+...$--> id
; +PatternMatch.Java.implements_infer_annotation "Assertions" &::.*--> no_model
; +PatternMatch.Java.implements_io "File" &:: "listFiles" <>$ capt_exp $--> File.list_files
; +PatternMatch.Java.implements_io "InputStream"
&:: "read" <>$ any_arg $+ any_arg $+ any_arg $+ capt_exp $--> InputStream.read
; +PatternMatch.Java.implements_iterator &:: "hasNext" <>$ capt_exp $!--> Collection.hasNext
; +PatternMatch.Java.implements_nio "Buffer" &:: "wrap" <>$ capt_exp $--> create_copy_array
; +PatternMatch.Java.implements_nio "Buffer"
&:: "allocate" <>$ capt_exp $--> Collection.allocate
; +PatternMatch.Java.implements_nio "Buffer"
&:: "hasRemaining" <>$ capt_exp $!--> Collection.hasNext
; +PatternMatch.Java.implements_iterator &:: "next" <>$ capt_exp $!--> Collection.next
; +PatternMatch.Java.implements_lang "CharSequence"
&:: "<init>" <>$ capt_exp $+ capt_exp $--> JavaString.copy_constructor
; +PatternMatch.Java.implements_lang "CharSequence"
&:: "charAt" <>$ capt_exp $+ capt_exp $--> JavaString.charAt
; +PatternMatch.Java.implements_lang "CharSequence"
&:: "equals"
$ any_arg_of_typ (+PatternMatch.Java.implements_lang "CharSequence")
$+ any_arg_of_typ (+PatternMatch.Java.implements_lang "CharSequence")
$--> by_value Dom.Val.Itv.unknown_bool
; +PatternMatch.Java.implements_lang "CharSequence"
&:: "length" <>$ capt_exp $!--> JavaString.length
; +PatternMatch.Java.implements_lang "CharSequence"
&:: "substring" <>$ any_arg $+ capt_exp $+ capt_exp $--> JavaString.substring
; +PatternMatch.Java.implements_lang "Class"
&:: "getCanonicalName" &::.*--> JavaString.inferbo_constant_string
; +PatternMatch.Java.implements_lang "Class"
&:: "getEnumConstants" <>$ capt_exp $--> JavaClass.get_enum_constants
; +PatternMatch.Java.implements_lang "Class"
&:: "getFields" <>$ capt_exp $--> JavaClass.get_fields
; +PatternMatch.Java.implements_lang "Enum"
&:: "name" &::.*--> JavaString.inferbo_constant_string
; +PatternMatch.Java.implements_lang "Integer"
&:: "intValue" <>$ capt_exp $--> JavaInteger.intValue
; +PatternMatch.Java.implements_lang "Integer"
&:: "valueOf" <>$ capt_exp $--> JavaInteger.valueOf
; +PatternMatch.Java.implements_lang "Iterable"
&:: "iterator" <>$ capt_exp $!--> Collection.iterator
; +PatternMatch.Java.implements_lang "Math"
&:: "max" <>$ capt_exp $+ capt_exp
$--> eval_binop ~f:(Itv.max_sem ~use_minmax_bound:true)
; +PatternMatch.Java.implements_lang "Math"
&:: "min" <>$ capt_exp $+ capt_exp
$--> eval_binop ~f:(Itv.min_sem ~use_minmax_bound:true)
; +PatternMatch.Java.implements_lang "String"
&:: "<init>" <>$ capt_exp $--> JavaString.empty_constructor
; +PatternMatch.Java.implements_lang "String"
&:: "concat" <>$ capt_exp $+ capt_exp $+...$--> JavaString.concat
; +PatternMatch.Java.implements_lang "String"
&:: "valueOf" <>$ capt_exp_of_prim_typ short_array $--> JavaString.create_from_short_arr
; +PatternMatch.Java.implements_lang "String"
&:: "indexOf" <>$ capt_exp $+ any_arg $+...$--> JavaString.indexOf
; +PatternMatch.Java.implements_lang "String"
&:: "lastIndexOf" <>$ capt_exp $+ any_arg $+...$--> JavaString.indexOf
; +PatternMatch.Java.implements_lang "String"
&:: "replace" <>$ capt_exp $+ any_arg_of_prim_typ int_typ $+ any_arg_of_prim_typ int_typ
$--> JavaString.replace
; +PatternMatch.Java.implements_lang "String"
&:: "split" <>$ any_arg $+ any_arg $+ capt_exp $--> JavaString.split_with_limit
; +PatternMatch.Java.implements_lang "String"
&:: "split" <>$ capt_exp $+ any_arg $--> JavaString.split
; +PatternMatch.Java.implements_lang "String"
&:: "startsWith"
$ any_arg_of_typ (+PatternMatch.Java.implements_lang "String")
$+ any_arg_of_typ (+PatternMatch.Java.implements_lang "String")
$--> by_value Dom.Val.Itv.unknown_bool
; +PatternMatch.Java.implements_lang "String"
&:: "substring" <>$ capt_exp $+ capt_exp $--> JavaString.substring_no_end
; +PatternMatch.Java.implements_lang "StringBuilder"
&:: "append" <>$ capt_exp $+ capt_exp $+...$--> JavaString.concat
; +PatternMatch.Java.implements_lang "StringBuilder" &:: "toString" <>$ capt_exp $+...$--> id
; +PatternMatch.Java.implements_list
&:: "listIterator" <>$ capt_exp $+...$--> Collection.iterator
; +PatternMatch.Java.implements_list
&:: "subList" <>$ any_arg $+ capt_exp $+ capt_exp $--> Collection.subList
; +PatternMatch.Java.implements_map &:: "entrySet" <>$ capt_exp $!--> Collection.iterator
; +PatternMatch.Java.implements_map &:: "keySet" <>$ capt_exp $!--> Collection.iterator
; +PatternMatch.Java.implements_map &:: "put" <>$ capt_var_exn $+ any_arg $+ capt_exp
$--> Collection.put_with_elem
; +PatternMatch.Java.implements_map &:: "putAll" <>$ capt_var_exn $+ capt_exp
$--> Collection.putAll
; +PatternMatch.Java.implements_map &:: "size" <>$ capt_exp $!--> Collection.size
; +PatternMatch.Java.implements_map &:: "values" <>$ capt_exp $!--> Collection.iterator
; +PatternMatch.Java.implements_nio "Buffer"
&::+ startsWith "get" <>$ capt_exp $+...$--> Buffer.get
; +PatternMatch.Java.implements_nio "Buffer" &:: "duplicate" <>$ capt_exp $+...$--> id
; +PatternMatch.Java.implements_nio "channels.FileChannel"
&:: "read" <>$ any_arg $+ capt_exp $+ any_arg $--> FileChannel.read
; +PatternMatch.Java.implements_org_json "JSONArray"
&:: "<init>" <>$ capt_var_exn
$+ capt_exp_of_typ (+PatternMatch.Java.implements_collection)
$--> Collection.init_with_arg
; +PatternMatch.Java.implements_org_json "JSONArray"
&:: "length" <>$ capt_exp $!--> Collection.size
; +PatternMatch.Java.implements_org_json "JSONArray"
&:: "put" <>$ capt_var_exn $+...$--> Collection.put
; +PatternMatch.Java.implements_pseudo_collection
&:: "put" <>$ capt_var_exn $+ any_arg $+ any_arg $--> Collection.put
; +PatternMatch.Java.implements_pseudo_collection
&:: "size" <>$ capt_exp $!--> Collection.size
; (* Java linked list models *)
+PatternMatch.Java.implements_app_activity
&:: "getParent" <>$ capt_arg $!--> JavaLinkedList.next
; +PatternMatch.Java.implements_app_fragment
&:: "getParentFragment" <>$ capt_arg $!--> JavaLinkedList.next
; +PatternMatch.Java.implements_graphql_story
&:: "getAttachedStory" <>$ capt_arg $!--> JavaLinkedList.next
; +PatternMatch.Java.implements_psi_element
&:: "getParent" <>$ capt_arg $!--> JavaLinkedList.next
; +PatternMatch.Java.implements_view_group
&:: "getParent" <>$ capt_arg $!--> JavaLinkedList.next
; +PatternMatch.Java.implements_view_parent
&:: "getParent" <>$ capt_arg $!--> JavaLinkedList.next ]
end