From 851fb3267bb42b760bb8051c2bad2b0c56609ccb Mon Sep 17 00:00:00 2001 From: Sungkeun Cho Date: Fri, 23 Apr 2021 08:43:07 -0700 Subject: [PATCH] [frontend] ObjC setter/getter for C struct field Summary: This diff copies each field values inside setter/getter of ObjC. Reviewed By: ezgicicek Differential Revision: D27940521 fbshipit-source-id: 9977cae75 --- infer/src/clang/CAddImplicitGettersSetters.ml | 63 +++++++++++-------- .../src/clang/CAddImplicitGettersSetters.mli | 2 +- infer/src/clang/cFrontend.ml | 2 +- infer/src/clang/cMethodSignature.ml | 3 +- .../objc/frontend/property/Property_getter.m | 11 ++++ .../frontend/property/Property_getter.m.dot | 49 +++++++++++++-- infer/tests/codetoanalyze/objc/pulse/uninit.m | 32 ++++++++-- 7 files changed, 124 insertions(+), 38 deletions(-) diff --git a/infer/src/clang/CAddImplicitGettersSetters.ml b/infer/src/clang/CAddImplicitGettersSetters.ml index d23a20410..03b7d421c 100644 --- a/infer/src/clang/CAddImplicitGettersSetters.ml +++ b/infer/src/clang/CAddImplicitGettersSetters.ml @@ -17,53 +17,64 @@ let get_load_self_instr location (self, self_typ) fieldname = (field_exp, load_self_instr) -let objc_getter proc_desc location self_with_typ (fieldname, field_typ, _) = +let objc_getter tenv proc_desc location self_with_typ (fieldname, field_typ, _) = let field_exp, load_self_instr = get_load_self_instr location self_with_typ fieldname in + let id_field = Ident.create_fresh Ident.knormal in let store_instrs = - let id_field = Ident.create_fresh Ident.knormal in - let load_field_instr = - Sil.Load {id= id_field; e= field_exp; root_typ= field_typ; typ= field_typ; loc= location} - in - let exp_var = Exp.Lvar (Procdesc.get_ret_var proc_desc) in - let return_exp = - Sil.Store - {e1= exp_var; root_typ= field_typ; typ= field_typ; e2= Exp.Var id_field; loc= location} - in - [load_field_instr; return_exp] + match field_typ with + | {Typ.desc= Tstruct (CStruct _ as struct_name)} -> + let ret_param = Exp.Lvar (Pvar.get_ret_param_pvar (Procdesc.get_proc_name proc_desc)) in + Sil.Load {id= id_field; e= ret_param; root_typ= field_typ; typ= field_typ; loc= location} + :: CStructCopy.struct_copy tenv location (Exp.Var id_field) field_exp ~typ:field_typ + ~struct_name + | _ -> + let load_field_instr = + Sil.Load {id= id_field; e= field_exp; root_typ= field_typ; typ= field_typ; loc= location} + in + let exp_var = Exp.Lvar (Procdesc.get_ret_var proc_desc) in + let return_exp = + Sil.Store + {e1= exp_var; root_typ= field_typ; typ= field_typ; e2= Exp.Var id_field; loc= location} + in + [load_field_instr; return_exp] in load_self_instr :: store_instrs -let objc_setter location self_with_typ (var, var_typ) (fieldname, field_typ, _) = +let objc_setter tenv location self_with_typ (var, var_typ) (fieldname, field_typ, _) = let field_exp, load_self_instr = get_load_self_instr location self_with_typ fieldname in let store_instrs = - let id_field = Ident.create_fresh Ident.knormal in - let load_var_instr = - Sil.Load {id= id_field; e= Lvar var; root_typ= var_typ; typ= var_typ; loc= location} - in - let store_exp = - Sil.Store - {e1= field_exp; root_typ= field_typ; typ= field_typ; e2= Exp.Var id_field; loc= location} - in - [load_var_instr; store_exp] + match field_typ with + | {Typ.desc= Tstruct (CStruct _ as struct_name)} -> + CStructCopy.struct_copy tenv location field_exp (Exp.Lvar var) ~typ:field_typ ~struct_name + | _ -> + let id_field = Ident.create_fresh Ident.knormal in + let load_var_instr = + Sil.Load {id= id_field; e= Lvar var; root_typ= var_typ; typ= var_typ; loc= location} + in + let store_exp = + Sil.Store + {e1= field_exp; root_typ= field_typ; typ= field_typ; e2= Exp.Var id_field; loc= location} + in + [load_var_instr; store_exp] in load_self_instr :: store_instrs -let process_getter_setter proc_name proc_desc = +let process_getter_setter tenv proc_name proc_desc = let location = Procdesc.get_loc proc_desc in let formals = Procdesc.get_formals proc_desc in let attributes = Procdesc.get_attributes proc_desc in Ident.NameGenerator.reset () ; let getter_setter_instrs = match (attributes.ProcAttributes.objc_accessor, formals) with - | Some (Objc_getter field), [(self, self_typ)] -> + | Some (Objc_getter field), (self, self_typ) :: _ -> let self_var = Pvar.mk self proc_name in - objc_getter proc_desc location (self_var, self_typ) field + objc_getter tenv proc_desc location (self_var, self_typ) field | Some (Objc_setter field), [(self, self_typ); (var_name, var_typ)] -> let self_var = Pvar.mk self proc_name in let var = Pvar.mk var_name proc_name in - objc_setter location (self_var, self_typ) (var, var_typ) field + objc_setter tenv location (self_var, self_typ) (var, var_typ) field | _ -> [] in @@ -83,4 +94,4 @@ let process_getter_setter proc_name proc_desc = Procdesc.node_set_succs proc_desc getter_setter_node ~normal:[exit_node] ~exn:[] ) -let process cfg = Procname.Hash.iter process_getter_setter cfg +let process cfg tenv = Procname.Hash.iter (process_getter_setter tenv) cfg diff --git a/infer/src/clang/CAddImplicitGettersSetters.mli b/infer/src/clang/CAddImplicitGettersSetters.mli index 7d9426ce0..7a5e7a62a 100644 --- a/infer/src/clang/CAddImplicitGettersSetters.mli +++ b/infer/src/clang/CAddImplicitGettersSetters.mli @@ -7,7 +7,7 @@ open! IStd -val process : Cfg.t -> unit +val process : Cfg.t -> Tenv.t -> unit (** In Objective-C when properties are created in the interface of a class, the compiler creates automatically the instance variable for it and also the getter and setter in the implementation of the class. In the frontend we collect the information about which method is the implicit diff --git a/infer/src/clang/cFrontend.ml b/infer/src/clang/cFrontend.ml index 3a8d36a4c..7c6a01fbd 100644 --- a/infer/src/clang/cFrontend.ml +++ b/infer/src/clang/cFrontend.ml @@ -51,7 +51,7 @@ let do_source_file (translation_unit_context : CFrontend_config.translation_unit "@\n Start building call/cfg graph for '%a'....@\n" SourceFile.pp source_file ; let cfg = compute_icfg translation_unit_context tenv ast in CAddImplicitDeallocImpl.process cfg tenv ; - CAddImplicitGettersSetters.process cfg ; + CAddImplicitGettersSetters.process cfg tenv ; CCallSpecializedWithClosures.process cfg ; L.(debug Capture Verbose) "@\n End building call/cfg graph for '%a'.@\n" SourceFile.pp source_file ; NullabilityPreanalysis.analysis cfg tenv ; diff --git a/infer/src/clang/cMethodSignature.ml b/infer/src/clang/cMethodSignature.ml index 23869a4bb..4c873e911 100644 --- a/infer/src/clang/cMethodSignature.ml +++ b/infer/src/clang/cMethodSignature.ml @@ -48,7 +48,8 @@ type t = (* A method is a getter if it has a link to a property and *) (* it has 0 arguments *) let is_getter {pointer_to_property_opt; params} = - Option.is_some pointer_to_property_opt && Int.equal (List.length params) 0 + Option.is_some pointer_to_property_opt + && match params with [] -> true | [{name}] -> Mangled.is_return_param name | _ -> false (* A method is a setter if it has a link to a property and *) diff --git a/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m b/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m index be788fbee..fa1989d67 100644 --- a/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m +++ b/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m @@ -7,8 +7,14 @@ #import +struct my_struct { + int x; + int y; +}; + @interface A : NSObject @property int x; +@property(nonatomic, readwrite) struct my_struct s; @end @implementation A @@ -17,4 +23,9 @@ return target.x; } ++ (int)getMyStructField:(A*)target my_struct:(struct my_struct)my_struct { + target.s = my_struct; + return target.s.x; +} + @end diff --git a/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m.dot b/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m.dot index b350f6ef8..ecd982df5 100644 --- a/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m.dot +++ b/infer/tests/codetoanalyze/objc/frontend/property/Property_getter.m.dot @@ -1,5 +1,24 @@ /* @generated */ digraph cfg { +"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_1" [label="1: Start A.getMyStructField:my_struct:\nFormals: target:A* my_struct:my_struct\nLocals: 0$?%__sil_tmp__temp_return_n$4:my_struct \n " color=yellow style=filled] + + + "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_1" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" ; +"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_2" [label="2: Exit A.getMyStructField:my_struct: \n " color=yellow style=filled] + + +"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_3" [label="3: Return Stmt \n n$2=*&target:A* [line 28, column 10]\n n$5=_fun_A.s(n$2:A*,&0$?%__sil_tmp__temp_return_n$4:my_struct*) assign_last [line 28, column 17]\n n$6=*&0$?%__sil_tmp__temp_return_n$4.x:int [line 28, column 10]\n " shape="box"] + + + "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_3" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_4" ; +"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_4" [label="4: Return Stmt \n *&return:int=n$6 [line 28, column 3]\n " shape="box"] + + + "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_4" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_2" ; +"getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" [label="5: Message Call: setS: \n n$8=*&target:A* [line 27, column 3]\n n$7=*&my_struct:my_struct [line 27, column 14]\n n$9=_fun_A.setS:(n$8:A*,n$7:my_struct) [line 27, column 10]\n " shape="box"] + + + "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_5" -> "getMyStructField:my_struct:#A(class A)#class.35da25720e80922866aa41dc70b313cb_3" ; "addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_1" [label="1: Start A.addTarget:\nFormals: self:A* target:A*\nLocals: \n " color=yellow style=filled] @@ -7,11 +26,11 @@ digraph cfg { "addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_2" [label="2: Exit A.addTarget: \n " color=yellow style=filled] -"addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_3" [label="3: Return Stmt \n n$0=*&target:A* [line 17, column 10]\n n$1=_fun_A.x(n$0:A*) [line 17, column 17]\n " shape="box"] +"addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_3" [label="3: Return Stmt \n n$0=*&target:A* [line 23, column 10]\n n$1=_fun_A.x(n$0:A*) [line 23, column 17]\n " shape="box"] "addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_3" -> "addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_4" ; -"addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_4" [label="4: Return Stmt \n *&return:int=n$1 [line 17, column 3]\n " shape="box"] +"addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_4" [label="4: Return Stmt \n *&return:int=n$1 [line 23, column 3]\n " shape="box"] "addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_4" -> "addTarget:#A(class A)#instance.ca26ddd02ac11fb266531b38b6edef27_2" ; @@ -26,6 +45,28 @@ digraph cfg { "dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_3" -> "dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_2" ; +"s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_1" [label="1: Start A.s\nFormals: self:A* __return_param:my_struct*\nLocals: \n " color=yellow style=filled] + + + "s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_1" -> "s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_3" ; +"s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_2" [label="2: Exit A.s \n " color=yellow style=filled] + + +"s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_3" [label="3: BinaryOperatorStmt: Node \n n$0=*&self:A* [line 17, column 50]\n n$1=*&__return_param:my_struct [line 17, column 50]\n n$2=*n$0._s.x:int [line 17, column 50]\n *n$1.x:int=n$2 [line 17, column 50]\n n$3=*n$0._s.y:int [line 17, column 50]\n *n$1.y:int=n$3 [line 17, column 50]\n " shape="box"] + + + "s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_3" -> "s#A(struct my_struct)#instance.8b615eca1cde58c02b016bbbf517c211_2" ; +"setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_1" [label="1: Start A.setS:\nFormals: self:A* s:my_struct\nLocals: \n " color=yellow style=filled] + + + "setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_1" -> "setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_3" ; +"setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_2" [label="2: Exit A.setS: \n " color=yellow style=filled] + + +"setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_3" [label="3: BinaryOperatorStmt: Node \n n$0=*&self:A* [line 17, column 50]\n n$1=*&s.x:int [line 17, column 50]\n *n$0._s.x:int=n$1 [line 17, column 50]\n n$2=*&s.y:int [line 17, column 50]\n *n$0._s.y:int=n$2 [line 17, column 50]\n " shape="box"] + + + "setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_3" -> "setS:#A#instance.190e00a9f8a69bd26b1a4e2b795d840c_2" ; "setX:#A#instance.00c5402542b9aade8ca8191be56dcd87_1" [label="1: Start A.setX:\nFormals: self:A* x:int\nLocals: \n " color=yellow style=filled] @@ -33,7 +74,7 @@ digraph cfg { "setX:#A#instance.00c5402542b9aade8ca8191be56dcd87_2" [label="2: Exit A.setX: \n " color=yellow style=filled] -"setX:#A#instance.00c5402542b9aade8ca8191be56dcd87_3" [label="3: BinaryOperatorStmt: Node \n n$0=*&self:A* [line 11, column 15]\n n$1=*&x:int [line 11, column 15]\n *n$0._x:int=n$1 [line 11, column 15]\n " shape="box"] +"setX:#A#instance.00c5402542b9aade8ca8191be56dcd87_3" [label="3: BinaryOperatorStmt: Node \n n$0=*&self:A* [line 16, column 15]\n n$1=*&x:int [line 16, column 15]\n *n$0._x:int=n$1 [line 16, column 15]\n " shape="box"] "setX:#A#instance.00c5402542b9aade8ca8191be56dcd87_3" -> "setX:#A#instance.00c5402542b9aade8ca8191be56dcd87_2" ; @@ -44,7 +85,7 @@ digraph cfg { "x#A#instance.37ea1b3cd5342ae67c7383da2227f91f_2" [label="2: Exit A.x \n " color=yellow style=filled] -"x#A#instance.37ea1b3cd5342ae67c7383da2227f91f_3" [label="3: BinaryOperatorStmt: Node \n n$0=*&self:A* [line 11, column 15]\n n$1=*n$0._x:int [line 11, column 15]\n *&return:int=n$1 [line 11, column 15]\n " shape="box"] +"x#A#instance.37ea1b3cd5342ae67c7383da2227f91f_3" [label="3: BinaryOperatorStmt: Node \n n$0=*&self:A* [line 16, column 15]\n n$1=*n$0._x:int [line 16, column 15]\n *&return:int=n$1 [line 16, column 15]\n " shape="box"] "x#A#instance.37ea1b3cd5342ae67c7383da2227f91f_3" -> "x#A#instance.37ea1b3cd5342ae67c7383da2227f91f_2" ; diff --git a/infer/tests/codetoanalyze/objc/pulse/uninit.m b/infer/tests/codetoanalyze/objc/pulse/uninit.m index ab5dbe252..20874e108 100644 --- a/infer/tests/codetoanalyze/objc/pulse/uninit.m +++ b/infer/tests/codetoanalyze/objc/pulse/uninit.m @@ -6,17 +6,18 @@ */ #import +struct my_struct { + int x; + int y; +}; + @interface Uninit : NSObject @property(nonatomic, assign) int obj_field; +@property(nonatomic, readwrite) struct my_struct s; @end -struct my_struct { - int x; - int y; -}; - @implementation Uninit - (void)capture_in_closure_ok { @@ -115,4 +116,25 @@ dispatch_queue_t queue; return x; } ++ (int)getter_c_struct:(Uninit*)obj { + struct my_struct s = obj.s; // getter is called + return s.x; +} + +- (int)call_getter_c_struct_ok { + Uninit* obj = [Uninit new]; + struct my_struct s; + s.x = 1; + s.y = 2; + obj.s = s; // setter is called + return [Uninit getter_c_struct:obj]; +} + +/* FN due to the frontend is incorrect when passing C struct value as a function + paraemeter when calling C struct setter. */ ++ (void)call_setter_c_struct_bad_FN:(Uninit*)obj { + struct my_struct s; + obj.s = s; // setter is called +} + @end