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.
699 lines
29 KiB
699 lines
29 KiB
(*
|
|
* Copyright (c) 2013 - present Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*)
|
|
|
|
(** Utility methods to support the translation of clang ast constructs into sil instructions. *)
|
|
|
|
open CFrontend_utils
|
|
|
|
module L = Logging
|
|
|
|
(* Extract the element of a singleton list. If the list is not a singleton *)
|
|
(* It stops the computation giving a warning. We use this because we *)
|
|
(* assume in many places that a list is just a singleton. We use the *)
|
|
(* warning if to see which assumption was not correct *)
|
|
let extract_item_from_singleton l warning_string failure_val =
|
|
match l with
|
|
| [item] -> item
|
|
| _ -> Printing.log_err "%s" warning_string; failure_val
|
|
|
|
let dummy_exp = (Sil.exp_minus_one, Sil.Tint Sil.IInt)
|
|
|
|
(* Extract the element of a singleton list. If the list is not a singleton *)
|
|
(* Gives a warning and return -1 as standard value indicating something *)
|
|
(* went wrong. *)
|
|
let extract_exp_from_list el warning_string =
|
|
extract_item_from_singleton el warning_string dummy_exp
|
|
|
|
module Nodes =
|
|
struct
|
|
|
|
let prune_kind b = Cfg.Node.Prune_node(b, Sil.Ik_bexp , ((string_of_bool b)^" Branch"))
|
|
|
|
let is_join_node n =
|
|
match Cfg.Node.get_kind n with
|
|
| Cfg.Node.Join_node -> true
|
|
| _ -> false
|
|
|
|
let is_prune_node n =
|
|
match Cfg.Node.get_kind n with
|
|
| Cfg.Node.Prune_node _ -> true
|
|
| _ -> false
|
|
|
|
let is_true_prune_node n =
|
|
match Cfg.Node.get_kind n with
|
|
| Cfg.Node.Prune_node(true, _, _) -> true
|
|
| _ -> false
|
|
|
|
let create_node node_kind temps instrs loc context =
|
|
let procdesc = CContext.get_procdesc context in
|
|
Cfg.Node.create (CContext.get_cfg context) loc node_kind instrs procdesc temps
|
|
|
|
let create_prune_node branch e_cond ids_cond instrs_cond loc ik context =
|
|
let (e_cond', _) = extract_exp_from_list e_cond
|
|
"\nWARNING: Missing expression for Conditional operator. Need to be fixed" in
|
|
let e_cond'' =
|
|
if branch then
|
|
Sil.BinOp(Sil.Ne, e_cond', Sil.exp_zero)
|
|
else
|
|
Sil.BinOp(Sil.Eq, e_cond', Sil.exp_zero) in
|
|
let instrs_cond'= instrs_cond @ [Sil.Prune(e_cond'', loc, branch, ik)] in
|
|
create_node (prune_kind branch) ids_cond instrs_cond' loc context
|
|
|
|
(** Check if this binary opertor requires the creation of a node in the cfg. *)
|
|
let is_binary_assign_op boi =
|
|
match boi.Clang_ast_t.boi_kind with
|
|
| `Assign | `MulAssign | `DivAssign | `RemAssign | `AddAssign | `SubAssign
|
|
| `ShlAssign | `ShrAssign | `AndAssign | `XorAssign | `OrAssign -> true
|
|
| `PtrMemD | `PtrMemI | `Mul | `Div | `Rem | `Add | `Sub | `Shl | `Shr
|
|
| `LT | `GT | `LE | `GE | `EQ | `NE | `And | `Xor | `Or | `LAnd | `LOr
|
|
| `Comma -> false
|
|
|
|
(** Check if this unary opertor requires the creation of a node in the cfg. *)
|
|
let need_unary_op_node uoi =
|
|
match uoi.Clang_ast_t.uoi_kind with
|
|
| `PostInc | `PostDec | `PreInc | `PreDec | `AddrOf | `Deref | `Plus -> true
|
|
| `Minus | `Not | `LNot | `Real | `Imag | `Extension | `Coawait -> false
|
|
|
|
end
|
|
|
|
type str_node_map = (string, Cfg.Node.t) Hashtbl.t
|
|
|
|
module GotoLabel =
|
|
struct
|
|
|
|
(* stores goto labels local to a function, with the relative node in the cfg *)
|
|
let goto_label_node_map : str_node_map = Hashtbl.create 17
|
|
|
|
let reset_all_labels () = Hashtbl.reset goto_label_node_map
|
|
|
|
let find_goto_label context label sil_loc =
|
|
try
|
|
Hashtbl.find goto_label_node_map label
|
|
with Not_found ->
|
|
let node_name = Format.sprintf "GotoLabel_%s" label in
|
|
let new_node = Nodes.create_node (Cfg.Node.Skip_node node_name) [] [] sil_loc context in
|
|
Hashtbl.add goto_label_node_map label new_node;
|
|
new_node
|
|
end
|
|
|
|
type continuation = {
|
|
break: Cfg.Node.t list;
|
|
continue: Cfg.Node.t list;
|
|
return_temp : bool; (* true if temps should not be removed in the node but returned to ancestors *)
|
|
}
|
|
|
|
let is_return_temp continuation =
|
|
match continuation with
|
|
| Some cont -> cont.return_temp
|
|
| _ -> false
|
|
|
|
let ids_to_parent cont ids =
|
|
if is_return_temp cont then ids else []
|
|
|
|
let ids_to_node cont ids =
|
|
if is_return_temp cont then [] else ids
|
|
|
|
let mk_cond_continuation cont =
|
|
match cont with
|
|
| Some cont' -> Some { cont' with return_temp = true; }
|
|
| None -> Some { break =[]; continue =[]; return_temp = true;}
|
|
|
|
type priority_node =
|
|
| Free
|
|
| Busy of Clang_ast_t.pointer
|
|
|
|
(* A translation state. It provides the translation function with the info*)
|
|
(* it need to carry on the tranlsation. *)
|
|
type trans_state = {
|
|
context: CContext.t; (* current context of the translation *)
|
|
succ_nodes: Cfg.Node.t list; (* successor nodes in the cfg *)
|
|
continuation: continuation option; (* current continuation *)
|
|
priority: priority_node;
|
|
var_exp_typ: (Sil.exp * Sil.typ) option;
|
|
}
|
|
|
|
(* A translation result. It is returned by the translation function. *)
|
|
type trans_result = {
|
|
root_nodes: Cfg.Node.t list; (* Top cfg nodes (root) created by the translation *)
|
|
leaf_nodes: Cfg.Node.t list; (* Bottom cfg nodes (leaf) created by the translate *)
|
|
ids: Ident.t list; (* list of temp identifiers created that need to be removed by the caller *)
|
|
instrs: Sil.instr list; (* list of SIL instruction that need to be placed in cfg nodes of the parent*)
|
|
exps: (Sil.exp * Sil.typ) list; (* SIL expressions resulting from the translation of the clang stmt *)
|
|
initd_exps: Sil.exp list;
|
|
is_cpp_call_virtual : bool;
|
|
}
|
|
|
|
(* Empty result translation *)
|
|
let empty_res_trans = {
|
|
root_nodes = [];
|
|
leaf_nodes = [];
|
|
ids = [];
|
|
instrs = [];
|
|
exps = [];
|
|
initd_exps = [];
|
|
is_cpp_call_virtual = false;
|
|
}
|
|
|
|
(** Collect the results of translating a list of instructions, and link up the nodes created. *)
|
|
let collect_res_trans l =
|
|
let rec collect l rt =
|
|
match l with
|
|
| [] -> rt
|
|
| rt':: l' ->
|
|
let root_nodes =
|
|
if rt.root_nodes <> [] then rt.root_nodes
|
|
else rt'.root_nodes in
|
|
let leaf_nodes =
|
|
if rt'.leaf_nodes <> [] then rt'.leaf_nodes
|
|
else rt.leaf_nodes in
|
|
if rt'.root_nodes <> [] then
|
|
IList.iter (fun n -> Cfg.Node.set_succs_exn n rt'.root_nodes []) rt.leaf_nodes;
|
|
collect l'
|
|
{ root_nodes = root_nodes;
|
|
leaf_nodes = leaf_nodes;
|
|
ids = rt.ids@rt'.ids;
|
|
instrs = rt.instrs@rt'.instrs;
|
|
exps = rt.exps@rt'.exps;
|
|
initd_exps = rt.initd_exps@rt'.initd_exps;
|
|
is_cpp_call_virtual = false; } in
|
|
collect l empty_res_trans
|
|
|
|
let extract_var_exp_of_fail transt_state =
|
|
match transt_state.var_exp_typ with
|
|
| Some var_exp_typ -> var_exp_typ
|
|
| None -> assert false
|
|
|
|
(* priority_node is used to enforce some kind of policy for creating nodes *)
|
|
(* in the cfg. Certain elements of the AST _must_ create nodes therefore *)
|
|
(* there is no need for them to use priority_node. Certain elements *)
|
|
(* instead need or need not to create a node depending of certain factors. *)
|
|
(* When an element of the latter kind wants to create a node it must claim *)
|
|
(* priority first (like taking a lock). priority can be claimes only when *)
|
|
(* it is free. If an element of AST succedes in claiming priority its id *)
|
|
(* (pointer) is recorded in priority. After an element has finished it *)
|
|
(* frees the priority. In general an AST element E checks if an ancestor *)
|
|
(* has claimed priority. If priority is already claimed E does not have to *)
|
|
(* create a node. If priority is free then it means E has to create the *)
|
|
(* node. Then E claims priority and release it afterward. *)
|
|
module PriorityNode =
|
|
struct
|
|
|
|
type t = priority_node
|
|
|
|
let try_claim_priority_node trans_state stmt_info =
|
|
match trans_state.priority with
|
|
| Free ->
|
|
Printing.log_out "Priority is free. Locking priority node in %d\n@."
|
|
stmt_info.Clang_ast_t.si_pointer;
|
|
{ trans_state with priority = Busy stmt_info.Clang_ast_t.si_pointer }
|
|
| _ ->
|
|
Printing.log_out "Priority busy in %d. No claim possible\n@."
|
|
stmt_info.Clang_ast_t.si_pointer;
|
|
trans_state
|
|
|
|
let force_claim_priority_node trans_state stmt_info =
|
|
{ trans_state with priority = Busy stmt_info.Clang_ast_t.si_pointer }
|
|
|
|
|
|
let is_priority_free trans_state =
|
|
match trans_state.priority with
|
|
| Free -> true
|
|
| _ -> false
|
|
|
|
let own_priority_node pri stmt_info =
|
|
match pri with
|
|
| Busy p when p = stmt_info.Clang_ast_t.si_pointer -> true
|
|
| _ -> false
|
|
|
|
(* Used by translation functions to handle potenatial cfg nodes. *)
|
|
(* It connects nodes returned by translation of stmt children and *)
|
|
(* deals with creating or not a cfg node depending of owning the *)
|
|
(* priority_node. It returns nodes, ids, instrs that should be passed to parent *)
|
|
let compute_results_to_parent trans_state loc nd_name stmt_info res_states_children =
|
|
let res_state = collect_res_trans res_states_children in
|
|
let create_node = own_priority_node trans_state.priority stmt_info && res_state.instrs <> [] in
|
|
if create_node then
|
|
(* We need to create a node *)
|
|
let ids_node = ids_to_node trans_state.continuation res_state.ids in
|
|
let ids_parent = ids_to_parent trans_state.continuation res_state.ids in
|
|
let node_kind = Cfg.Node.Stmt_node (nd_name) in
|
|
let node = Nodes.create_node node_kind ids_node res_state.instrs loc trans_state.context in
|
|
Cfg.Node.set_succs_exn node trans_state.succ_nodes [];
|
|
IList.iter (fun leaf -> Cfg.Node.set_succs_exn leaf [node] []) res_state.leaf_nodes;
|
|
(* Invariant: if root_nodes is empty then the params have not created a node.*)
|
|
let root_nodes = (if res_state.root_nodes <> [] then res_state.root_nodes
|
|
else [node]) in
|
|
{ res_state with
|
|
root_nodes = root_nodes;
|
|
leaf_nodes = [node];
|
|
ids = ids_parent;
|
|
instrs = [];
|
|
exps = [];
|
|
}
|
|
else
|
|
(* The node is created by the parent. We just pass back nodes/leafs params *)
|
|
{ res_state with exps = []}
|
|
|
|
end
|
|
|
|
module Loops =
|
|
struct
|
|
|
|
type loop_kind =
|
|
| For of Clang_ast_t.stmt * Clang_ast_t.stmt * Clang_ast_t.stmt * Clang_ast_t.stmt * Clang_ast_t.stmt
|
|
(* init, decl_stmt, condition, increment and body *)
|
|
| While of Clang_ast_t.stmt option * Clang_ast_t.stmt * Clang_ast_t.stmt
|
|
(* decl_stmt, condition and body *)
|
|
| DoWhile of Clang_ast_t.stmt * Clang_ast_t.stmt (* condition and body *)
|
|
|
|
let loop_kind_to_if_kind loop_kind =
|
|
match loop_kind with
|
|
| For _ -> Sil.Ik_for
|
|
| While _ -> Sil.Ik_while
|
|
| DoWhile _ -> Sil.Ik_dowhile
|
|
|
|
let get_body loop_kind =
|
|
match loop_kind with
|
|
| For (_, _, _, _, body) | While (_, _, body) | DoWhile (_, body) -> body
|
|
|
|
let get_cond loop_kind =
|
|
match loop_kind with
|
|
| For (_, _, cond, _, _) | While (_, cond, _) | DoWhile (cond, _) -> cond
|
|
end
|
|
|
|
(** This function handles ObjC new/alloc and C++ new calls *)
|
|
let create_alloc_instrs context sil_loc function_type fname =
|
|
let function_type, function_type_np =
|
|
match function_type with
|
|
| Sil.Tptr (styp, Sil.Pk_pointer)
|
|
| Sil.Tptr (styp, Sil.Pk_objc_weak)
|
|
| Sil.Tptr (styp, Sil.Pk_objc_unsafe_unretained)
|
|
| Sil.Tptr (styp, Sil.Pk_objc_autoreleasing) ->
|
|
function_type, styp
|
|
| _ -> Sil.Tptr (function_type, Sil.Pk_pointer), function_type in
|
|
let function_type_np = CTypes.expand_structured_type context.CContext.tenv function_type_np in
|
|
let sizeof_exp = Sil.Sizeof (function_type_np, Sil.Subtype.exact) in
|
|
let exp = (sizeof_exp, Sil.Tint Sil.IULong) in
|
|
let ret_id = Ident.create_fresh Ident.knormal in
|
|
let stmt_call = Sil.Call([ret_id], (Sil.Const (Sil.Cfun fname)), [exp], sil_loc, Sil.cf_default) in
|
|
(function_type, ret_id, stmt_call, Sil.Var ret_id)
|
|
|
|
let alloc_trans trans_state loc stmt_info function_type is_cf_non_null_alloc =
|
|
let fname = if is_cf_non_null_alloc then
|
|
SymExec.ModelBuiltins.__objc_alloc_no_fail
|
|
else
|
|
SymExec.ModelBuiltins.__objc_alloc in
|
|
let (function_type, ret_id, stmt_call, exp) = create_alloc_instrs trans_state.context loc function_type fname in
|
|
let res_trans_tmp = { empty_res_trans with ids =[ret_id]; instrs =[stmt_call]} in
|
|
let res_trans =
|
|
let nname = "Call alloc" in
|
|
PriorityNode.compute_results_to_parent trans_state loc nname stmt_info [res_trans_tmp] in
|
|
{ res_trans with exps =[(exp, function_type)]}
|
|
|
|
let objc_new_trans trans_state loc stmt_info cls_name function_type =
|
|
let fname = SymExec.ModelBuiltins.__objc_alloc_no_fail in
|
|
let (alloc_ret_type, alloc_ret_id, alloc_stmt_call, _) =
|
|
create_alloc_instrs trans_state.context loc function_type fname in
|
|
let init_ret_id = Ident.create_fresh Ident.knormal in
|
|
let is_instance = true in
|
|
let call_flags = { Sil.cf_default with Sil.cf_virtual = is_instance; } in
|
|
let pname = General_utils.mk_procname_from_objc_method cls_name CFrontend_config.init Procname.Instance_objc_method in
|
|
CMethod_trans.create_external_procdesc trans_state.context.CContext.cfg pname is_instance None;
|
|
let args = [(Sil.Var alloc_ret_id, alloc_ret_type)] in
|
|
let init_stmt_call = Sil.Call([init_ret_id], (Sil.Const (Sil.Cfun pname)), args, loc, call_flags) in
|
|
let instrs = [alloc_stmt_call; init_stmt_call] in
|
|
let ids = [alloc_ret_id; init_ret_id] in
|
|
let res_trans_tmp = { empty_res_trans with ids = ids; instrs = instrs } in
|
|
let res_trans =
|
|
let nname = "Call objC new" in
|
|
PriorityNode.compute_results_to_parent trans_state loc nname stmt_info [res_trans_tmp] in
|
|
{ res_trans with exps = [(Sil.Var init_ret_id, alloc_ret_type)]}
|
|
|
|
let new_or_alloc_trans trans_state loc stmt_info type_ptr class_name_opt selector =
|
|
let tenv = trans_state.context.CContext.tenv in
|
|
let function_type = CTypes_decl.type_ptr_to_sil_type tenv type_ptr in
|
|
let class_name =
|
|
match class_name_opt with
|
|
| Some class_name -> class_name
|
|
| None -> CTypes.classname_of_type function_type in
|
|
if selector = CFrontend_config.alloc then
|
|
alloc_trans trans_state loc stmt_info function_type true
|
|
else if selector = CFrontend_config.new_str then
|
|
objc_new_trans trans_state loc stmt_info class_name function_type
|
|
else assert false
|
|
|
|
let cpp_new_trans trans_state sil_loc stmt_info function_type =
|
|
let fname = SymExec.ModelBuiltins.__new in
|
|
let (function_type, ret_id, stmt_call, exp) = create_alloc_instrs trans_state.context sil_loc function_type fname in
|
|
let res_trans_tmp = { empty_res_trans with ids =[ret_id]; instrs =[stmt_call]} in
|
|
let res_trans =
|
|
let nname = "Call C++ new" in
|
|
PriorityNode.compute_results_to_parent trans_state sil_loc nname stmt_info [res_trans_tmp] in
|
|
{ res_trans with exps = [(exp, function_type)] }
|
|
|
|
let create_cast_instrs context exp cast_from_typ cast_to_typ sil_loc =
|
|
let ret_id = Ident.create_fresh Ident.knormal in
|
|
let typ = CTypes.remove_pointer_to_typ cast_to_typ in
|
|
let cast_typ_no_pointer = CTypes.expand_structured_type context.CContext.tenv typ in
|
|
let sizeof_exp = Sil.Sizeof (cast_typ_no_pointer, Sil.Subtype.exact) in
|
|
let pname = SymExec.ModelBuiltins.__objc_cast in
|
|
let args = [(exp, cast_from_typ); (sizeof_exp, Sil.Tint Sil.IULong)] in
|
|
let stmt_call = Sil.Call([ret_id], (Sil.Const (Sil.Cfun pname)), args, sil_loc, Sil.cf_default) in
|
|
(ret_id, stmt_call, Sil.Var ret_id)
|
|
|
|
let cast_trans context exps sil_loc callee_pname_opt function_type =
|
|
if CTrans_models.is_toll_free_bridging callee_pname_opt then
|
|
match exps with
|
|
| [exp, typ] ->
|
|
Some (create_cast_instrs context exp typ function_type sil_loc)
|
|
| _ -> assert false
|
|
else None
|
|
|
|
let builtin_trans trans_state loc stmt_info function_type callee_pname_opt =
|
|
if CTrans_models.is_cf_non_null_alloc callee_pname_opt ||
|
|
CTrans_models.is_alloc_model function_type callee_pname_opt then
|
|
Some (alloc_trans trans_state loc stmt_info function_type true)
|
|
else if CTrans_models.is_alloc callee_pname_opt then
|
|
Some (alloc_trans trans_state loc stmt_info function_type false)
|
|
else None
|
|
|
|
let dereference_var_sil (exp, typ) sil_loc =
|
|
let id = Ident.create_fresh Ident.knormal in
|
|
let sil_instr = Sil.Letderef (id, exp, typ, sil_loc) in
|
|
([id], [sil_instr], Sil.Var id)
|
|
|
|
(** Given trans_result with ONE expression, create temporary variable with *)
|
|
(** value of an expression assigned to it *)
|
|
let dereference_value_from_result sil_loc trans_result ~strip_pointer =
|
|
let (obj_sil, class_typ) = extract_exp_from_list trans_result.exps "" in
|
|
let cast_ids, cast_inst, cast_exp = dereference_var_sil (obj_sil, class_typ) sil_loc in
|
|
let typ_no_ptr = match class_typ with | Sil.Tptr (typ, _) -> typ | _ -> assert false in
|
|
let cast_typ = if strip_pointer then typ_no_ptr else class_typ in
|
|
{ trans_result with
|
|
ids = trans_result.ids @ cast_ids;
|
|
instrs = trans_result.instrs @ cast_inst;
|
|
exps = [(cast_exp, cast_typ)]
|
|
}
|
|
|
|
|
|
let cast_operation context cast_kind exps cast_typ sil_loc is_objc_bridged =
|
|
let (exp, typ) = extract_exp_from_list exps "" in
|
|
let exp_typ = match cast_kind with
|
|
| `UncheckedDerivedToBase | `DerivedToBase -> typ (* These casts ignore change of type *)
|
|
| _ -> cast_typ (* by default use the return type of cast expr *) in
|
|
if is_objc_bridged then
|
|
let id, instr, exp = create_cast_instrs context exp typ cast_typ sil_loc in
|
|
[id], [instr], (exp, exp_typ)
|
|
else
|
|
match cast_kind with
|
|
| `NoOp
|
|
| `BitCast
|
|
| `IntegralCast
|
|
| `DerivedToBase
|
|
| `UncheckedDerivedToBase
|
|
| `IntegralToBoolean -> (* This is treated as a nop by returning the same expressions exps*)
|
|
([], [], (exp, exp_typ))
|
|
| `LValueToRValue ->
|
|
(* Takes an LValue and allow it to use it as RValue. *)
|
|
(* So we assign the LValue to a temp and we pass it to the parent.*)
|
|
let ids, instrs, deref_exp = dereference_var_sil (exp, typ) sil_loc in
|
|
ids, instrs, (deref_exp, exp_typ)
|
|
| `CPointerToObjCPointerCast ->
|
|
[], [], (Sil.Cast(typ, exp), exp_typ)
|
|
| _ ->
|
|
Printing.log_err
|
|
"\nWARNING: Missing translation for Cast Kind %s. The construct has been ignored...\n"
|
|
(Clang_ast_j.string_of_cast_kind cast_kind);
|
|
([],[], (exp, exp_typ))
|
|
|
|
let trans_assertion_failure sil_loc context =
|
|
let assert_fail_builtin = Sil.Const (Sil.Cfun SymExec.ModelBuiltins.__infer_fail) in
|
|
let args = [Sil.Const (Sil.Cstr Config.default_failure_name), Sil.Tvoid] in
|
|
let call_instr = Sil.Call ([], assert_fail_builtin, args, sil_loc, Sil.cf_default) in
|
|
let exit_node = Cfg.Procdesc.get_exit_node (CContext.get_procdesc context)
|
|
and failure_node =
|
|
Nodes.create_node (Cfg.Node.Stmt_node "Assertion failure") [] [call_instr] sil_loc context in
|
|
Cfg.Node.set_succs_exn failure_node [exit_node] [];
|
|
{ empty_res_trans with root_nodes = [failure_node]; }
|
|
|
|
let trans_assume_false sil_loc context succ_nodes =
|
|
let instrs_cond = [Sil.Prune (Sil.exp_zero, sil_loc, true, Sil.Ik_land_lor)] in
|
|
let prune_node = Nodes.create_node (Nodes.prune_kind true) [] instrs_cond sil_loc context in
|
|
Cfg.Node.set_succs_exn prune_node succ_nodes [];
|
|
{ empty_res_trans with root_nodes = [prune_node]; leaf_nodes = [prune_node] }
|
|
|
|
let define_condition_side_effects e_cond instrs_cond sil_loc =
|
|
let (e', typ) = extract_exp_from_list e_cond "\nWARNING: Missing expression in IfStmt. Need to be fixed\n" in
|
|
match e' with
|
|
| Sil.Lvar pvar ->
|
|
let id = Ident.create_fresh Ident.knormal in
|
|
[(Sil.Var id, typ)],
|
|
[Sil.Letderef (id, Sil.Lvar pvar, typ, sil_loc)]
|
|
| _ -> [(e', typ)], instrs_cond
|
|
|
|
let fix_param_exps_mismatch params_stmt exps_param =
|
|
let diff = IList.length params_stmt - IList.length exps_param in
|
|
let args = if diff >0 then Array.make diff dummy_exp
|
|
else assert false in
|
|
let exps'= exps_param @ (Array.to_list args) in
|
|
exps'
|
|
|
|
let is_superinstance mei =
|
|
match mei.Clang_ast_t.omei_receiver_kind with
|
|
| `SuperInstance -> true
|
|
| _ -> false
|
|
|
|
let get_selector_receiver obj_c_message_expr_info =
|
|
obj_c_message_expr_info.Clang_ast_t.omei_selector, obj_c_message_expr_info.Clang_ast_t.omei_receiver_kind
|
|
|
|
let is_member_exp stmt =
|
|
match stmt with
|
|
| Clang_ast_t.MemberExpr _ -> true
|
|
| _ -> false
|
|
|
|
let is_enumeration_constant stmt =
|
|
match stmt with
|
|
| Clang_ast_t.DeclRefExpr(_, _, _, drei) ->
|
|
(match drei.Clang_ast_t.drti_decl_ref with
|
|
| Some d -> (match d.Clang_ast_t.dr_kind with
|
|
| `EnumConstant -> true
|
|
| _ -> false)
|
|
| _ -> false)
|
|
| _ -> false
|
|
|
|
let is_null_stmt s =
|
|
match s with
|
|
| Clang_ast_t.NullStmt _ -> true
|
|
| _ -> false
|
|
|
|
let extract_stmt_from_singleton stmt_list warning_string =
|
|
extract_item_from_singleton stmt_list warning_string (Ast_expressions.dummy_stmt ())
|
|
|
|
let rec get_type_from_exp_stmt stmt =
|
|
let do_decl_ref_exp i =
|
|
match i.Clang_ast_t.drti_decl_ref with
|
|
| Some d -> (match d.Clang_ast_t.dr_type_ptr with
|
|
| Some n -> n
|
|
| _ -> assert false )
|
|
| _ -> assert false in
|
|
let open Clang_ast_t in
|
|
match stmt with
|
|
| CXXOperatorCallExpr(_, _, ei)
|
|
| CallExpr(_, _, ei) -> ei.Clang_ast_t.ei_type_ptr
|
|
| MemberExpr (_, _, ei, _) -> ei.Clang_ast_t.ei_type_ptr
|
|
| ParenExpr (_, _, ei) -> ei.Clang_ast_t.ei_type_ptr
|
|
| ArraySubscriptExpr(_, _, ei) -> ei.Clang_ast_t.ei_type_ptr
|
|
| ObjCIvarRefExpr (_, _, ei, _) -> ei.Clang_ast_t.ei_type_ptr
|
|
| ObjCMessageExpr (_, _, ei, _ ) -> ei.Clang_ast_t.ei_type_ptr
|
|
| PseudoObjectExpr(_, _, ei) -> ei.Clang_ast_t.ei_type_ptr
|
|
| CStyleCastExpr(_, stmt_list, _, _, _)
|
|
| UnaryOperator(_, stmt_list, _, _)
|
|
| ImplicitCastExpr(_, stmt_list, _, _) ->
|
|
get_type_from_exp_stmt (extract_stmt_from_singleton stmt_list "WARNING: We expect only one stmt.")
|
|
| DeclRefExpr(_, _, _, info) -> do_decl_ref_exp info
|
|
| _ -> Printing.log_err "Failing with: %s \n%!" (Clang_ast_j.string_of_stmt stmt);
|
|
Printing.print_failure_info "";
|
|
assert false
|
|
|
|
module Self =
|
|
struct
|
|
|
|
exception SelfClassException of string
|
|
|
|
let add_self_parameter_for_super_instance context procname loc mei =
|
|
if is_superinstance mei then
|
|
let typ, self_expr, id, ins =
|
|
let t' = CTypes.add_pointer_to_typ
|
|
(CTypes_decl.get_type_curr_class_objc
|
|
context.CContext.tenv context.CContext.curr_class) in
|
|
let e = Sil.Lvar (Sil.mk_pvar (Mangled.from_string CFrontend_config.self) procname) in
|
|
let id = Ident.create_fresh Ident.knormal in
|
|
t', Sil.Var id, [id], [Sil.Letderef (id, e, t', loc)] in
|
|
{ empty_res_trans with
|
|
exps = [(self_expr, typ)];
|
|
ids = id;
|
|
instrs = ins }
|
|
else empty_res_trans
|
|
|
|
let is_var_self pvar is_objc_method =
|
|
let is_self = Mangled.to_string (Sil.pvar_get_name pvar) = CFrontend_config.self in
|
|
is_self && is_objc_method
|
|
|
|
end
|
|
|
|
(* From the manual: A selector is in a certain selector family if, ignoring any leading underscores, *)
|
|
(* the first component of the selector either consists entirely of the name of *)
|
|
(* the method family or it begins with that followed by character other than lower case letter.*)
|
|
(* For example: '__perform:with' and 'performWith:' would fall into the 'perform' family (if we had one),*)
|
|
(* but 'performing:with' would not. *)
|
|
let is_owning_name n =
|
|
let is_family fam s'=
|
|
if String.length s' < String.length fam then false
|
|
else (
|
|
let prefix = Str.string_before s' (String.length fam) in
|
|
let suffix = Str.string_after s' (String.length fam) in
|
|
prefix = fam && not (Str.string_match (Str.regexp "[a-z]") suffix 0)
|
|
) in
|
|
match Str.split (Str.regexp_string ":") n with
|
|
| fst:: _ ->
|
|
(match Str.split (Str.regexp "['_']+") fst with
|
|
| [no_und]
|
|
| _:: no_und:: _ ->
|
|
is_family CFrontend_config.alloc no_und ||
|
|
is_family CFrontend_config.copy no_und ||
|
|
is_family CFrontend_config.new_str no_und ||
|
|
is_family CFrontend_config.mutableCopy no_und ||
|
|
is_family CFrontend_config.init no_und
|
|
| _ -> assert false)
|
|
| _ -> assert false
|
|
|
|
let rec is_owning_method s =
|
|
match s with
|
|
| Clang_ast_t.ObjCMessageExpr(_, _ , _, mei) ->
|
|
is_owning_name mei.Clang_ast_t.omei_selector
|
|
| _ -> (match snd (Clang_ast_proj.get_stmt_tuple s) with
|
|
| [] -> false
|
|
| s'':: _ -> is_owning_method s'')
|
|
|
|
let rec is_method_call s =
|
|
match s with
|
|
| Clang_ast_t.ObjCMessageExpr _ -> true
|
|
| _ -> (match snd (Clang_ast_proj.get_stmt_tuple s) with
|
|
| [] -> false
|
|
| s'':: _ -> is_method_call s'')
|
|
|
|
let get_info_from_decl_ref decl_ref =
|
|
let name_info = match decl_ref.Clang_ast_t.dr_name with Some ni -> ni | _ -> assert false in
|
|
let decl_ptr = decl_ref.Clang_ast_t.dr_decl_pointer in
|
|
let type_ptr = match decl_ref.Clang_ast_t.dr_type_ptr with Some tp -> tp | _ -> assert false in
|
|
name_info, decl_ptr, type_ptr
|
|
|
|
let rec get_decl_ref_info s =
|
|
match s with
|
|
| Clang_ast_t.DeclRefExpr (_, _, _, decl_ref_expr_info) ->
|
|
(match decl_ref_expr_info.Clang_ast_t.drti_decl_ref with
|
|
| Some decl_ref -> decl_ref
|
|
| None -> assert false)
|
|
| _ ->
|
|
match Clang_ast_proj.get_stmt_tuple s with
|
|
| _, [] -> assert false
|
|
| _, s'':: _ ->
|
|
get_decl_ref_info s''
|
|
|
|
let rec contains_opaque_value_expr s =
|
|
match s with
|
|
| Clang_ast_t.OpaqueValueExpr _ -> true
|
|
| _ -> match snd (Clang_ast_proj.get_stmt_tuple s) with
|
|
| [] -> false
|
|
| s'':: _ -> contains_opaque_value_expr s''
|
|
|
|
(* checks if a unary operator is a logic negation applied to integers*)
|
|
let is_logical_negation_of_int tenv ei uoi =
|
|
match CTypes_decl.type_ptr_to_sil_type tenv ei.Clang_ast_t.ei_type_ptr, uoi.Clang_ast_t.uoi_kind with
|
|
| Sil.Tint Sil.IInt,`LNot -> true
|
|
| _, _ -> false
|
|
|
|
(* Checks if stmt_list is a call to a special dispatch function *)
|
|
let is_dispatch_function stmt_list =
|
|
let open Clang_ast_t in
|
|
match stmt_list with
|
|
| ImplicitCastExpr(_,[DeclRefExpr(_, _, _, di)], _, _):: stmts ->
|
|
(match di.Clang_ast_t.drti_decl_ref with
|
|
| None -> None
|
|
| Some d ->
|
|
(match d.Clang_ast_t.dr_kind, d.Clang_ast_t.dr_name with
|
|
| `Function, Some name_info ->
|
|
let s = name_info.Clang_ast_t.ni_name in
|
|
(match (CTrans_models.is_dispatch_function_name s) with
|
|
| None -> None
|
|
| Some (_, block_arg_pos) ->
|
|
try
|
|
(match IList.nth stmts block_arg_pos with
|
|
| BlockExpr _ -> Some block_arg_pos
|
|
| _ -> None)
|
|
with Not_found -> None
|
|
)
|
|
| _ -> None))
|
|
| _ -> None
|
|
|
|
let is_block_enumerate_function mei =
|
|
mei.Clang_ast_t.omei_selector = CFrontend_config.enumerateObjectsUsingBlock
|
|
|
|
(* This takes a variable of type struct or array and returns a list of expressions *)
|
|
(* for each of its fields (also recursively, such that each field access is of a basic type) *)
|
|
(* If the flag return_zero is true, the list will be a list of zero values, otherwise it will *)
|
|
(* be a list of LField expressions *)
|
|
let var_or_zero_in_init_list tenv e typ ~return_zero:return_zero =
|
|
let rec var_or_zero_in_init_list' e typ tns =
|
|
let open General_utils in
|
|
match typ with
|
|
| Sil.Tvar tn ->
|
|
(match Sil.tenv_lookup tenv tn with
|
|
| Some struct_typ -> var_or_zero_in_init_list' e (Sil.Tstruct struct_typ) tns
|
|
| _ -> [[(e, typ)]] (*This case is an error, shouldn't happen.*))
|
|
| Sil.Tstruct { Sil.instance_fields } as type_struct ->
|
|
let lh_exprs = IList.map ( fun (fieldname, _, _) ->
|
|
Sil.Lfield (e, fieldname, type_struct) ) instance_fields in
|
|
let lh_types = IList.map ( fun (_, fieldtype, _) -> fieldtype) instance_fields in
|
|
let exp_types = zip lh_exprs lh_types in
|
|
IList.map (fun (e, t) ->
|
|
IList.flatten (var_or_zero_in_init_list' e t tns)) exp_types
|
|
| Sil.Tarray (arrtyp, Sil.Const (Sil.Cint n)) ->
|
|
let size = Sil.Int.to_int n in
|
|
let indices = list_range 0 (size - 1) in
|
|
let index_constants =
|
|
IList.map (fun i -> (Sil.Const (Sil.Cint (Sil.Int.of_int i)))) indices in
|
|
let lh_exprs =
|
|
IList.map (fun index_expr -> Sil.Lindex (e, index_expr)) index_constants in
|
|
let lh_types = replicate size arrtyp in
|
|
let exp_types = zip lh_exprs lh_types in
|
|
IList.map (fun (e, t) ->
|
|
IList.flatten (var_or_zero_in_init_list' e t tns)) exp_types
|
|
| Sil.Tint _ | Sil.Tfloat _ | Sil.Tptr _ ->
|
|
let exp = if return_zero then Sil.zero_value_of_numerical_type typ else e in
|
|
[ [(exp, typ)] ]
|
|
| Sil.Tfun _ | Sil.Tvoid | Sil.Tarray _ -> assert false in
|
|
IList.flatten (var_or_zero_in_init_list' e typ StringSet.empty)
|
|
|
|
(*
|
|
(** Similar to extract_item_from_singleton but for option type *)
|
|
let extract_item_from_option op warning_string =
|
|
match op with
|
|
| Some item -> item
|
|
| _ -> Printing.log_err warning_string; assert false
|
|
|
|
let extract_id_from_singleton id_list warning_string =
|
|
extract_item_from_singleton id_list warning_string (dummy_id ())
|
|
|
|
let get_decl_pointer decl_ref_expr_info =
|
|
match decl_ref_expr_info.Clang_ast_t.drti_decl_ref with
|
|
| Some decl_ref -> decl_ref.Clang_ast_t.dr_decl_pointer
|
|
| None -> assert false
|
|
*)
|