From 60b4f6e766889017424b8cf40a9b73d3929ef48a Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Mon, 30 Nov 2015 05:59:49 -0800 Subject: [PATCH] Refactor PriorityNode.compute_results_to_parent to call collect_res_trans directly Summary: public Refactor compute_results_to_parent to call collect_res_trans directly there. So the job of PriorityNode.compute_results_to_parent is the following: Given list of [res_trans_1; res_trans_2; ... ; res_trans_n], 1. Connect nodes linearly (ie. res_trans_1 with res_trans_2, res_trans_2 with res_trans_3) etc. If res_trans_k has no nodes, try connecting res_trans_k-1 with res_trans_k+1 (this is what collect_res_trans always did) 2. Collect all ids, instrs and exps into one res_trans 3. Create node if needed and put there same ids and instrs as usual. 4. Connect new node with existing ones 4. Return new node structrue with exps = [] This will make function usable in more places so we don't have to create nodes over and over again Reviewed By: ddino Differential Revision: D2696351 fb-gh-sync-id: 9f4a9b4 --- infer/src/clang/cTrans.ml | 132 ++++++++++++++----------------- infer/src/clang/cTrans_utils.ml | 62 ++++++--------- infer/src/clang/cTrans_utils.mli | 21 ++--- 3 files changed, 91 insertions(+), 124 deletions(-) diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 93d5c857e..ab39b49dd 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -171,6 +171,9 @@ struct | e :: es' -> e :: f es' in (f exps, !insts, !ids) + let collect_exprs res_trans_list = + IList.flatten (IList.map (fun res_trans -> res_trans.exps) res_trans_list) + (* If e is a block and the calling node has the priority then *) (* we need to release the priority to allow*) (* creation of nodes inside the block.*) @@ -698,11 +701,10 @@ struct let params_stmt = if should_translate_args then CTrans_utils.assign_default_params params_stmt fun_exp_stmt else [] in - let res_trans_par = + let result_trans_subexprs = let instruction' = exec_with_self_exception (exec_with_lvalue_as_reference instruction) in - let l = IList.map (instruction' trans_state_param) params_stmt in - let rt = collect_res_trans (res_trans_callee :: l) in - { rt with exps = IList.tl rt.exps } in + let res_trans_p = IList.map (instruction' trans_state_param) params_stmt in + res_trans_callee :: res_trans_p in let sil_fe, is_cf_retain_release = CTrans_models.builtin_predefined_model fun_exp_stmt sil_fe in if CTrans_models.is_assert_log sil_fe then if Config.report_assertion_failure then @@ -710,11 +712,13 @@ struct else CTrans_utils.trans_assume_false sil_loc context trans_state.succ_nodes else - let act_params = if IList.length res_trans_par.exps = IList.length params_stmt then - res_trans_par.exps + let act_params = + let params = IList.tl (collect_exprs result_trans_subexprs) in + if IList.length params = IList.length params_stmt then + params else (Printing.log_err "WARNING: stmt_list and res_trans_par.exps must have same size. NEED TO BE FIXED\n\n"; - fix_param_exps_mismatch params_stmt res_trans_par.exps) in + fix_param_exps_mismatch params_stmt params) in let act_params = if is_cf_retain_release then (Sil.Const (Sil.Cint Sil.Int.one), Sil.Tint Sil.IBool) :: act_params else act_params in @@ -730,12 +734,12 @@ struct let call_flags = { Sil.cf_virtual = false; Sil.cf_noreturn = false; Sil.cf_is_objc_block = is_call_to_block; } in let call_instr = Sil.Call(ret_id, sil_fe, act_params, sil_loc, call_flags) in ret_id, call_instr in - let ids = res_trans_par.ids@ret_id in - let instrs = res_trans_par.instrs @ [call_instr] in + + let res_trans_call = { empty_res_trans with ids = ret_id; instrs = [call_instr] } in let nname = "Call "^(Sil.exp_to_string sil_fe) in - let res_trans_tmp = { res_trans_par with ids = ids; instrs = instrs; exps =[]} in + let all_res_trans = result_trans_subexprs @ [res_trans_call] in let res_trans_to_parent = - PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si res_trans_tmp in + PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si all_res_trans in (match callee_pname_opt with | Some callee_pname -> let open CContext in @@ -769,14 +773,12 @@ struct (* afterwards. The 'instructions' function does not do that *) let trans_state_param = { trans_state_pri with parent_line_number = line_number; succ_nodes = [] } in - let result_trans_params = - let instruction' = exec_with_lvalue_as_reference instruction in - let l = IList.map (exec_with_self_exception instruction' trans_state_param) params_stmt in - (* this function will automatically merge 'this' argument with rest of arguments in 'l'*) - let rt = collect_res_trans (result_trans_callee :: l) in - { rt with exps = IList.tl rt.exps } in - - let actual_params = result_trans_params.exps in + let result_trans_subexprs = + let instruction' = exec_with_self_exception (exec_with_lvalue_as_reference instruction) in + let res_trans_p = IList.map (instruction' trans_state_param) params_stmt in + result_trans_callee :: res_trans_p in + (* first expr is method address, rest are params including 'this' parameter *) + let actual_params = IList.tl (collect_exprs result_trans_subexprs) in let ret_id = if (Sil.typ_equal function_type Sil.Tvoid) then [] else [Ident.create_fresh Ident.knormal] in let call_flags = { @@ -785,12 +787,11 @@ struct Sil.cf_is_objc_block = false; } in let call_instr = Sil.Call (ret_id, sil_method, actual_params, sil_loc, call_flags) in - let ids = result_trans_params.ids @ ret_id in - let instrs = result_trans_params.instrs @ [call_instr] in - let res_trans_tmp = { result_trans_params with ids = ids; instrs = instrs; exps =[] } in + let res_trans_call = { empty_res_trans with ids = ret_id; instrs = [call_instr] } in let nname = "Call " ^ (Sil.exp_to_string sil_method) in + let all_res_trans = result_trans_subexprs @ [res_trans_call] in let result_trans_to_parent = - PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si res_trans_tmp in + PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si all_res_trans in Cg.add_edge context.CContext.cg procname callee_pname; match ret_id with | [] -> { result_trans_to_parent with exps = [] } @@ -871,8 +872,8 @@ struct let instruction' = exec_with_self_exception (exec_with_lvalue_as_reference instruction) in let l = IList.map (instruction' trans_state_param) rest in - obj_c_message_expr_info, collect_res_trans (fst_res_trans :: l) - | [] -> obj_c_message_expr_info, empty_res_trans + obj_c_message_expr_info, fst_res_trans :: l + | [] -> obj_c_message_expr_info, [empty_res_trans] and objCMessageExpr_trans trans_state si obj_c_message_expr_info stmt_list expr_info = Printing.log_out " priority node free = '%s'\n@." @@ -886,22 +887,25 @@ struct let trans_state_pri = PriorityNode.try_claim_priority_node trans_state si in let trans_state_param = { trans_state_pri with parent_line_number = line_number; succ_nodes = [] } in - let obj_c_message_expr_info, res_trans_par = + let obj_c_message_expr_info, res_trans_subexpr_list = objCMessageExpr_deal_with_static_self trans_state_param stmt_list obj_c_message_expr_info in + let subexpr_exprs = collect_exprs res_trans_subexpr_list in match objCMessageExpr_trans_special_cases trans_state si obj_c_message_expr_info stmt_list - expr_info method_type trans_state_pri sil_loc res_trans_par.exps with + expr_info method_type trans_state_pri sil_loc subexpr_exprs with | Some res -> res | None -> let procname = Cfg.Procdesc.get_proc_name context.CContext.procdesc in let callee_name, method_call_type = - get_callee_objc_method context obj_c_message_expr_info res_trans_par.exps in - let res_trans_par = Self.add_self_parameter_for_super_instance context procname sil_loc - obj_c_message_expr_info res_trans_par in + get_callee_objc_method context obj_c_message_expr_info subexpr_exprs in + let res_trans_add_self = Self.add_self_parameter_for_super_instance context procname sil_loc + obj_c_message_expr_info in + let res_trans_subexpr_list = res_trans_add_self :: res_trans_subexpr_list in + let subexpr_exprs = collect_exprs res_trans_subexpr_list in let is_virtual = method_call_type = CMethod_trans.MCVirtual in Cg.add_edge context.CContext.cg procname callee_name; let param_exps, instr_block_param, ids_block_param = - extract_block_from_tuple procname res_trans_par.exps sil_loc in + extract_block_from_tuple procname subexpr_exprs sil_loc in let ret_id = if Sil.typ_equal method_type Sil.Tvoid then [] @@ -910,19 +914,17 @@ struct Sil.cf_virtual = is_virtual; Sil.cf_noreturn = false; Sil.cf_is_objc_block = false; } in - let stmt_call = Sil.Call (ret_id, (Sil.Const (Sil.Cfun callee_name)), param_exps, sil_loc, call_flags) in let selector = obj_c_message_expr_info.Clang_ast_t.omei_selector in let nname = "Message Call: "^selector in - let res_trans_tmp = { - res_trans_par with - ids = res_trans_par.ids @ ids_block_param @ ret_id; - instrs = res_trans_par.instrs @ instr_block_param @ [stmt_call]; - exps = [] - } in - let res_trans_to_parent = ( - PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si res_trans_tmp) in + let res_trans_call = { empty_res_trans with + ids = ids_block_param @ ret_id; + instrs = instr_block_param @ [stmt_call]; + } in + let all_res_trans = res_trans_subexpr_list @ [res_trans_call] in + let res_trans_to_parent = + PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si all_res_trans in match ret_id with | [] -> { res_trans_to_parent with exps = [] } | [ret_id'] -> { res_trans_to_parent with exps = [(Sil.Var ret_id', method_type)] } @@ -1445,33 +1447,21 @@ struct "\nWARNING: Missing RHS operand in Compount Assign operator. Need Fixing.\n" in let id_op, exp_op, instr_op = CArithmetic_trans.compound_assignment_binary_operation_instruction binary_operator_info lhs_e sil_typ sil_e2 sil_loc in - let ids = res_trans_s1.ids@res_trans_s2.ids@id_op in - let instrs = res_trans_s1.instrs@res_trans_s2.instrs@instr_op in - let res_trans_tmp = { res_trans_s2 with ids = ids; instrs = instrs; exps =[]} in - let res_trans_to_parent = - PriorityNode.compute_results_to_parent trans_state_pri sil_loc "ComppoundAssignStmt" stmt_info res_trans_tmp in - - let trans_s1_succs = - if res_trans_to_parent.root_nodes <> [] - then res_trans_to_parent.root_nodes - else trans_state_pri.succ_nodes in - IList.iter - (fun n -> Cfg.Node.set_succs_exn n trans_s1_succs []) - res_trans_s1.leaf_nodes; - - let instrs_to_parent', ids_to_parent', exp_to_parent' = - compute_instr_ids_exp_to_parent stmt_info res_trans_to_parent.instrs res_trans_to_parent.ids - [(exp_op, sil_typ)] lhs_e sil_typ sil_loc trans_state_pri.priority in - - let root_nodes = - if res_trans_s1.root_nodes <> [] - then res_trans_s1.root_nodes - else res_trans_to_parent.root_nodes in - { root_nodes = root_nodes; - leaf_nodes = res_trans_to_parent.leaf_nodes; - ids = ids_to_parent'; - instrs = instrs_to_parent'; - exps = exp_to_parent' } + let extra_deref_instrs, extra_deref_ids, exp_to_parent = + if not (PriorityNode.own_priority_node trans_state_pri.priority stmt_info) && + (* assignment operator result is lvalue in CPP, rvalue in C, hence the difference *) + not (General_utils.is_cpp_translation !CFrontend_config.language) then + let id = Ident.create_fresh Ident.knormal in + let instr = Sil.Letderef (id, lhs_e, sil_typ, sil_loc) in + [instr], [id], (Sil.Var id, sil_typ) + else [], [], (exp_op, sil_typ) in + let op_res_trans = { empty_res_trans with + ids = id_op @ extra_deref_ids; + instrs = instr_op @ extra_deref_instrs } in + let all_res_trans = [res_trans_s1; res_trans_s2; op_res_trans] in + let res_trans_to_parent = PriorityNode.compute_results_to_parent trans_state_pri + sil_loc "ComppoundAssignStmt" stmt_info all_res_trans in + { res_trans_to_parent with exps = [exp_to_parent] } | _ -> assert false) (* Compound assign statement should have two operands*) and initListExpr_trans trans_state var_res_trans stmt_info expr_info stmts = @@ -1978,10 +1968,10 @@ struct "WARNING: There should be one expression to delete. \n" in (* function is void *) let call_instr = Sil.Call ([], (Sil.Const (Sil.Cfun fname)), [exp], sil_loc, Sil.cf_default) in - let instrs = result_trans_param.instrs @ [call_instr] in - let res_trans_tmp = { result_trans_param with instrs = instrs} in - let res_trans = - PriorityNode.compute_results_to_parent trans_state_pri sil_loc "Call delete" stmt_info res_trans_tmp in + let call_res_trans = { empty_res_trans with instrs = [call_instr] } in + let all_res_trans = [ result_trans_param; call_res_trans] in + let res_trans = PriorityNode.compute_results_to_parent trans_state_pri sil_loc + "Call delete" stmt_info all_res_trans in { res_trans with exps = [] } and materializeTemporaryExpr_trans trans_state stmt_info stmt_list expr_info = diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 4f7503a39..4d98bb2ae 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -210,20 +210,22 @@ struct | Busy p when p = stmt_info.Clang_ast_t.si_pointer -> true | _ -> false - (* Used for function call and method call. It deals with creating or not *) - (* a cfg node depending of owning the priority_node and the nodes, ids, instrs returned *) - (* by the parameters of the call *) - let compute_results_to_parent trans_state loc nd_name stmt_info res_state_param = - let mk_node () = - let ids_node = ids_to_node trans_state.continuation res_state_param.ids in + (* 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 mk_node res_state = + let ids_node = ids_to_node trans_state.continuation res_state.ids in let node_kind = Cfg.Node.Stmt_node (nd_name) in - Nodes.create_node node_kind ids_node res_state_param.instrs loc trans_state.context in + Nodes.create_node node_kind ids_node res_state.instrs loc trans_state.context in (* Invariant: if leaf_nodes is empty then the params have not created a node.*) + let res_state_param = collect_res_trans res_states_children in match res_state_param.leaf_nodes, own_priority_node trans_state.priority stmt_info with | _, false -> (* The node is created by the parent. We just pass back nodes/leafs params *) { res_state_param with exps = []} | [], true -> (* We need to create a node and params did not create a node.*) - let node' = mk_node () in + let node' = mk_node res_state_param in let ids_parent = ids_to_parent trans_state.continuation res_state_param.ids in Cfg.Node.set_succs_exn node' trans_state.succ_nodes []; { root_nodes =[node']; @@ -234,7 +236,7 @@ struct | _, true -> (* We need to create a node but params also created some,*) (* so we need to pass back the nodes/leafs params*) - let node' = mk_node () in + let node' = mk_node res_state_param in Cfg.Node.set_succs_exn node' trans_state.succ_nodes []; let ids_parent = ids_to_parent trans_state.continuation res_state_param.ids in IList.iter (fun n' -> Cfg.Node.set_succs_exn n' [node'] []) res_state_param.leaf_nodes; @@ -296,7 +298,8 @@ let alloc_trans trans_state loc stmt_info function_type is_cf_non_null_alloc = 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 = - PriorityNode.compute_results_to_parent trans_state loc "Call alloc" stmt_info res_trans_tmp in + 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 = @@ -314,7 +317,8 @@ let objc_new_trans trans_state loc stmt_info cls_name function_type = 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 = - PriorityNode.compute_results_to_parent trans_state loc "Call objC new" stmt_info res_trans_tmp in + 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 = @@ -335,7 +339,8 @@ let cpp_new_trans trans_state sil_loc stmt_info function_type = 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 = - PriorityNode.compute_results_to_parent trans_state sil_loc "Call C++ new" stmt_info res_trans_tmp in + 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 = @@ -446,27 +451,6 @@ let define_condition_side_effects context e_cond instrs_cond sil_loc = [Sil.Letderef (id, Sil.Lvar pvar, typ, sil_loc)] | _ -> [(e', typ)], instrs_cond -(* Given a list of instuctions, ids, an expression, lhs of an compound *) -(* assignment its type and loc it computes which instructions, ids, and *) -(* expression need to be returned to the AST's parent node. This function *) -(* is used by a compount assignment. The expression e is the result of *) -(* translating the rhs of the assignment *) -let compute_instr_ids_exp_to_parent stmt_info instr ids e lhs typ loc pri = - if PriorityNode.own_priority_node pri stmt_info then( - (* The current AST element has created a node then instr and ids have *) - (* been already included in the node. *) - [], [], e - ) else if General_utils.is_cpp_translation !CFrontend_config.language then ( - (* assignment operator result is lvalue in CPP, rvalue in C, hence the difference *) - instr, ids, e - ) else ( - (* The node will be created by the parent. We pass the instr and ids. *) - (* For the expression we need to save the constend of the lhs in a new *) - (* temp so that can be used by the parent node (for example: x=(y=10)) *) - let id = Ident.create_fresh Ident.knormal in - let res_instr = [Sil.Letderef (id, lhs, typ, loc)] in - instr@res_instr, ids @ [id], [(Sil.Var id, typ)]) - 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 @@ -548,7 +532,7 @@ struct exception SelfClassException of string - let add_self_parameter_for_super_instance context procname loc mei trans_result = + 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 @@ -556,11 +540,11 @@ struct 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 - { trans_result with - exps = (self_expr, typ):: trans_result.exps; - ids = id@trans_result.ids; - instrs = ins@trans_result.instrs } - else trans_result + { 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 diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index 39ade7d72..b4da92de4 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -37,9 +37,6 @@ type trans_result = { val empty_res_trans: trans_result -(** Collect the results of translating a list of instructions, and link up the nodes created. *) -val collect_res_trans: trans_result list -> trans_result - val is_return_temp: continuation option -> bool val ids_to_parent: continuation option -> Ident.t list -> Ident.t list @@ -64,11 +61,6 @@ val extract_stmt_from_singleton : Clang_ast_t.stmt list -> string -> Clang_ast_t val is_null_stmt : Clang_ast_t.stmt -> bool -val compute_instr_ids_exp_to_parent : - Clang_ast_t.stmt_info -> Sil.instr list -> Ident.t list -> (Sil.exp * Sil.typ) list -> - Sil.exp -> Sil.typ -> Location.t -> priority_node -> - Sil.instr list * Ident.t list * (Sil.exp * Sil.typ) list - val is_enumeration_constant : Clang_ast_t.stmt -> bool val is_member_exp : Clang_ast_t.stmt -> bool @@ -162,11 +154,12 @@ sig val own_priority_node : t -> Clang_ast_t.stmt_info -> bool - (* Used for function call and method call. It deals with creating or not *) - (* a cfg node depending of owning the priority_node and the nodes returned *) - (* by the parameters of the call *) - val compute_results_to_parent : - trans_state -> Location.t -> string -> Clang_ast_t.stmt_info -> trans_result -> trans_result + (* 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 *) + val compute_results_to_parent : trans_state -> Location.t -> string -> Clang_ast_t.stmt_info -> + trans_result list -> trans_result end @@ -206,7 +199,7 @@ sig val add_self_parameter_for_super_instance : CContext.t -> Procname.t -> Location.t -> Clang_ast_t.obj_c_message_expr_info -> - trans_result -> trans_result + trans_result val is_var_self : Sil.pvar -> bool -> bool end