[closures preanalysis] [1/n] Adding first step of preanalysis to specialize methods with concrete closures they are called with

Summary:
This preanalysis in general aims to create specialized clones of methods that have blocks as arguments and that are called with concrete closures, and then call these clone methods instead of the original ones.

One complication is what happens with the captured variables in the closure. What we do is we add them to the formals of the cloned method and passed them through to the concrete blocks.

We do this transformation in two steps:
1. Go through all the callers of methods with blocks as parameters, and create the clone methods. In this preanalysis we only create the attributes for the new method, not the code. We also update the call instructions in the callers to represent a call to the cloned method with updated arguments: we don't need to pass closures arguments anymore, we instead pass the captured variables as new arguments.
2. We add the corresponding code to the newly created clones: this means swapping the call to the block variable with a call to the corresponding block.  Moreover, we add some of the new formals (that correspond to the captured variables) to the arguments of the call.

This diff implements step 1 of the analysis.  The next diff D23216021 implements step 2.

Reviewed By: ngorogiannis

Differential Revision: D23109204

fbshipit-source-id: 91a5eb16b
master
Dulma Churchill 4 years ago committed by Facebook GitHub Bot
parent 0a4af7754d
commit 892b16b8c0

@ -48,10 +48,14 @@ let rename ~f {plain; mangled} =
{plain; mangled}
module Set = Caml.Set.Make (struct
module Set = PrettyPrintable.MakePPSet (struct
type nonrec t = t [@@deriving compare]
let pp = pp
end)
module Map = Caml.Map.Make (struct
module Map = PrettyPrintable.MakePPMap (struct
type nonrec t = t [@@deriving compare]
let pp = pp
end)

@ -43,7 +43,7 @@ val rename : f:(string -> string) -> t -> t
(** Maps over both the plain and the mangled components. *)
(** Set of Mangled. *)
module Set : Caml.Set.S with type elt = t
module Set : PrettyPrintable.PPSet with type elt = t
(** Map with Mangled as key *)
module Map : Caml.Map.S with type key = t
module Map : PrettyPrintable.PPMap with type key = t

@ -38,6 +38,11 @@ let pp_var_data fmt {name; typ; modify_in_block; is_declared_unused} =
Mangled.pp name (Typ.pp_full Pp.text) typ modify_in_block is_declared_unused
type specialized_with_blocks_info =
{ orig_proc: Procname.t
; formals_to_procs_and_new_formals: (Procname.t * (Mangled.t * Typ.t) list) Mangled.Map.t }
[@@deriving compare]
type t =
{ access: PredSymb.access (** visibility access *)
; captured: (Mangled.t * Typ.t * Pvar.capture_mode) list
@ -58,6 +63,10 @@ type t =
; is_synthetic_method: bool (** the procedure is a synthetic method *)
; is_variadic: bool (** the procedure is variadic, only supported for Clang procedures *)
; sentinel_attr: (int * int) option (** __attribute__((sentinel(int, int))) *)
; specialized_with_blocks_info: specialized_with_blocks_info option
(** the procedure is a clone specialized with calls to concrete closures, with link to the
original procedure, and a map that links the original formals to the elements of the
closure used to specialize the procedure. *)
; clang_method_kind: ClangMethodKind.t (** the kind of method the procedure is *)
; loc: Location.t (** location of this procedure in the source code *)
; translation_unit: SourceFile.t (** translation unit to which the procedure belongs *)
@ -102,6 +111,7 @@ let default translation_unit proc_name =
; passed_as_noescape_block_to= None
; is_no_return= false
; is_specialized= false
; specialized_with_blocks_info= None
; is_synthetic_method= false
; is_variadic= false
; sentinel_attr= None
@ -120,6 +130,16 @@ let pp_parameters =
Pp.semicolon_seq ~print_env:Pp.text_break (Pp.pair ~fst:Mangled.pp ~snd:(Typ.pp_full Pp.text))
let pp_specialized_with_blocks_info fmt info =
let pp_new_formal fmt el =
F.fprintf fmt "%a:%a" Mangled.pp (fst el) (Typ.pp_full Pp.text) (snd el)
in
let pp_new_formals = Pp.semicolon_seq ~print_env:Pp.text_break pp_new_formal in
F.fprintf fmt "orig_procname=%a, formals_to_procs_and_new_formals=%a" Procname.pp info.orig_proc
(Mangled.Map.pp ~pp_value:(Pp.pair ~fst:Procname.pp ~snd:pp_new_formals))
info.formals_to_procs_and_new_formals
let pp_captured_var fmt (var, typ, mode) =
F.fprintf fmt "(%a,@,%a,@,%s)" Mangled.pp var (Typ.pp_full Pp.text) typ
(Pvar.string_of_capture_mode mode)
@ -141,6 +161,7 @@ let pp f
; passed_as_noescape_block_to
; is_no_return
; is_specialized
; specialized_with_blocks_info
; is_synthetic_method
; is_variadic
; sentinel_attr
@ -188,6 +209,14 @@ let pp f
passed_as_noescape_block_to ;
pp_bool_default ~default:default.is_no_return "is_no_return" is_no_return f () ;
pp_bool_default ~default:default.is_specialized "is_specialized" is_specialized f () ;
if
not
([%compare.equal: specialized_with_blocks_info option] default.specialized_with_blocks_info
specialized_with_blocks_info)
then
F.fprintf f "; specialized_with_blocks_info %a@,"
(Pp.option pp_specialized_with_blocks_info)
specialized_with_blocks_info ;
pp_bool_default ~default:default.is_synthetic_method "is_synthetic_method" is_synthetic_method f
() ;
pp_bool_default ~default:default.is_variadic "is_variadic" is_variadic f () ;

@ -20,6 +20,11 @@ type var_data =
; is_constexpr: bool
; is_declared_unused: bool (** variable declared with attribute [unused] *) }
type specialized_with_blocks_info =
{ orig_proc: Procname.t
; formals_to_procs_and_new_formals: (Procname.t * (Mangled.t * Typ.t) list) Mangled.Map.t }
[@@deriving compare]
type t =
{ access: PredSymb.access (** visibility access *)
; captured: (Mangled.t * Typ.t * Pvar.capture_mode) list
@ -40,6 +45,10 @@ type t =
; is_synthetic_method: bool (** the procedure is a synthetic method *)
; is_variadic: bool (** the procedure is variadic, only supported for Clang procedures *)
; sentinel_attr: (int * int) option (** __attribute__((sentinel(int, int))) *)
; specialized_with_blocks_info: specialized_with_blocks_info option
(** the procedure is a clone specialized with calls to concrete closures, with link to the
original procedure, and a map that links the original formals to the elements of the
closure used to specialize the procedure. *)
; clang_method_kind: ClangMethodKind.t (** the kind of method the procedure is *)
; loc: Location.t (** location of this procedure in the source code *)
; translation_unit: SourceFile.t (** source file where the procedure was captured *)

@ -553,6 +553,13 @@ let get_global_name_of_initializer = function
None
let pp_with_block_parameters pp fmt base blocks =
pp fmt base ;
F.pp_print_string fmt "[" ;
Pp.seq ~sep:"^" F.pp_print_string fmt blocks ;
F.pp_print_string fmt "]"
(** Very verbose representation of an existing Procname.t *)
let rec pp_unique_id fmt = function
| Java j ->
@ -566,9 +573,7 @@ let rec pp_unique_id fmt = function
| WithBlockParameters (base, []) ->
pp_unique_id fmt base
| WithBlockParameters (base, (_ :: _ as blocks)) ->
pp_unique_id fmt base ;
F.pp_print_string fmt "_" ;
Pp.seq ~sep:"_" F.pp_print_string fmt blocks
pp_with_block_parameters pp_unique_id fmt base blocks
| Linters_dummy_method ->
F.pp_print_string fmt "Linters_dummy_method"
@ -588,9 +593,7 @@ let rec pp fmt = function
| WithBlockParameters (base, []) ->
pp fmt base
| WithBlockParameters (base, (_ :: _ as blocks)) ->
pp fmt base ;
F.pp_print_string fmt "_" ;
Pp.seq ~sep:"_" F.pp_print_string fmt blocks
pp_with_block_parameters pp fmt base blocks
| Linters_dummy_method ->
pp_unique_id fmt Linters_dummy_method

@ -35,10 +35,13 @@ type pvar_kind =
(** Names for program variables. *)
type t = {pv_hash: int; pv_name: Mangled.t; pv_kind: pvar_kind} [@@deriving compare]
let get_name_of_local_with_procname var =
let build_formal_from_pvar var =
match var.pv_kind with
| Local_var pname ->
Mangled.from_string (F.asprintf "%s_%a" (Mangled.to_string var.pv_name) Procname.pp pname)
Mangled.from_string
(F.asprintf "%s[%a]" (Mangled.to_string var.pv_name)
(Procname.pp_simplified_string ~withclass:false)
pname)
| _ ->
var.pv_name

@ -161,9 +161,9 @@ val is_pod : t -> bool
val get_initializer_pname : t -> Procname.t option
(** Get the procname of the initializer function for the given global variable *)
val get_name_of_local_with_procname : t -> Mangled.t
(** [get_name_of_local_with_procname var] Return a name that is composed of the name of var and the
name of the procname in case of locals *)
val build_formal_from_pvar : t -> Mangled.t
(** [build_formal_from_pvar var] Return a name that is composed of the name of var (and the name of
the procname in case of locals) *)
val materialized_cpp_temporary : string

@ -307,7 +307,7 @@ let with_block_args callee_pdesc pname_with_block_args block_args =
~f:(fun (_, var, typ, _) ->
(* Here we create fresh names for the new formals, based on the names of the captured
variables annotated with the name of the caller method *)
(Pvar.get_name_of_local_with_procname var, typ) )
(Pvar.build_formal_from_pvar var, typ) )
cl.captured_vars
in
Mangled.Map.add param_name (cl.name, formals_from_captured) subts

@ -0,0 +1,172 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
type formal_annot = {formal_type: Mangled.t * Typ.t; formal_annot: Annot.Item.t}
[@@deriving compare]
type formal_actual = {formal: formal_annot; actual: Exp.t * Typ.t [@ignore]} [@@deriving compare]
let pp_formal_annot fmt formal =
F.fprintf fmt "(formal_type=%a, formal_annot=%a)"
(Pp.pair ~fst:Mangled.pp ~snd:(Typ.pp Pp.text))
formal.formal_type Annot.Item.pp formal.formal_annot
let pp_formal_actual fmt formal_actual =
F.fprintf fmt "(formal=%a, actual=%a)" pp_formal_annot formal_actual.formal
(Pp.pair ~fst:Exp.pp ~snd:(Typ.pp Pp.text))
formal_actual.actual
module FormalsMap = PrettyPrintable.MakePPMap (struct
type t = formal_annot [@@deriving compare]
let pp = pp_formal_annot
end)
module FormalsActualsSet = PrettyPrintable.MakePPSet (struct
type t = formal_actual [@@deriving compare]
let pp = pp_formal_actual
end)
let formals_actuals_map formals annotations actual_params =
let rec formals_actuals_map_inner acc formals annotations actual_params =
match (formals, annotations, actual_params) with
| [], [], [] ->
Some acc
| fml :: fmls, an :: ans, act :: acts ->
let acc = FormalsMap.add {formal_type= fml; formal_annot= an} act acc in
formals_actuals_map_inner acc fmls ans acts
| _, _, _ ->
None
in
formals_actuals_map_inner FormalsMap.empty formals annotations actual_params
let formals_actuals_new_set map =
FormalsMap.fold
(fun formal actual set ->
match actual with
| Exp.Closure closure, _ ->
List.fold_left closure.Exp.captured_vars ~init:set ~f:(fun set (exp, var, typ, _) ->
let formal_annot =
{formal_type= (Pvar.build_formal_from_pvar var, typ); formal_annot= Annot.Item.empty}
in
let formal_actual = {formal= formal_annot; actual= (exp, typ)} in
FormalsActualsSet.add formal_actual set )
| actual ->
FormalsActualsSet.add {formal; actual} set )
map FormalsActualsSet.empty
let formals_annots_actuals_lists new_formals_actuals =
FormalsActualsSet.fold
(fun {formal; actual} (fs, ans, acts) ->
(formal.formal_type :: fs, formal.formal_annot :: ans, actual :: acts) )
new_formals_actuals ([], [], [])
let has_closure actual_params =
List.exists actual_params ~f:(fun (exp, _) ->
match exp with Exp.Closure c -> Procname.is_objc_block c.name | _ -> false )
let should_specialize actual_params call_flags =
let block_is_receiver actual_params =
Int.equal (List.length actual_params) 1 && call_flags.CallFlags.cf_virtual
in
has_closure actual_params && not (block_is_receiver actual_params)
(* name for the specialized method instantiated with closure arguments *)
let pname_with_closure_args callee_pname actual_params =
let block_name_args =
List.filter_map actual_params ~f:(function
| Exp.Closure cl, _ when Procname.is_objc_block cl.name ->
Some (Procname.block_name_of_procname cl.name)
| _ ->
None )
in
Procname.with_block_parameters callee_pname block_name_args
let formals_closures_map map =
FormalsMap.fold
(fun formal actual new_map ->
match actual with
| Exp.Closure closure, _ ->
let captured_as_formals =
List.map
~f:(fun (_, var, typ, _) -> (Pvar.build_formal_from_pvar var, typ))
closure.captured_vars
in
Mangled.Map.add (fst formal.formal_type) (closure.name, captured_as_formals) new_map
| _ ->
new_map )
map Mangled.Map.empty
let is_objc_setter proc_desc =
let attributes = Procdesc.get_attributes proc_desc in
match attributes.ProcAttributes.objc_accessor with Some (Objc_setter _) -> true | _ -> false
let is_initializer proc_desc =
let proc_name = Procdesc.get_proc_name proc_desc in
Procname.is_constructor proc_name
let replace_with_specialize_methods cfg _node instr =
match instr with
| Sil.Call (ret, Exp.Const (Const.Cfun callee_pname), actual_params, loc, flags)
when should_specialize actual_params flags -> (
match Procname.Hash.find_opt cfg callee_pname with
(*TODO(T74127433): This specialization works well only when the we specialize methods that take a block
parameter and then run the block. It doesn't work well when the block is instead stored in
a field. This case will be left as future work, and we won't specialize common cases where this
happens such as setters or initializers. *)
| Some proc_desc when (not (is_objc_setter proc_desc)) && not (is_initializer proc_desc) -> (
let callee_attributes = Procdesc.get_attributes proc_desc in
match
formals_actuals_map callee_attributes.formals callee_attributes.method_annotation.params
actual_params
with
| Some map ->
let set = formals_actuals_new_set map in
let new_formals, new_annots, new_actuals = formals_annots_actuals_lists set in
let annot = callee_attributes.method_annotation in
let specialized_pname = pname_with_closure_args callee_pname actual_params in
let new_attributes =
{ callee_attributes with
specialized_with_blocks_info=
Some
{ orig_proc= callee_pname
; formals_to_procs_and_new_formals= formals_closures_map map }
; is_defined= true
; formals= new_formals
; method_annotation= {annot with params= new_annots}
; proc_name= specialized_pname }
in
Cfg.create_proc_desc cfg new_attributes |> ignore ;
Sil.Call (ret, Exp.Const (Const.Cfun specialized_pname), new_actuals, loc, flags)
| None ->
instr )
| _ ->
instr )
| _ ->
instr
let process cfg =
let process_pdesc _proc_name proc_desc =
Procdesc.replace_instrs proc_desc ~f:(replace_with_specialize_methods cfg) |> ignore
in
Procname.Hash.iter process_pdesc cfg

@ -0,0 +1,10 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
val process : Cfg.t -> unit

@ -52,6 +52,7 @@ let do_source_file (translation_unit_context : CFrontend_config.translation_unit
let cfg = compute_icfg translation_unit_context tenv ast in
CAddImplicitDeallocImpl.process cfg tenv ;
CAddImplicitGettersSetters.process cfg ;
CCallSpecializedWithClosures.process cfg ;
L.(debug Capture Verbose) "@\n End building call/cfg graph for '%a'.@\n" SourceFile.pp source_file ;
NullabilityPreanalysis.analysis cfg tenv ;
SourceFiles.add source_file cfg (Tenv.FileLocal tenv) (Some integer_type_widths) ;

@ -104,6 +104,7 @@ SOURCES_ARC = \
npe/nullable.m \
property/ExplicitIvarName.m \
shared/block/dispatch_examples.m \
specialized_methods_with_blocks/BlockAsReceiver.m \
subtyping/KindOfClassExample.m \
variadic_methods/premature_nil_termination.m \

@ -0,0 +1,31 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <Foundation/Foundation.h>
@interface BlockAsReceiver : NSObject
@end
@implementation BlockAsReceiver
static bool _isAppStartingUp() { return true; }
/* This program would crash if we would specialize NSObject.copy
here because it would become a virtual call without receiver,
as the receiver is the block parameter. Blocks as also objects! */
static void setupTimerOk() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_block_t _timerBlock = [^{
if (_isAppStartingUp()) {
return;
}
} copy];
});
}
@end

@ -52,7 +52,7 @@ digraph cfg {
"capture#A#instance.d411336575e4bf632a1828f5f5979726_2" [label="2: Exit A.capture \n " color=yellow style=filled]
"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" [label="3: Message Call: sHandler: \n n$3=*&self:A* [line 45, column 4]\n n$4=*n$3._b:B* [line 45, column 4]\n n$0=*&self:A* [line 45, column 16]\n n$5=_fun_B.sHandler:(n$4:B*,(_fun_objc_blockA.capture_1,([by ref]n$0 &self:A*)):_fn_(*)) block_params virtual [line 45, column 3]\n " shape="box"]
"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" [label="3: Message Call: sHandler: \n n$3=*&self:A* [line 45, column 4]\n n$4=*n$3._b:B* [line 45, column 4]\n n$0=*&self:A* [line 45, column 16]\n n$5=_fun_B.sHandler:[objc_blockA.capture_1](n$0:A*,n$4:B*) block_params virtual [line 45, column 3]\n " shape="box"]
"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" -> "capture#A#instance.d411336575e4bf632a1828f5f5979726_2" ;

@ -0,0 +1,50 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@interface A
typedef void (^MyBlock)(int x);
@end
int p() { return 0; }
foo(_Nullable MyBlock my_block1, _Nullable MyBlock my_block2, _Nonnull A* a) {
my_block1(22);
}
@implementation A {
int x;
}
- (int)bar:(A*)a {
int x = 0;
foo(
^(int i) {
self->x = x;
},
^(int i) {
self->x = i;
},
a);
int y = p();
return self->x;
}
- (int)bar2 {
foo(
^(int i) {
self->x = 5;
},
^(int i) {
self->x = 5;
},
self);
return self->x;
}
@end

@ -0,0 +1,118 @@
/* @generated */
digraph cfg {
"foo.acbd18db4cc2f85cedef654fccc4a4d8_1" [label="1: Start foo\nFormals: my_block1:_fn_(*) my_block2:_fn_(*) a:A*\nLocals: \nAnnotation: <> foo(<_Nullable> <_Nullable> <_Nonnull>) \n " color=yellow style=filled]
"foo.acbd18db4cc2f85cedef654fccc4a4d8_1" -> "foo.acbd18db4cc2f85cedef654fccc4a4d8_3" ;
"foo.acbd18db4cc2f85cedef654fccc4a4d8_2" [label="2: Exit foo \n " color=yellow style=filled]
"foo.acbd18db4cc2f85cedef654fccc4a4d8_3" [label="3: Call n$0 \n n$0=*&my_block1:_fn_(*) [line 16, column 3]\n n$1=n$0(22:int) objc_block [line 16, column 3]\n " shape="box"]
"foo.acbd18db4cc2f85cedef654fccc4a4d8_3" -> "foo.acbd18db4cc2f85cedef654fccc4a4d8_2" ;
"p.83878c91171338902e0fe0fb97a8c47a_1" [label="1: Start p\nFormals: \nLocals: \n " color=yellow style=filled]
"p.83878c91171338902e0fe0fb97a8c47a_1" -> "p.83878c91171338902e0fe0fb97a8c47a_3" ;
"p.83878c91171338902e0fe0fb97a8c47a_2" [label="2: Exit p \n " color=yellow style=filled]
"p.83878c91171338902e0fe0fb97a8c47a_3" [label="3: Return Stmt \n *&return:int=0 [line 13, column 11]\n " shape="box"]
"p.83878c91171338902e0fe0fb97a8c47a_3" -> "p.83878c91171338902e0fe0fb97a8c47a_2" ;
"objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_1" [label="1: Start objc_blockA.bar2_3\nFormals: self:A* i:int\nLocals: \nCaptured: [by ref]self:A* \n " color=yellow style=filled]
"objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_1" -> "objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_3" ;
"objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_2" [label="2: Exit objc_blockA.bar2_3 \n " color=yellow style=filled]
"objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_3" [label="3: BinaryOperatorStmt: Assign \n n$15=*&self:A* [line 41, column 9]\n *n$15.x:int=5 [line 41, column 9]\n " shape="box"]
"objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_3" -> "objc_blockA.bar2_3(class A).d749ef9e4d7f0a45237d8fe9e40fc593_2" ;
"objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_1" [label="1: Start objc_blockA.bar2_4\nFormals: self:A* i:int\nLocals: \nCaptured: [by ref]self:A* \n " color=yellow style=filled]
"objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_1" -> "objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_3" ;
"objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_2" [label="2: Exit objc_blockA.bar2_4 \n " color=yellow style=filled]
"objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_3" [label="3: BinaryOperatorStmt: Assign \n n$17=*&self:A* [line 44, column 9]\n *n$17.x:int=5 [line 44, column 9]\n " shape="box"]
"objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_3" -> "objc_blockA.bar2_4(class A).a4f29e420077ca6ce7e44776941a7430_2" ;
"objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_1" [label="1: Start objc_blockA.bar:_1\nFormals: self:A* x:int i:int\nLocals: \nCaptured: [by ref]self:A* [by ref]x:int \n " color=yellow style=filled]
"objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_1" -> "objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_3" ;
"objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_2" [label="2: Exit objc_blockA.bar:_1 \n " color=yellow style=filled]
"objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_3" [label="3: BinaryOperatorStmt: Assign \n n$5=*&self:A* [line 28, column 9]\n n$6=*&x:int [line 28, column 19]\n *n$5.x:int=n$6 [line 28, column 9]\n " shape="box"]
"objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_3" -> "objc_blockA.bar:_1(class A).3dfd8f5104e9624c9f972777203745e3_2" ;
"objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_1" [label="1: Start objc_blockA.bar:_2\nFormals: self:A* i:int\nLocals: \nCaptured: [by ref]self:A* \n " color=yellow style=filled]
"objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_1" -> "objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_3" ;
"objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_2" [label="2: Exit objc_blockA.bar:_2 \n " color=yellow style=filled]
"objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_3" [label="3: BinaryOperatorStmt: Assign \n n$8=*&self:A* [line 31, column 9]\n n$9=*&i:int [line 31, column 19]\n *n$8.x:int=n$9 [line 31, column 9]\n " shape="box"]
"objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_3" -> "objc_blockA.bar:_2(class A).714c02790d023adc163c946a9f0220cd_2" ;
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_1" [label="1: Start A.bar2\nFormals: self:A*\nLocals: \n " color=yellow style=filled]
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_1" -> "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_4" ;
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_2" [label="2: Exit A.bar2 \n " color=yellow style=filled]
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_3" [label="3: Return Stmt \n n$12=*&self:A* [line 47, column 10]\n n$13=*n$12.x:int [line 47, column 10]\n *&return:int=n$13 [line 47, column 3]\n " shape="box"]
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_3" -> "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_2" ;
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_4" [label="4: Call _fun_foo \n n$14=*&self:A* [line 40, column 7]\n n$16=*&self:A* [line 43, column 7]\n n$18=*&self:A* [line 46, column 7]\n n$19=_fun_foo[objc_blockA.bar2_3^objc_blockA.bar2_4](n$14:A*,n$18:A*) block_params [line 39, column 3]\n " shape="box"]
"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_4" -> "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_3" ;
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_1" [label="1: Start A.bar:\nFormals: self:A* a:A*\nLocals: y:int x:int \n " color=yellow style=filled]
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_1" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_6" ;
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_2" [label="2: Exit A.bar: \n " color=yellow style=filled]
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_3" [label="3: Return Stmt \n n$0=*&self:A* [line 35, column 10]\n n$1=*n$0.x:int [line 35, column 10]\n *&return:int=n$1 [line 35, column 3]\n " shape="box"]
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_3" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_2" ;
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_4" [label="4: DeclStmt \n VARIABLE_DECLARED(y:int); [line 34, column 3]\n n$2=_fun_p() [line 34, column 11]\n *&y:int=n$2 [line 34, column 3]\n " shape="box"]
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_4" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_3" ;
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" [label="5: Call _fun_foo \n n$3=*&self:A* [line 27, column 7]\n n$4=*&x:int [line 27, column 7]\n n$7=*&self:A* [line 30, column 7]\n n$10=*&a:A* [line 33, column 7]\n n$11=_fun_foo[objc_blockA.bar:_1^objc_blockA.bar:_2](n$4:int,n$3:A*,n$10:A*) block_params [line 26, column 3]\n " shape="box"]
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_4" ;
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_6" [label="6: DeclStmt \n VARIABLE_DECLARED(x:int); [line 25, column 3]\n *&x:int=0 [line 25, column 3]\n " shape="box"]
"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_6" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" ;
"dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_1" [label="1: Start A.dealloc\nFormals: self:A*\nLocals: \n " color=yellow style=filled]
"dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_1" -> "dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_3" ;
"dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_2" [label="2: Exit A.dealloc \n " color=yellow style=filled]
"dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_3" [label="3: Call dealloc \n " shape="box"]
"dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_3" -> "dealloc#A#instance.55ac864e91dcd5d484e8ab7d8eb94fcb_2" ;
}

@ -10,6 +10,7 @@ CLANG_OPTIONS = -c $(OBJC_CLANG_OPTIONS)
SOURCES = \
../block/retain_cycle.m \
../block/static.m \
../block/specialized_method_with_block_params.m \
../boxing/Boxing.m \
../boxing/array.m \
../boxing/array_literal.c \

@ -33,7 +33,7 @@ digraph cfg {
"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_3" -> "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_2" ;
"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" [label="4: Message Call: foo:and: \n n$4=*&self:B* [line 23, column 10]\n n$5=*n$4.h:int [line 23, column 10]\n n$2=*&self:B* const [line 24, column 11]\n n$6=_fun_B.foo:and:(n$5:int,(_fun_objc_blockB.f_1,([by ref]n$2 &self:B* const )):_fn_(*)) block_params [line 23, column 3]\n " shape="box"]
"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" [label="4: Message Call: foo:and: \n n$4=*&self:B* [line 23, column 10]\n n$5=*n$4.h:int [line 23, column 10]\n n$2=*&self:B* const [line 24, column 11]\n n$6=_fun_B.foo:and:[objc_blockB.f_1](n$5:int,n$2:B* const ) block_params [line 23, column 3]\n " shape="box"]
"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" -> "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_3" ;

@ -63,7 +63,7 @@ digraph cfg {
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_2" [label="2: Exit MyBlock.array \n " color=yellow style=filled]
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" [label="3: Message Call: enumerateObjectsUsingBlock: \n n$5=*&a:NSArray* [line 19, column 4]\n n$6=_fun_NSArray.enumerateObjectsUsingBlock:(n$5:NSArray*,(_fun_objc_blockMyBlock.array_1):_fn_(*)) block_params virtual [line 19, column 3]\n " shape="box"]
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" [label="3: Message Call: enumerateObjectsUsingBlock: \n n$5=*&a:NSArray* [line 19, column 4]\n n$6=_fun_NSArray.enumerateObjectsUsingBlock:[objc_blockMyBlock.array_1](n$5:NSArray*) block_params virtual [line 19, column 3]\n " shape="box"]
"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" -> "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_2" ;

@ -110,7 +110,7 @@ digraph cfg {
"block_attribute#DispatchA#class.df997e345dbf19ec3438c667c942e14a_3" -> "block_attribute#DispatchA#class.df997e345dbf19ec3438c667c942e14a_2" ;
"block_attribute#DispatchA#class.df997e345dbf19ec3438c667c942e14a_4" [label="4: Call _fun__dispatch_once \n n$7=*&a:DispatchA* [line 37, column 24]\n n$9=_fun__dispatch_once(&#GB<codetoanalyze/objc/shared/block/dispatch.m>$DispatchA.block_attribute.once:long*,(_fun_objc_blockDispatchA.block_attribute_2,([by ref]n$7 &a:DispatchA*)):_fn_(*)) block_params [line 37, column 3]\n " shape="box"]
"block_attribute#DispatchA#class.df997e345dbf19ec3438c667c942e14a_4" [label="4: Call _fun__dispatch_once \n n$7=*&a:DispatchA* [line 37, column 24]\n n$9=_fun__dispatch_once[objc_blockDispatchA.block_attribute_2](&#GB<codetoanalyze/objc/shared/block/dispatch.m>$DispatchA.block_attribute.once:long*,n$7:DispatchA*) block_params [line 37, column 3]\n " shape="box"]
"block_attribute#DispatchA#class.df997e345dbf19ec3438c667c942e14a_4" -> "block_attribute#DispatchA#class.df997e345dbf19ec3438c667c942e14a_3" ;
@ -190,7 +190,7 @@ digraph cfg {
"sharedInstance#DispatchA#class.8992c6086d1ce5c225093940f62386ac_3" -> "sharedInstance#DispatchA#class.8992c6086d1ce5c225093940f62386ac_2" ;
"sharedInstance#DispatchA#class.8992c6086d1ce5c225093940f62386ac_4" [label="4: Call _fun__dispatch_once \n n$4=_fun__dispatch_once(&#GB<codetoanalyze/objc/shared/block/dispatch.m>$DispatchA.sharedInstance.once:long*,(_fun_objc_blockDispatchA.sharedInstance_1):_fn_(*)) block_params [line 28, column 3]\n " shape="box"]
"sharedInstance#DispatchA#class.8992c6086d1ce5c225093940f62386ac_4" [label="4: Call _fun__dispatch_once \n n$4=_fun__dispatch_once[objc_blockDispatchA.sharedInstance_1](&#GB<codetoanalyze/objc/shared/block/dispatch.m>$DispatchA.sharedInstance.once:long*) block_params [line 28, column 3]\n " shape="box"]
"sharedInstance#DispatchA#class.8992c6086d1ce5c225093940f62386ac_4" -> "sharedInstance#DispatchA#class.8992c6086d1ce5c225093940f62386ac_3" ;

@ -101,7 +101,7 @@ digraph cfg {
"dispatch_after_example#DispatchEx#class.1d25856bd99eb1ef683c8f65ff46d05d_3" -> "dispatch_after_example#DispatchEx#class.1d25856bd99eb1ef683c8f65ff46d05d_2" ;
"dispatch_after_example#DispatchEx#class.1d25856bd99eb1ef683c8f65ff46d05d_4" [label="4: Call _fun_dispatch_after \n n$16=_fun_dispatch_time(0:unsigned long long,((unsigned long long)2 * 1000000000):long long) [line 46, column 18]\n n$17=_fun_dispatch_get_main_queue() [line 47, column 18]\n n$21=_fun_dispatch_after(n$16:unsigned long long,n$17:NSObject*,(_fun_objc_blockDispatchEx.dispatch_after_example_3):_fn_(*)) block_params [line 46, column 3]\n " shape="box"]
"dispatch_after_example#DispatchEx#class.1d25856bd99eb1ef683c8f65ff46d05d_4" [label="4: Call _fun_dispatch_after \n n$16=_fun_dispatch_time(0:unsigned long long,((unsigned long long)2 * 1000000000):long long) [line 46, column 18]\n n$17=_fun_dispatch_get_main_queue() [line 47, column 18]\n n$21=_fun_dispatch_after[objc_blockDispatchEx.dispatch_after_example_3](n$16:unsigned long long,n$17:NSObject*) block_params [line 46, column 3]\n " shape="box"]
"dispatch_after_example#DispatchEx#class.1d25856bd99eb1ef683c8f65ff46d05d_4" -> "dispatch_after_example#DispatchEx#class.1d25856bd99eb1ef683c8f65ff46d05d_3" ;
@ -120,7 +120,7 @@ digraph cfg {
"dispatch_async_example#DispatchEx#class.5c5d7347be2a9654ad7e32514189fe54_3" -> "dispatch_async_example#DispatchEx#class.5c5d7347be2a9654ad7e32514189fe54_2" ;
"dispatch_async_example#DispatchEx#class.5c5d7347be2a9654ad7e32514189fe54_4" [label="4: Call _fun_dispatch_async \n n$9=_fun_dispatch_get_global_queue(0:long,(unsigned long)0:unsigned long) [line 36, column 18]\n n$13=_fun_dispatch_async(n$9:NSObject*,(_fun_objc_blockDispatchEx.dispatch_async_example_2):_fn_(*)) block_params [line 36, column 3]\n " shape="box"]
"dispatch_async_example#DispatchEx#class.5c5d7347be2a9654ad7e32514189fe54_4" [label="4: Call _fun_dispatch_async \n n$9=_fun_dispatch_get_global_queue(0:long,(unsigned long)0:unsigned long) [line 36, column 18]\n n$13=_fun_dispatch_async[objc_blockDispatchEx.dispatch_async_example_2](n$9:NSObject*) block_params [line 36, column 3]\n " shape="box"]
"dispatch_async_example#DispatchEx#class.5c5d7347be2a9654ad7e32514189fe54_4" -> "dispatch_async_example#DispatchEx#class.5c5d7347be2a9654ad7e32514189fe54_3" ;
@ -139,7 +139,7 @@ digraph cfg {
"dispatch_barrier_example#DispatchEx#class.a541a40f2f04e29019c58e563f7544d8_3" -> "dispatch_barrier_example#DispatchEx#class.a541a40f2f04e29019c58e563f7544d8_2" ;
"dispatch_barrier_example#DispatchEx#class.a541a40f2f04e29019c58e563f7544d8_4" [label="4: Call _fun_dispatch_barrier_async \n n$38=_fun_dispatch_get_main_queue() [line 75, column 26]\n n$42=_fun_dispatch_barrier_async(n$38:NSObject*,(_fun_objc_blockDispatchEx.dispatch_barrier_example_6):_fn_(*)) block_params [line 75, column 3]\n " shape="box"]
"dispatch_barrier_example#DispatchEx#class.a541a40f2f04e29019c58e563f7544d8_4" [label="4: Call _fun_dispatch_barrier_async \n n$38=_fun_dispatch_get_main_queue() [line 75, column 26]\n n$42=_fun_dispatch_barrier_async[objc_blockDispatchEx.dispatch_barrier_example_6](n$38:NSObject*) block_params [line 75, column 3]\n " shape="box"]
"dispatch_barrier_example#DispatchEx#class.a541a40f2f04e29019c58e563f7544d8_4" -> "dispatch_barrier_example#DispatchEx#class.a541a40f2f04e29019c58e563f7544d8_3" ;
@ -158,7 +158,7 @@ digraph cfg {
"dispatch_group_example#DispatchEx#class.f420a75c58eda6d3f0e5e05fadabfc18_3" -> "dispatch_group_example#DispatchEx#class.f420a75c58eda6d3f0e5e05fadabfc18_2" ;
"dispatch_group_example#DispatchEx#class.f420a75c58eda6d3f0e5e05fadabfc18_4" [label="4: Call _fun_dispatch_group_async \n n$24=_fun_dispatch_get_main_queue() [line 57, column 30]\n n$28=_fun_dispatch_group_async(null:NSObject*,n$24:NSObject*,(_fun_objc_blockDispatchEx.dispatch_group_example_4):_fn_(*)) block_params [line 57, column 3]\n " shape="box"]
"dispatch_group_example#DispatchEx#class.f420a75c58eda6d3f0e5e05fadabfc18_4" [label="4: Call _fun_dispatch_group_async \n n$24=_fun_dispatch_get_main_queue() [line 57, column 30]\n n$28=_fun_dispatch_group_async[objc_blockDispatchEx.dispatch_group_example_4](n$24:NSObject*,null:NSObject*) block_params [line 57, column 3]\n " shape="box"]
"dispatch_group_example#DispatchEx#class.f420a75c58eda6d3f0e5e05fadabfc18_4" -> "dispatch_group_example#DispatchEx#class.f420a75c58eda6d3f0e5e05fadabfc18_3" ;
@ -177,7 +177,7 @@ digraph cfg {
"dispatch_group_notify_example#DispatchEx#class.f5cf54b07621c319cf7ead3b217760ed_3" -> "dispatch_group_notify_example#DispatchEx#class.f5cf54b07621c319cf7ead3b217760ed_2" ;
"dispatch_group_notify_example#DispatchEx#class.f5cf54b07621c319cf7ead3b217760ed_4" [label="4: Call _fun_dispatch_group_async \n n$31=_fun_dispatch_get_main_queue() [line 66, column 30]\n n$35=_fun_dispatch_group_async(null:NSObject*,n$31:NSObject*,(_fun_objc_blockDispatchEx.dispatch_group_notify_example_5):_fn_(*)) block_params [line 66, column 3]\n " shape="box"]
"dispatch_group_notify_example#DispatchEx#class.f5cf54b07621c319cf7ead3b217760ed_4" [label="4: Call _fun_dispatch_group_async \n n$31=_fun_dispatch_get_main_queue() [line 66, column 30]\n n$35=_fun_dispatch_group_async[objc_blockDispatchEx.dispatch_group_notify_example_5](n$31:NSObject*,null:NSObject*) block_params [line 66, column 3]\n " shape="box"]
"dispatch_group_notify_example#DispatchEx#class.f5cf54b07621c319cf7ead3b217760ed_4" -> "dispatch_group_notify_example#DispatchEx#class.f5cf54b07621c319cf7ead3b217760ed_3" ;
@ -196,7 +196,7 @@ digraph cfg {
"dispatch_once_example#DispatchEx#class.d3456446b1a2d5355c1767887cc8b62c_3" -> "dispatch_once_example#DispatchEx#class.d3456446b1a2d5355c1767887cc8b62c_2" ;
"dispatch_once_example#DispatchEx#class.d3456446b1a2d5355c1767887cc8b62c_4" [label="4: Call _fun__dispatch_once \n n$6=_fun__dispatch_once(&#GB<codetoanalyze/objc/shared/block/dispatch_examples.m>$DispatchEx.dispatch_once_example.onceToken:long*,(_fun_objc_blockDispatchEx.dispatch_once_example_1):_fn_(*)) block_params [line 27, column 3]\n " shape="box"]
"dispatch_once_example#DispatchEx#class.d3456446b1a2d5355c1767887cc8b62c_4" [label="4: Call _fun__dispatch_once \n n$6=_fun__dispatch_once[objc_blockDispatchEx.dispatch_once_example_1](&#GB<codetoanalyze/objc/shared/block/dispatch_examples.m>$DispatchEx.dispatch_once_example.onceToken:long*) block_params [line 27, column 3]\n " shape="box"]
"dispatch_once_example#DispatchEx#class.d3456446b1a2d5355c1767887cc8b62c_4" -> "dispatch_once_example#DispatchEx#class.d3456446b1a2d5355c1767887cc8b62c_3" ;

@ -11,7 +11,7 @@ digraph cfg {
"DispatchInMacroTest.f5d56763274a479d06265a2f9562bef1_3" -> "DispatchInMacroTest.f5d56763274a479d06265a2f9562bef1_5" ;
"DispatchInMacroTest.f5d56763274a479d06265a2f9562bef1_4" [label="4: Call _fun__dispatch_once \n n$3=_fun__dispatch_once(&#GB<codetoanalyze/objc/shared/block/dispatch_in_macro.m>$DispatchInMacroTest.once_token:long*,(_fun_objc_blockDispatchInMacroTest_1):_fn_(*)) block_params [line 21, column 10]\n " shape="box"]
"DispatchInMacroTest.f5d56763274a479d06265a2f9562bef1_4" [label="4: Call _fun__dispatch_once \n n$3=_fun__dispatch_once[objc_blockDispatchInMacroTest_1](&#GB<codetoanalyze/objc/shared/block/dispatch_in_macro.m>$DispatchInMacroTest.once_token:long*) block_params [line 21, column 10]\n " shape="box"]
"DispatchInMacroTest.f5d56763274a479d06265a2f9562bef1_4" -> "DispatchInMacroTest.f5d56763274a479d06265a2f9562bef1_3" ;

Loading…
Cancel
Save