diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index 248603f14..708f2549f 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -61,3 +61,6 @@ let pp_proc_signatures fmt cfg = F.fprintf fmt "@[METHOD SIGNATURES@;" ; iter_over_sorted_procs ~f:(Procdesc.pp_signature fmt) cfg ; F.fprintf fmt "@]" + + +let mem = Procname.Hash.mem diff --git a/infer/src/IR/Cfg.mli b/infer/src/IR/Cfg.mli index 362eae7b5..95a88e71f 100644 --- a/infer/src/IR/Cfg.mli +++ b/infer/src/IR/Cfg.mli @@ -20,6 +20,8 @@ val store : SourceFile.t -> t -> unit (** Save the individual [Procdesc.t] and [ProcAttributes.t] to the database for the procedures in the cfg. *) +val mem : t -> Procname.t -> bool + (** {2 Functions for manipulating an interprocedural CFG} *) val create : unit -> t diff --git a/infer/src/IR/Exp.ml b/infer/src/IR/Exp.ml index 2fb6bcccf..6c2487cb7 100644 --- a/infer/src/IR/Exp.ml +++ b/infer/src/IR/Exp.ml @@ -225,12 +225,8 @@ let rec pp_ pe pp_t f e = F.fprintf f "(%a %s %a)" pp_exp e1 (Binop.str pe op) pp_exp e2 | Exn e -> F.fprintf f "EXN %a" pp_exp e - | Closure {name; captured_vars} -> - if List.is_empty captured_vars then F.fprintf f "(%a)" pp_exp (Const (Cfun name)) - else - F.fprintf f "(%a,%a)" pp_exp (Const (Cfun name)) - (Pp.comma_seq (pp_captured_var pe pp_t)) - captured_vars + | Closure closure -> + pp_closure_ pe pp_t f closure | Lvar pv -> Pvar.pp pe f pv | Lfield (e, fld, _) -> @@ -259,6 +255,15 @@ and pp_captured_var pe pp_t f (exp, var, typ, mode) = (pp_ pe pp_t) exp (Pvar.pp pe) var (Typ.pp pe) typ +and pp_closure_ pe pp_t f {name; captured_vars} = + let pp_exp = pp_ pe pp_t in + if List.is_empty captured_vars then F.fprintf f "(%a)" pp_exp (Const (Cfun name)) + else + F.fprintf f "(%a,%a)" pp_exp (Const (Cfun name)) + (Pp.comma_seq (pp_captured_var pe pp_t)) + captured_vars + + let pp_printenv ~print_types pe f e = let pp_typ = if print_types then Typ.pp_full else Typ.pp in pp_ pe (pp_typ pe) f e @@ -266,6 +271,8 @@ let pp_printenv ~print_types pe f e = let pp f e = pp_printenv ~print_types:false Pp.text f e +let pp_closure = pp_closure_ Pp.text (Typ.pp Pp.text) + let to_string e = F.asprintf "%a" pp e let color_wrapper ~f = if Config.print_using_diff then Pp.color_wrapper ~f else f diff --git a/infer/src/IR/Exp.mli b/infer/src/IR/Exp.mli index c343a73fe..eb744c8a5 100644 --- a/infer/src/IR/Exp.mli +++ b/infer/src/IR/Exp.mli @@ -142,6 +142,8 @@ val pp_diff : ?print_types:bool -> Pp.env -> F.formatter -> t -> unit val pp : F.formatter -> t -> unit +val pp_closure : F.formatter -> closure -> unit + val to_string : t -> string val d_exp : t -> unit diff --git a/infer/src/backend/ClosuresSubstitution.ml b/infer/src/backend/ClosuresSubstitution.ml index f37da21c4..52d6e06fc 100644 --- a/infer/src/backend/ClosuresSubstitution.ml +++ b/infer/src/backend/ClosuresSubstitution.ml @@ -83,9 +83,9 @@ let get_invariant_at_node (map : Analyzer.invariant_map) node : Domain.t = Domain.bottom -let replace_instr node (astate : Domain.t) (instr : Sil.instr) : Sil.instr = +let replace_closure_call node (astate : Domain.t) (instr : Sil.instr) : Sil.instr = let kind = `ExecNode in - let pp_name fmt = Format.pp_print_string fmt "Closure Substitution" in + let pp_name fmt = Format.pp_print_string fmt "Closure Call Substitution" in NodePrinter.with_session (CFG.Node.underlying_node node) ~kind ~pp_name ~f:(fun () -> match instr with | Call (ret_id_typ, Var id, actual_params, loc, call_flags) -> ( @@ -110,8 +110,39 @@ let replace_instr node (astate : Domain.t) (instr : Sil.instr) : Sil.instr = instr ) -let process summary = - let pdesc = Summary.get_proc_desc summary in +(** [replace_closure_param] propagates closures to function parameters, so that more functions are + specialized by [CCallSpecializedWithClosures.process]. Note that unlike [replace_closure_call] + running at the analysis phase, [replace_closure_param] should run before + [CCallSpecializedWithClosures.process] at the capture phase. *) +let replace_closure_param node (astate : Domain.t) (instr : Sil.instr) : Sil.instr = + let kind = `ExecNode in + let pp_name fmt = Format.pp_print_string fmt "Closure Param Substitution" in + let replace () = + match instr with + | Sil.Call (ret, func, actual_params, loc, flags) -> + let modified = ref false in + let replace_param ((exp, typ) as param) = + match exp with + | Exp.Closure _ -> + param + | _ -> ( + match VDom.get (eval_expr astate exp) with + | None -> + param + | Some c -> + L.d_printfln "found closure %a for parameter %a\n" Exp.pp_closure c Exp.pp exp ; + modified := true ; + (Exp.Closure c, typ) ) + in + let actual_params' = List.map actual_params ~f:replace_param in + if !modified then Sil.Call (ret, func, actual_params', loc, flags) else instr + | _ -> + instr + in + NodePrinter.with_session (CFG.Node.underlying_node node) ~kind ~pp_name ~f:replace + + +let process_common replace_instr pdesc = let node_cfg = CFG.from_pdesc pdesc in let map = Analyzer.exec_cfg node_cfg ~initial:Domain.empty () in let update_context = eval_instr in @@ -120,3 +151,11 @@ let process summary = Procdesc.replace_instrs_using_context pdesc ~f:replace_instr ~update_context ~context_at_node in () + + +let process_closure_call summary = + let pdesc = Summary.get_proc_desc summary in + process_common replace_closure_call pdesc + + +let process_closure_param pdesc = process_common replace_closure_param pdesc diff --git a/infer/src/backend/preanal.ml b/infer/src/backend/preanal.ml index 226081195..8b3d3a573 100644 --- a/infer/src/backend/preanal.ml +++ b/infer/src/backend/preanal.ml @@ -375,7 +375,7 @@ let do_preanalysis exe_env pdesc = FunctionPointers.substitute pdesc ; (* NOTE: It is important that this preanalysis stays before Liveness *) if not (Procname.is_java proc_name) then ( - ClosuresSubstitution.process summary ; + ClosuresSubstitution.process_closure_call summary ; ClosureSubstSpecializedMethod.process summary ) ; Liveness.process summary tenv ; AddAbstractionInstructions.process pdesc ; diff --git a/infer/src/clang/CCallSpecializedWithClosures.ml b/infer/src/clang/CCallSpecializedWithClosures.ml index 4ad55eeda..d510ccbe8 100644 --- a/infer/src/clang/CCallSpecializedWithClosures.ml +++ b/infer/src/clang/CCallSpecializedWithClosures.ml @@ -163,7 +163,15 @@ let replace_with_specialize_methods cfg _node instr = ; method_annotation= {annot with params= new_annots} ; proc_name= specialized_pname } in - Cfg.create_proc_desc cfg new_attributes |> ignore ; + (* To avoid duplicated additions on a specialized procname, it does a membership check. + This may happen when there are multiple function calls with the same callees and the + same closure parameters. For the following additions, we can simply ignore them, + because the function bodies of the same procname must be the same. + + Here, it adds an empty procdesc temporarily. The function body will be filled later + by [ClosureSubstSpecializedMethod]. *) + if not (Cfg.mem cfg specialized_pname) then + Cfg.create_proc_desc cfg new_attributes |> ignore ; Sil.Call (ret, Exp.Const (Const.Cfun specialized_pname), new_actuals, loc, flags) | None -> instr ) @@ -175,6 +183,7 @@ let replace_with_specialize_methods cfg _node instr = let process cfg = let process_pdesc _proc_name proc_desc = + ClosuresSubstitution.process_closure_param proc_desc ; Procdesc.replace_instrs proc_desc ~f:(replace_with_specialize_methods cfg) |> ignore in Procname.Hash.iter process_pdesc cfg diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m b/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m index 9ef0d7145..222a619cc 100644 --- a/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m @@ -20,4 +20,13 @@ }]; } ++ (void)callIndexOfObjectPassingTest_param_linear:(NSArray*)x { + BOOL (^b)(NSObject*, NSUInteger, BOOL*) = + ^BOOL(NSObject* obj, NSUInteger idx, BOOL* stop) { + NoArcCallee* o = [NoArcCallee giveMeObject]; + return false; + }; + int i = [x indexOfObjectPassingTest:b]; +} + @end diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp index 858d8dca2..9d50c254a 100644 --- a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp @@ -1,7 +1,10 @@ ${XCODE_ISYSROOT}/System/Library/Frameworks/Foundation.framework/Headers/NSArray.h, NSArray.indexOfObjectPassingTest:[objc_blockArcBlock.callIndexOfObjectPassingTest_linear:_1], 0, OnUIThread:false, [] +${XCODE_ISYSROOT}/System/Library/Frameworks/Foundation.framework/Headers/NSArray.h, NSArray.indexOfObjectPassingTest:[objc_blockArcBlock.callIndexOfObjectPassingTest_param_linear:_2], 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_linear:, x->elements.length.ub, OnUIThread:false, [{x->elements.length.ub},Modeled call to indexOfObjectPassingTest:,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] +codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_param_linear:, x->elements.length.ub, OnUIThread:false, [{x->elements.length.ub},Modeled call to indexOfObjectPassingTest:,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.dealloc, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_block.m, objc_blockArcBlock.callIndexOfObjectPassingTest_linear:_1, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] +codetoanalyze/objc/autoreleasepool/arc_block.m, objc_blockArcBlock.callIndexOfObjectPassingTest_param_linear:_2, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.allocObject, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.copyObject:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.dealloc, 0, OnUIThread:false, [] diff --git a/infer/tests/codetoanalyze/objc/biabduction/issues.exp b/infer/tests/codetoanalyze/objc/biabduction/issues.exp index e52d6934a..16d267cbf 100644 --- a/infer/tests/codetoanalyze/objc/biabduction/issues.exp +++ b/infer/tests/codetoanalyze/objc/biabduction/issues.exp @@ -106,5 +106,5 @@ codetoanalyze/objc/shared/annotations/nonnull_annotations.m, NonnullAnnot.test3: codetoanalyze/objc/shared/annotations/nullable_annotations.m, User.otherUserName, 2, NULL_DEREFERENCE, B2, ERROR, [start of procedure otherUserName,Skipping otherUser: method has no implementation] codetoanalyze/objc/shared/annotations/nullable_annotations.m, npe_property_nullable, 3, NULL_DEREFERENCE, B1, ERROR, [start of procedure npe_property_nullable(),Skipping child: method has no implementation] codetoanalyze/objc/shared/annotations/nullable_annotations_fields.m, A.nullable_field, 3, NULL_DEREFERENCE, B1, ERROR, [start of procedure nullable_field,Skipping getA(): method has no implementation] -codetoanalyze/objc/shared/block/dispatch.m, DispatchA.dispatch_a_block_variable_from_macro_delivers_initialised_object, 3, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure dispatch_a_block_variable_from_macro_delivers_initialised_object,start of procedure dispatch_a_block_variable_from_macro,Skipping _dispatch_once(): method has no implementation,return from a call to DispatchA.dispatch_a_block_variable_from_macro] +codetoanalyze/objc/shared/block/dispatch.m, DispatchA.dispatch_a_block_variable_from_macro_delivers_initialised_object, 3, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure dispatch_a_block_variable_from_macro_delivers_initialised_object,start of procedure dispatch_a_block_variable_from_macro,start of procedure block,start of procedure init,return from a call to DispatchA.init,return from a call to objc_blockDispatchA.dispatch_a_block_variable_from_macro_5,return from a call to DispatchA.dispatch_a_block_variable_from_macro] codetoanalyze/objc/shared/npe/Available_expr.m, Available_expr.test_no_bug, 3, NULL_DEREFERENCE, B1, ERROR, [start of procedure test_no_bug,Taking true branch] diff --git a/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m b/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m index 36b7a7e8a..149e61b85 100644 --- a/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m +++ b/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m @@ -47,4 +47,13 @@ foo(_Nullable MyBlock my_block1, _Nullable MyBlock my_block2, _Nonnull A* a) { return self->x; } +- (int)call_foo_with_same_param { + void (^b)(int) = ^(int i) { + self->x = 5; + }; + foo(b, b, self); + foo(b, b, self); + return self->x; +} + @end diff --git a/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot b/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot index c3f610c50..e864236d0 100644 --- a/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot +++ b/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot @@ -66,6 +66,17 @@ digraph cfg { "objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_3" -> "objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_2" ; +"objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_1" [label="1: Start objc_blockA.call_foo_with_same_param_5\nFormals: self:A* i:int\nLocals: \nCaptured: [by ref]self:A* \n " color=yellow style=filled] + + + "objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_1" -> "objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_3" ; +"objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_2" [label="2: Exit objc_blockA.call_foo_with_same_param_5 \n " color=yellow style=filled] + + +"objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_3" [label="3: BinaryOperatorStmt: Assign \n n$31=*&self:A* [line 52, column 5]\n *n$31.x:int=5 [line 52, column 5]\n " shape="box"] + + + "objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_3" -> "objc_blockA.call_foo_with_same_param_5(class A).03c7f776ebd8253258577359808dec16_2" ; "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_1" [label="1: Start A.bar2\nFormals: self:A*\nLocals: \n " color=yellow style=filled] @@ -104,6 +115,29 @@ digraph cfg { "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_6" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" ; +"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_1" [label="1: Start A.call_foo_with_same_param\nFormals: self:A*\nLocals: b:_fn_(*) \n " color=yellow style=filled] + + + "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_1" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_6" ; +"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_2" [label="2: Exit A.call_foo_with_same_param \n " color=yellow style=filled] + + +"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_3" [label="3: Return Stmt \n n$20=*&self:A* [line 56, column 10]\n n$21=*n$20.x:int [line 56, column 10]\n *&return:int=n$21 [line 56, column 3]\n " shape="box"] + + + "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_3" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_2" ; +"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" [label="4: Call _fun_foo \n n$22=*&b:_fn_(*) [line 55, column 7]\n n$23=*&b:_fn_(*) [line 55, column 10]\n n$24=*&self:A* [line 55, column 13]\n n$25=_fun_foo[objc_blockA.call_foo_with_same_param_5^objc_blockA.call_foo_with_same_param_5](n$30:A*,n$24:A*) [line 55, column 3]\n " shape="box"] + + + "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_3" ; +"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_5" [label="5: Call _fun_foo \n n$26=*&b:_fn_(*) [line 54, column 7]\n n$27=*&b:_fn_(*) [line 54, column 10]\n n$28=*&self:A* [line 54, column 13]\n n$29=_fun_foo[objc_blockA.call_foo_with_same_param_5^objc_blockA.call_foo_with_same_param_5](n$30:A*,n$28:A*) [line 54, column 3]\n " shape="box"] + + + "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_5" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" ; +"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_6" [label="6: DeclStmt \n VARIABLE_DECLARED(b:_fn_(*)); [line 51, column 3]\n n$30=*&self:A* [line 51, column 20]\n *&b:_fn_(*)=(_fun_objc_blockA.call_foo_with_same_param_5,([by ref]n$30 &self:A*)) [line 51, column 3]\n " shape="box"] + + + "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_6" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_5" ; "dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_1" [label="1: Start A.dealloc\nFormals: self:A*\nLocals: \n " color=yellow style=filled] diff --git a/infer/tests/codetoanalyze/objc/performance/block.m b/infer/tests/codetoanalyze/objc/performance/block.m index 3771d9c65..f95ade3d3 100644 --- a/infer/tests/codetoanalyze/objc/performance/block.m +++ b/infer/tests/codetoanalyze/objc/performance/block.m @@ -26,7 +26,7 @@ void loop_linear(int x) { void runBlockA(BlockA block) { block(); } -void doBlockA_linear_FN(int a) { +void doBlockA_linear(int a) { BlockA block = ^{ loop_linear(a); }; diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index 1bdfb0c75..538e3bdd2 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -85,13 +85,14 @@ codetoanalyze/objc/performance/araii.m, Araii.setBuffer:, 4, OnUIThread:false, codetoanalyze/objc/performance/araii.m, memory_leak_raii_main, 18, OnUIThread:false, [] codetoanalyze/objc/performance/block.m, block_multiply_array_linear, 13 + 5 ⋅ array->elements.length.ub + 4 ⋅ (array->elements.length.ub + 1), OnUIThread:false, [{array->elements.length.ub + 1},Call to objc_blockblock_multiply_array_linear_1,Loop,{array->elements.length.ub},Call to objc_blockblock_multiply_array_linear_1,Loop] codetoanalyze/objc/performance/block.m, doBlockA_direct_block_linear, 10 + 3 ⋅ a + 2 ⋅ (1+max(0, a)), OnUIThread:false, [{1+max(0, a)},Call to runBlockA[objc_blockdoBlockA_direct_block_linear_3],Call to objc_blockdoBlockA_direct_block_linear_3,Call to loop_linear,Loop,{a},Call to runBlockA[objc_blockdoBlockA_direct_block_linear_3],Call to objc_blockdoBlockA_direct_block_linear_3,Call to loop_linear,Loop] -codetoanalyze/objc/performance/block.m, doBlockA_linear_FN, 7, OnUIThread:false, [] +codetoanalyze/objc/performance/block.m, doBlockA_linear, 12 + 3 ⋅ a + 2 ⋅ (1+max(0, a)), OnUIThread:false, [{1+max(0, a)},Call to runBlockA[objc_blockdoBlockA_linear_2],Call to objc_blockdoBlockA_linear_2,Call to loop_linear,Loop,{a},Call to runBlockA[objc_blockdoBlockA_linear_2],Call to objc_blockdoBlockA_linear_2,Call to loop_linear,Loop] codetoanalyze/objc/performance/block.m, loop_linear, 3 + 3 ⋅ x + 2 ⋅ (1+max(0, x)), OnUIThread:false, [{1+max(0, x)},Loop,{x},Loop] codetoanalyze/objc/performance/block.m, objc_blockblock_multiply_array_linear_1, 8 + 5 ⋅ array->elements.length.ub + 4 ⋅ (array->elements.length.ub + 1), OnUIThread:false, [{array->elements.length.ub + 1},Loop,{array->elements.length.ub},Loop] codetoanalyze/objc/performance/block.m, objc_blockdoBlockA_direct_block_linear_3, 5 + 3 ⋅ a + 2 ⋅ (1+max(0, a)), OnUIThread:false, [{1+max(0, a)},Call to loop_linear,Loop,{a},Call to loop_linear,Loop] -codetoanalyze/objc/performance/block.m, objc_blockdoBlockA_linear_FN_2, 5 + 3 ⋅ a + 2 ⋅ (1+max(0, a)), OnUIThread:false, [{1+max(0, a)},Call to loop_linear,Loop,{a},Call to loop_linear,Loop] +codetoanalyze/objc/performance/block.m, objc_blockdoBlockA_linear_2, 5 + 3 ⋅ a + 2 ⋅ (1+max(0, a)), OnUIThread:false, [{1+max(0, a)},Call to loop_linear,Loop,{a},Call to loop_linear,Loop] codetoanalyze/objc/performance/block.m, runBlockA, 3, OnUIThread:false, [] codetoanalyze/objc/performance/block.m, runBlockA[objc_blockdoBlockA_direct_block_linear_3], 8 + 3 ⋅ a[doBlockA_direct_block_linear()] + 2 ⋅ (1+max(0, a[doBlockA_direct_block_linear()])), OnUIThread:false, [{1+max(0, a[doBlockA_direct_block_linear()])},Call to objc_blockdoBlockA_direct_block_linear_3,Call to loop_linear,Loop,{a[doBlockA_direct_block_linear()]},Call to objc_blockdoBlockA_direct_block_linear_3,Call to loop_linear,Loop] +codetoanalyze/objc/performance/block.m, runBlockA[objc_blockdoBlockA_linear_2], 8 + 3 ⋅ a[doBlockA_linear()] + 2 ⋅ (1+max(0, a[doBlockA_linear()])), OnUIThread:false, [{1+max(0, a[doBlockA_linear()])},Call to objc_blockdoBlockA_linear_2,Call to loop_linear,Loop,{a[doBlockA_linear()]},Call to objc_blockdoBlockA_linear_2,Call to loop_linear,Loop] codetoanalyze/objc/performance/break.m, break_constant_FP, 8 + 5 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Call to break_loop,Loop,{p},Call to break_loop,Loop] codetoanalyze/objc/performance/break.m, break_loop, 5 + 5 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop,{p},Loop] codetoanalyze/objc/performance/break.m, break_loop_with_t, 7 + 5 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop,{p},Loop] diff --git a/infer/tests/codetoanalyze/objc/shared/block/dispatch.m.dot b/infer/tests/codetoanalyze/objc/shared/block/dispatch.m.dot index 06f122bb4..3cc8c656d 100644 --- a/infer/tests/codetoanalyze/objc/shared/block/dispatch.m.dot +++ b/infer/tests/codetoanalyze/objc/shared/block/dispatch.m.dot @@ -129,7 +129,7 @@ digraph cfg { "dispatch_a_block_variable#DispatchA#class.3cc12dd22127281b8293b7c046d21bb2_3" -> "dispatch_a_block_variable#DispatchA#class.3cc12dd22127281b8293b7c046d21bb2_2" ; -"dispatch_a_block_variable#DispatchA#class.3cc12dd22127281b8293b7c046d21bb2_4" [label="4: Call _fun__dispatch_once \n n$18=*&initialization_block__:_fn_(*) [line 58, column 32]\n n$19=_fun__dispatch_once(&#GB$DispatchA.dispatch_a_block_variable.once_token__:long*,n$18:_fn_(*)) [line 58, column 3]\n " shape="box"] +"dispatch_a_block_variable#DispatchA#class.3cc12dd22127281b8293b7c046d21bb2_4" [label="4: Call _fun__dispatch_once \n n$18=*&initialization_block__:_fn_(*) [line 58, column 32]\n n$19=_fun__dispatch_once(&#GB$DispatchA.dispatch_a_block_variable.once_token__:long*,(_fun_objc_blockDispatchA.dispatch_a_block_variable_4):_fn_(*)) [line 58, column 3]\n " shape="box"] "dispatch_a_block_variable#DispatchA#class.3cc12dd22127281b8293b7c046d21bb2_4" -> "dispatch_a_block_variable#DispatchA#class.3cc12dd22127281b8293b7c046d21bb2_3" ; @@ -148,7 +148,7 @@ digraph cfg { "dispatch_a_block_variable_from_macro#DispatchA#class.92567a38d5ab3cf637f72030b1097441_3" -> "dispatch_a_block_variable_from_macro#DispatchA#class.92567a38d5ab3cf637f72030b1097441_6" ; -"dispatch_a_block_variable_from_macro#DispatchA#class.92567a38d5ab3cf637f72030b1097441_4" [label="4: Call _fun__dispatch_once \n n$23=*&initialization_block__:_fn_(*) [line 69, column 34]\n n$24=_fun__dispatch_once(&#GB$DispatchA.dispatch_a_block_variable_from_macro.once_token__:long*,n$23:_fn_(*)) [line 69, column 5]\n " shape="box"] +"dispatch_a_block_variable_from_macro#DispatchA#class.92567a38d5ab3cf637f72030b1097441_4" [label="4: Call _fun__dispatch_once \n n$23=*&initialization_block__:_fn_(*) [line 69, column 34]\n n$24=_fun__dispatch_once(&#GB$DispatchA.dispatch_a_block_variable_from_macro.once_token__:long*,(_fun_objc_blockDispatchA.dispatch_a_block_variable_from_macro_5):_fn_(*)) [line 69, column 5]\n " shape="box"] "dispatch_a_block_variable_from_macro#DispatchA#class.92567a38d5ab3cf637f72030b1097441_4" -> "dispatch_a_block_variable_from_macro#DispatchA#class.92567a38d5ab3cf637f72030b1097441_3" ;