[closure] Substitute closure parameter when given via variable

Summary:
This diff substitutes closure parameter when it is given via variable.  For example,

```
x = ^{ ... }
foo(x)
```

this diff substitutes the closure variable `x`,

```
x = ^{ ... }
foo(^{ ... })
```

so that the specialization of `foo` can be done by `CCallSpecializaedWithClosures.process`.

Reviewed By: jvillard

Differential Revision: D23814595

fbshipit-source-id: a89f1530f
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent 6678455838
commit ead975f193

@ -61,3 +61,6 @@ let pp_proc_signatures fmt cfg =
F.fprintf fmt "@[<v>METHOD SIGNATURES@;" ;
iter_over_sorted_procs ~f:(Procdesc.pp_signature fmt) cfg ;
F.fprintf fmt "@]"
let mem = Procname.Hash.mem

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

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

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

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

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

@ -163,6 +163,14 @@ let replace_with_specialize_methods cfg _node instr =
; method_annotation= {annot with params= new_annots}
; proc_name= specialized_pname }
in
(* 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 ->
@ -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

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

@ -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, []

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

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

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

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

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

@ -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<codetoanalyze/objc/shared/block/dispatch.m>$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<codetoanalyze/objc/shared/block/dispatch.m>$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<codetoanalyze/objc/shared/block/dispatch.m>$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<codetoanalyze/objc/shared/block/dispatch.m>$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" ;

Loading…
Cancel
Save