[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[]`
master
Andrzej Kotulski 9 years ago
parent 3b22adfc15
commit 1d6873f471

@ -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);

@ -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 =

@ -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

@ -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;
}

@ -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 ;
}

@ -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<String> 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));
}
}
Loading…
Cancel
Save