From 1d6873f4711ed8841b98cfb8b279c9011550fa19 Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Wed, 5 Aug 2015 15:34:00 -0100 Subject: [PATCH] [C++] Handle simple new cases Summary: Add basic translation for C++ `new` keyword. Currently, it's modeled as simple `malloc` call. Following constructs are still not working properly: - array new `new [size_expr]` - run initializer attached to `new` (such as `new int(5)`) - `delete[]` --- infer/src/clang/cTrans.ml | 33 ++++++++++++ infer/src/clang/cTrans_utils.ml | 30 +++++++---- infer/src/clang/cTrans_utils.mli | 2 + .../cpp/frontend/builtin/new.cpp | 18 +++++++ .../cpp/frontend/builtin/new.dot | 25 +++++++++ infer/tests/frontend/cpp/NewTest.java | 51 +++++++++++++++++++ 6 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/frontend/builtin/new.cpp create mode 100644 infer/tests/codetoanalyze/cpp/frontend/builtin/new.dot create mode 100644 infer/tests/frontend/cpp/NewTest.java diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index a37393975..44b784c9f 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -1645,6 +1645,35 @@ struct { empty_res_trans with ids = ids_block @ ids; instrs = alloc_block_instr @ instrs; exps = [(Sil.Const tu, typ)]} | _ -> assert false) + and cxxNewExpr_trans trans_state stmt_info expr_info = + let context = trans_state.context in + let typ = CTypes_decl.get_type_from_expr_info expr_info context.tenv in + let sil_loc = get_sil_location stmt_info trans_state.parent_line_number context in + let trans_state_pri = PriorityNode.try_claim_priority_node trans_state stmt_info in + cpp_new_trans trans_state_pri sil_loc stmt_info typ + (* TODOs 7912220 - no usable information in json as of right now *) + (* 1. Handle __new_array *) + (* 2. Handle initialization values *) + + and cxxDeleteExpr_trans trans_state stmt_info stmt_list expr_info = + let context = trans_state.context in + let sil_loc = get_sil_location stmt_info trans_state.parent_line_number context in + let fname = SymExec.ModelBuiltins.__delete in + let param = match stmt_list with [p] -> p | _ -> assert false in + let trans_state_pri = PriorityNode.try_claim_priority_node trans_state stmt_info in + let trans_state_param = { trans_state_pri with succ_nodes = [] } in + let result_trans_param = exec_with_self_exception instruction trans_state_param param in + let exp = extract_exp_from_list result_trans_param.exps + "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 + { res_trans with exps = [] } + + (* Translates a clang instruction into SIL instructions. It takes a *) (* a trans_state containing current info on the translation and it returns *) (* a result_state.*) @@ -1845,6 +1874,10 @@ struct "BinaryConditionalOperator not translated %s @." (Ast_utils.string_of_stmt instr); assert false) + | CXXNewExpr (stmt_info, stmt_list, expr_info) -> + cxxNewExpr_trans trans_state stmt_info expr_info + | CXXDeleteExpr (stmt_info, stmt_list, expr_info) -> + cxxDeleteExpr_trans trans_state stmt_info stmt_list expr_info | s -> (Printing.log_stats "\n!!!!WARNING: found statement %s. \nACTION REQUIRED: Translation need to be defined. Statement ignored.... \n" (Ast_utils.string_of_stmt s); diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 533b82399..f1f30e604 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -271,11 +271,8 @@ struct | For (_, cond, _, _) | While (cond, _) | DoWhile (cond, _) -> cond end -let create_alloc_instrs context sil_loc 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 +(** 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) @@ -291,15 +288,20 @@ let create_alloc_instrs context sil_loc function_type is_cf_non_null_alloc = (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 (function_type, ret_id, stmt_call, exp) = create_alloc_instrs trans_state.context loc function_type is_cf_non_null_alloc in + 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 = PriorityNode.compute_results_to_parent trans_state loc "Call alloc" stmt_info res_trans_tmp in { res_trans with exps =[(exp, function_type)]} -let new_trans trans_state loc stmt_info cls_name 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, alloc_exp) = - create_alloc_instrs trans_state.context loc function_type true in + 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_virtual = is_instance; Sil.cf_noreturn = false; Sil.cf_is_objc_block = false; } in @@ -311,7 +313,7 @@ let 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 new" stmt_info res_trans_tmp in + PriorityNode.compute_results_to_parent trans_state loc "Call objC new" 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 class_name selector = @@ -319,9 +321,17 @@ let new_or_alloc_trans trans_state loc stmt_info class_name selector = if selector = CFrontend_config.alloc then alloc_trans trans_state loc stmt_info function_type true else if selector = CFrontend_config.new_str then - new_trans trans_state loc stmt_info class_name function_type + 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 = + PriorityNode.compute_results_to_parent trans_state sil_loc "Call C++ new" 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 cast_typ_no_pointer = diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index 5f7ef58be..85b6c8473 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -100,6 +100,8 @@ val alloc_trans : trans_state -> Sil.location -> Clang_ast_t.stmt_info -> Sil.ty val new_or_alloc_trans : trans_state -> Sil.location -> Clang_ast_t.stmt_info -> string -> string -> trans_result +val cpp_new_trans : trans_state -> Sil.location -> Clang_ast_t.stmt_info -> Sil.typ -> trans_result + val cast_trans : CContext.t -> (Sil.exp * Sil.typ) list -> Sil.location -> Procname.t option -> Sil.typ -> (Ident.t * Sil.instr * Sil.exp) option diff --git a/infer/tests/codetoanalyze/cpp/frontend/builtin/new.cpp b/infer/tests/codetoanalyze/cpp/frontend/builtin/new.cpp new file mode 100644 index 000000000..65df46ee1 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/builtin/new.cpp @@ -0,0 +1,18 @@ +/* +* Copyright (c) 2015 - 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. +*/ + +void test() { + int x = 2; + int* i = new int; + new int; // this will be leak - test that it gets to cfg + delete i; + + //int* i_a = new int[10]; + //delete[] i_a; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/builtin/new.dot b/infer/tests/codetoanalyze/cpp/frontend/builtin/new.dot new file mode 100644 index 000000000..ef2fb0f2a --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/builtin/new.dot @@ -0,0 +1,25 @@ +digraph iCFG { +6 [label="6: DeclStmt \n *&x:int =2 [line 11]\n NULLIFY(&x,false); [line 11]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: DeclStmt \n n$2=_fun___new(sizeof(int ):int *) [line 12]\n *&i:int *=n$2 [line 12]\n REMOVE_TEMPS(n$2); [line 12]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: Call C++ new \n n$1=_fun___new(sizeof(int ):int *) [line 13]\n REMOVE_TEMPS(n$1); [line 13]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: Call delete \n n$0=*&i:int * [line 14]\n _fun___delete(n$0:int *) [line 14]\n REMOVE_TEMPS(n$0); [line 14]\n NULLIFY(&i,false); [line 14]\n APPLY_ABSTRACTION; [line 14]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit test \n " color=yellow style=filled] + + +1 [label="1: Start test\nFormals: \nLocals: x:int i:int * \n DECLARE_LOCALS(&return,&x,&i); [line 10]\n NULLIFY(&i,false); [line 10]\n NULLIFY(&x,false); [line 10]\n " color=yellow style=filled] + + + 1 -> 6 ; +} diff --git a/infer/tests/frontend/cpp/NewTest.java b/infer/tests/frontend/cpp/NewTest.java new file mode 100644 index 000000000..904753629 --- /dev/null +++ b/infer/tests/frontend/cpp/NewTest.java @@ -0,0 +1,51 @@ +/* +* 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. +*/ + +package frontend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.DotFilesEqual.dotFileEqualTo; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferRunner; + +public class NewTest { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunCommaThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = + "infer/tests/codetoanalyze/cpp/frontend/builtin/new.cpp"; + + String dotty = + "infer/tests/codetoanalyze/cpp/frontend/builtin/new.dot"; + + ImmutableList inferCmd = + InferRunner.createCPPInferCommandFrontend( + folder, + src); + File newDotFile = InferRunner.runInferFrontend(inferCmd); + assertThat( + "In the capture of " + src + + " the dotty files should be the same.", + newDotFile, dotFileEqualTo(dotty)); + } +}