Translate global const vars locally to a procedure

Summary: With this approach, all the global consts will be inlined in the places where they are used.

Reviewed By: dulmarod

Differential Revision: D3703133

fbshipit-source-id: 3c19479
master
Martino Luca 8 years ago committed by Facebook Github Bot 8
parent 6840efdd1c
commit 40b176fd01

@ -472,39 +472,6 @@ struct
{ empty_res_trans with exps = [(Exp.Const (Const.Cfun pname), typ)] }
end
let var_deref_trans trans_state stmt_info decl_ref =
let open CContext in
let context = trans_state.context in
let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in
let ast_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in
let typ = match ast_typ with
| Typ.Tstruct _ when decl_ref.Clang_ast_t.dr_kind = `ParmVar ->
if General_utils.is_cpp_translation Config.clang_lang then
Typ.Tptr (ast_typ, Typ.Pk_reference)
else ast_typ
| _ -> ast_typ in
let procname = Cfg.Procdesc.get_proc_name context.procdesc in
let sil_loc = CLocation.get_sil_location stmt_info context in
let pvar = CVar_decl.sil_var_of_decl_ref context decl_ref procname in
CContext.add_block_static_var context procname (pvar, typ);
let e = Exp.Lvar pvar in
let exps = if Self.is_var_self pvar (CContext.is_objc_method context) then
let curr_class = CContext.get_curr_class context in
if (CTypes.is_class typ) then
raise (Self.SelfClassException (CContext.get_curr_class_name curr_class))
else
let typ = CTypes.add_pointer_to_typ
(CTypes_decl.get_type_curr_class_objc context.tenv curr_class) in
[(e, typ)]
else [(e, typ)] in
Printing.log_out "\n\n PVAR ='%s'\n\n" (Pvar.to_string pvar);
let res_trans = { empty_res_trans with exps = exps } in
match typ with
| Typ.Tptr (_, Typ.Pk_reference) ->
(* dereference pvar due to the behavior of reference types in clang's AST *)
dereference_value_from_result sil_loc res_trans ~strip_pointer:true
| _ -> res_trans
let field_deref_trans trans_state stmt_info pre_trans_result decl_ref ~is_constructor_init =
let open CContext in
let context = trans_state.context in
@ -632,6 +599,57 @@ struct
Cfg.Node.set_succs_exn trans_state.context.cfg root_node' res_trans.root_nodes [];
{ empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes }
and var_deref_trans trans_state stmt_info (decl_ref : Clang_ast_t.decl_ref) =
let context = trans_state.context in
let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in
let ast_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in
let typ = match ast_typ with
| Tstruct _ when decl_ref.dr_kind = `ParmVar ->
if General_utils.is_cpp_translation Config.clang_lang then
Typ.Tptr (ast_typ, Pk_reference)
else ast_typ
| _ -> ast_typ in
let procname = Cfg.Procdesc.get_proc_name context.procdesc in
let sil_loc = CLocation.get_sil_location stmt_info context in
let pvar = CVar_decl.sil_var_of_decl_ref context decl_ref procname in
CContext.add_block_static_var context procname (pvar, typ);
let var_exp = Exp.Lvar pvar in
(* handle references to global const *)
(* if there's a reference to a global const, add a fake instruction that *)
(* assigns the global again to its initialization value right before the *)
(* place where it is used *)
let trans_result' =
let is_global_const, init_expr =
match Ast_utils.get_decl decl_ref.dr_decl_pointer with
| Some VarDecl (_, _, qual_type, vdi) ->
(match ast_typ with
| Tstruct _
when not (General_utils.is_cpp_translation Config.clang_lang) ->
(* Do not convert a global struct to a local because SIL
values do not include structs, they must all be heap-allocated *)
false, None
| _ -> vdi.vdi_is_global && qual_type.is_const, vdi.vdi_init_expr)
| _ -> false, None in
if is_global_const then
init_expr_trans trans_state (var_exp, typ) stmt_info init_expr
else empty_res_trans in
let exps = if Self.is_var_self pvar (CContext.is_objc_method context) then
let curr_class = CContext.get_curr_class context in
if (CTypes.is_class typ) then
raise (Self.SelfClassException (CContext.get_curr_class_name curr_class))
else
let typ = CTypes.add_pointer_to_typ
(CTypes_decl.get_type_curr_class_objc context.tenv curr_class) in
[(var_exp, typ)]
else [(var_exp, typ)] in
Printing.log_out "\n\n PVAR ='%s'\n\n" (Pvar.to_string pvar);
let res_trans = { trans_result' with exps } in
match typ with
| Tptr (_, Pk_reference) ->
(* dereference pvar due to the behavior of reference types in clang's AST *)
dereference_value_from_result sil_loc res_trans ~strip_pointer:true
| _ -> res_trans
and decl_ref_trans trans_state pre_trans_result stmt_info decl_ref ~is_constructor_init =
Printing.log_out " priority node free = '%s'\n@."
(string_of_bool (PriorityNode.is_priority_free trans_state));

@ -0,0 +1,22 @@
/*
* Copyright (c) 2016 - 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.
*/
struct X {
X() {}
};
const X global;
X test() { return global; }
static const int v = 2;
int test2() {
int local = v;
return v;
}

@ -0,0 +1,43 @@
/* @generated */
digraph iCFG {
11 [label="11: DeclStmt \n *&#GB$v:int =2 [line 20]\n n$1=*&#GB$v:int [line 20]\n *&local:int =n$1 [line 20]\n " shape="box"]
11 -> 10 ;
10 [label="10: Return Stmt \n *&#GB$v:int =2 [line 21]\n n$0=*&#GB$v:int [line 21]\n *&return:int =n$0 [line 21]\n " shape="box"]
10 -> 9 ;
9 [label="9: Exit test2 \n " color=yellow style=filled]
8 [label="8: Start test2\nFormals: \nLocals: local:int \n DECLARE_LOCALS(&return,&local); [line 19]\n " color=yellow style=filled]
8 -> 11 ;
7 [label="7: Return Stmt \n n$0=*&__return_param:class X * [line 15]\n _fun_X_X(&#GB$global:class X *) [line 13]\n _fun_X_X(n$0:class X *,&#GB$global:class X &) [line 15]\n " shape="box"]
7 -> 6 ;
6 [label="6: Exit test \n " color=yellow style=filled]
5 [label="5: Start test\nFormals: __return_param:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 15]\n " color=yellow style=filled]
5 -> 7 ;
4 [label="4: Exit X_X \n " color=yellow style=filled]
3 [label="3: Start X_X\nFormals: this:class X * __param_0:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled]
3 -> 4 ;
2 [label="2: Exit X_X \n " color=yellow style=filled]
1 [label="1: Start X_X\nFormals: this:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 11]\n " color=yellow style=filled]
1 -> 2 ;
}

@ -0,0 +1,11 @@
/*
* Copyright (c) 2016 - 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.
*/
const int global = 1 ? 2 : 3;
int test() { return global; }

@ -0,0 +1,35 @@
/* @generated */
digraph iCFG {
8 [label="8: Return Stmt \n n$1=*&0$?%__sil_tmpSIL_temp_conditional___n$0:int [line 10]\n *&#GB$global:int =n$1 [line 11]\n n$2=*&#GB$global:int [line 11]\n *&return:int =n$2 [line 11]\n " shape="box"]
8 -> 2 ;
7 [label="7: ConditinalStmt Branch \n *&0$?%__sil_tmpSIL_temp_conditional___n$0:int =3 [line 10]\n " shape="box"]
7 -> 3 ;
6 [label="6: ConditinalStmt Branch \n *&0$?%__sil_tmpSIL_temp_conditional___n$0:int =2 [line 10]\n " shape="box"]
6 -> 3 ;
5 [label="5: Prune (false branch) \n PRUNE((1 == 0), false); [line 10]\n " shape="invhouse"]
5 -> 7 ;
4 [label="4: Prune (true branch) \n PRUNE((1 != 0), true); [line 10]\n " shape="invhouse"]
4 -> 6 ;
3 [label="3: + \n " ]
3 -> 8 ;
2 [label="2: Exit test \n " color=yellow style=filled]
1 [label="1: Start test\nFormals: \nLocals: 0$?%__sil_tmpSIL_temp_conditional___n$0:int \n DECLARE_LOCALS(&return,&0$?%__sil_tmpSIL_temp_conditional___n$0); [line 11]\n " color=yellow style=filled]
1 -> 4 ;
1 -> 5 ;
}

@ -32,7 +32,7 @@ digraph iCFG {
17 -> 16 ;
16 [label="16: BinaryOperatorStmt: Assign \n n$0=*&#GB$bar::pi:double [line 57]\n *&j:double =n$0 [line 57]\n " shape="box"]
16 [label="16: BinaryOperatorStmt: Assign \n *&#GB$bar::pi:double =3.141600 [line 57]\n n$0=*&#GB$bar::pi:double [line 57]\n *&j:double =n$0 [line 57]\n " shape="box"]
16 -> 15 ;
@ -54,7 +54,7 @@ digraph iCFG {
11 -> 12 ;
10 [label="10: Return Stmt \n n$0=*&#GB$bar::pi:double [line 30]\n *&return:double =(2 * n$0) [line 30]\n " shape="box"]
10 [label="10: Return Stmt \n *&#GB$bar::pi:double =3.141600 [line 30]\n n$0=*&#GB$bar::pi:double [line 30]\n *&return:double =(2 * n$0) [line 30]\n " shape="box"]
10 -> 9 ;

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016 - 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.
*/
#import <Foundation/Foundation.h>
static NSString* const GlobalConst = @"A Global Const!";
static NSString* Global = @"A Global!";
@interface SimpleRoot : NSObject
@end
@implementation SimpleRoot
- (void)doSomethingOkWithDict:(NSMutableDictionary*)dict
andString:(NSString*)input {
NSString* str = [GlobalConst stringByAppendingString:input];
[dict removeObjectForKey:str];
}
- (void)doSomethingBadWithDict:(NSMutableDictionary*)dict
andString:(NSString*)input {
NSString* str = [Global stringByAppendingString:input];
[dict removeObjectForKey:str];
}
@end

@ -0,0 +1,20 @@
/*
* Copyright (c) 2016 - 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.
*/
typedef struct {
float field1;
float field2;
float field3;
} Fields;
static const Fields __someFields = {
.field1 = 1, .field2 = 2, .field3 = 3,
};
Fields fields() { return __someFields; }

@ -0,0 +1,33 @@
/* @generated */
digraph iCFG {
8 [label="8: Return Stmt \n n$0=*&__return_param:class Fields * [line 20]\n *&#GB$__someFields.field1:float =1 [line 16]\n *&#GB$__someFields.field2:float =2 [line 16]\n *&#GB$__someFields.field3:float =3 [line 16]\n _fun_Fields_(n$0:class Fields *,&#GB$__someFields:class Fields &) [line 20]\n " shape="box"]
8 -> 7 ;
7 [label="7: Exit fields \n " color=yellow style=filled]
6 [label="6: Start fields\nFormals: __return_param:class Fields *\nLocals: \n DECLARE_LOCALS(&return); [line 20]\n " color=yellow style=filled]
6 -> 8 ;
5 [label="5: Constructor Init \n n$6=*&this:class Fields * [line 10]\n n$7=*&__param_0:class Fields & [line 10]\n n$8=*n$7.field1:float [line 10]\n *n$6.field1:float =n$8 [line 10]\n " shape="box"]
5 -> 4 ;
4 [label="4: Constructor Init \n n$3=*&this:class Fields * [line 10]\n n$4=*&__param_0:class Fields & [line 10]\n n$5=*n$4.field2:float [line 10]\n *n$3.field2:float =n$5 [line 10]\n " shape="box"]
4 -> 3 ;
3 [label="3: Constructor Init \n n$0=*&this:class Fields * [line 10]\n n$1=*&__param_0:class Fields & [line 10]\n n$2=*n$1.field3:float [line 10]\n *n$0.field3:float =n$2 [line 10]\n " shape="box"]
3 -> 2 ;
2 [label="2: Exit Fields_ \n " color=yellow style=filled]
1 [label="1: Start Fields_\nFormals: this:class Fields * __param_0:class Fields &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled]
1 -> 5 ;
}

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016 - 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 endtoend.objc.infer;
import static org.hamcrest.MatcherAssert.assertThat;
import static utils.matchers.ResultContainsExactly.containsExactly;
import com.google.common.collect.ImmutableList;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import java.io.IOException;
import utils.DebuggableTemporaryFolder;
import utils.InferException;
import utils.InferResults;
import utils.InferRunner;
public class GlobalConstNPETest {
public static final String FILE =
"infer/tests/codetoanalyze/objc/errors/global_const/global_const.m";
private static ImmutableList<String> inferCmd;
public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE";
@ClassRule
public static DebuggableTemporaryFolder folder =
new DebuggableTemporaryFolder();
@BeforeClass
public static void runInfer() throws InterruptedException, IOException {
inferCmd = InferRunner.createObjCInferCommand(folder, FILE);
}
@Test
public void whenInferRunsOnMain1ThenDivideByZeroIsFound()
throws InterruptedException, IOException, InferException {
InferResults inferResults = InferRunner.runInferObjC(inferCmd);
String[] procedures = {"doSomethingBadWithDict:andString:"};
assertThat(
"Results should contain the expected " + NULL_DEREFERENCE,
inferResults,
containsExactly(NULL_DEREFERENCE, FILE, procedures)
);
}
}

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016 - 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 org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import utils.ClangFrontendUtils;
import utils.DebuggableTemporaryFolder;
import utils.InferException;
public class GlobalConstTest {
@Rule
public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder();
@Test
public void whenCaptureRunCommaThenDotFilesAreTheSame()
throws InterruptedException, IOException, InferException {
String src =
"infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp";
ClangFrontendUtils.createAndCompareCppDotFiles(folder, src);
}
}

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016 - 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 org.junit.Rule;
import org.junit.Test;
import java.io.IOException;
import utils.ClangFrontendUtils;
import utils.DebuggableTemporaryFolder;
import utils.InferException;
public class GlobalConstTest2 {
@Rule
public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder();
@Test
public void whenCaptureRunCommaThenDotFilesAreTheSame()
throws InterruptedException, IOException, InferException {
String src =
"infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp";
ClangFrontendUtils.createAndCompareCppDotFiles(folder, src);
}
}

@ -0,0 +1,49 @@
/*
* Copyright (c) 2016 - 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.objcpp;
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 GlobalConstTest {
@Rule
public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder();
@Test
public void whenCaptureRunSwitchStmtThenDotFilesAreTheSame()
throws InterruptedException, IOException, InferException {
String switch_src =
"infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm";
String switch_dotty =
"infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm.dot";
ImmutableList<String> inferCmd =
InferRunner.createObjCPPInferCommand(folder, switch_src);
File newDotFile = InferRunner.runInferFrontend(inferCmd);
assertThat(
"In the capture of " + switch_src +
" the dotty files should be the same.",
newDotFile, dotFileEqualTo(switch_dotty));
}
}
Loading…
Cancel
Save