diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index 2a69b5cf5..94b91c3c7 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -24,6 +24,8 @@ let alloc = "alloc" let arrayWithObjects_count = "arrayWithObjects:count:" +let dictionaryWithObjects_forKeys_count = "dictionaryWithObjects:forKeys:count:" + let dealloc = "dealloc" let assert_fail = "__assert_fail" diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index e3cee104f..4a45dea75 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -24,6 +24,8 @@ val alloc : string val arrayWithObjects_count : string +val dictionaryWithObjects_forKeys_count : string + val dealloc : string val assert_fail : string diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 50f10d881..f25cbf839 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -668,6 +668,16 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s None ) + let get_dictionaryWithObjects_forKeys_count_infos method_pointer = + Option.bind (CAst_utils.get_decl_opt method_pointer) ~f:(function + | Clang_ast_t.ObjCMethodDecl + (decl_info, {ni_name}, {omdi_parameters= ParmVarDecl (_, _, objects_qual_typ, _) :: _}) + when String.equal ni_name CFrontend_config.dictionaryWithObjects_forKeys_count -> + Some (decl_info, objects_qual_typ) + | _ -> + None ) + + let this_expr_trans stmt_info ?class_qual_type trans_state sil_loc = let this_pvar, this_typ = get_this_pvar_typ stmt_info ?class_qual_type trans_state.context in let return = (Exp.Lvar this_pvar, this_typ) in @@ -2955,9 +2965,122 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s and objCDictionaryLiteral_trans trans_state expr_info stmt_info stmts dict_literal_info = let method_pointer = dict_literal_info.Clang_ast_t.odlei_dict_method in - let stmts = CGeneral_utils.swap_elements_list stmts in - let stmts = stmts @ [Ast_expressions.create_nil stmt_info] in - objCArrayDictLiteral_trans trans_state expr_info stmt_info stmts method_pointer + match get_dictionaryWithObjects_forKeys_count_infos method_pointer with + | Some infos -> + objCDictLiteral_dictionaryWithObjects_forKeys_count_trans trans_state expr_info stmt_info + stmts infos method_pointer + | None -> + let stmts = CGeneral_utils.swap_elements_list stmts in + let stmts = stmts @ [Ast_expressions.create_nil stmt_info] in + objCArrayDictLiteral_trans trans_state expr_info stmt_info stmts method_pointer + + + (** Translates an dictionary literal @ [ @"firstName": @"Foo", @"lastName":@"Bar" ] to + + {[ + n$1=NSString.stringWithUTF8:(@"firstName") + n$2=NSNumber.stringWithUTF8:(@"Foo") + n$3=NSNumber.stringWithUTF8:(@"lastName") + n$4=NSNumber.stringWithUTF8:(@"Bar") + temp1[0]:objc_object*=n$1 + temp1[1]:objc_object*=n$3 + temp2[0]:objc_object*=n$2 + temp2[1]:objc_object*=n$4 + n$3=NSDictionary.dictionaryWithObjects:forKeys:count:(temp2:objc_object* const [2*8], + temp1:objc_object* const [2*8], 2:int) + ]} + + where [temp1] [temp2] are additional local variables declared as array. *) + and objCDictLiteral_dictionaryWithObjects_forKeys_count_trans + ({context= {procdesc; tenv; translation_unit_context} as context} as trans_state) expr_info + stmt_info stmts (decl_info, objects_qual_typ) method_pointer = + let loc = CLocation.location_of_stmt_info translation_unit_context.source_file stmt_info in + (* 1. Add a temporary local variable for an array *) + let temp1 = + CVar_decl.mk_temp_sil_var procdesc ~name:"SIL_dictionaryWithObjects_forKeys_count1___" + in + let temp2 = + CVar_decl.mk_temp_sil_var procdesc ~name:"SIL_dictionaryWithObjects_forKeys_count2___" + in + let length = List.length stmts |> IntLit.of_int in + let create_var = function + | temp -> + let array_typ = + match CType_decl.qual_type_to_sil_type tenv objects_qual_typ with + | Typ.{desc= Tptr (t, _)} -> + Typ.mk_array ~length ~stride:(IntLit.of_int 8) t + | _ -> + Typ.void_star + in + (Exp.Lvar temp, array_typ) + in + let append_var temp array_typ = + Procdesc.append_locals procdesc + [ { ProcAttributes.name= Pvar.get_name temp + ; typ= array_typ + ; modify_in_block= false + ; is_constexpr= false + ; is_declared_unused= false } ] + in + let ((temp1_var, array1_typ) as temp1_with_typ) = create_var temp1 in + let ((temp2_var, array2_typ) as temp2_with_typ) = create_var temp2 in + append_var temp1 array1_typ ; + append_var temp2 array2_typ ; + (* 2. Translate array elements *) + let res_trans_elems = List.mapi ~f:(exec_instruction_with_trans_state trans_state None) stmts in + (* 3. Add array initialization (elements assignments) *) + let none_id = Ident.create_none () in + let array_init temp_var temp_with_typ idx_mod = + let instrs = + List.mapi res_trans_elems ~f:(fun i {return= e, typ} -> + if Int.equal (i % 2) idx_mod then + let idx = Exp.Const (Cint (IntLit.of_int (i / 2))) in + [ Sil.Load {id= none_id; e; root_typ= typ; typ; loc} + ; Sil.Store {e1= Lindex (temp_var, idx); root_typ= typ; typ; e2= e; loc} ] + else [] ) + |> List.concat + in + mk_trans_result temp_with_typ {empty_control with instrs} + in + let res_trans_array1 = array_init temp1_var temp1_with_typ 1 in + let res_trans_array2 = array_init temp2_var temp2_with_typ 0 in + (* 4. Add a function call. *) + let res_trans_call = + let method_type_no_ref = CType_decl.get_type_from_expr_info expr_info tenv in + let method_type = add_reference_if_glvalue method_type_no_ref expr_info in + let actuals = + [ temp1_with_typ + ; temp2_with_typ + ; (Exp.Const (Cint (IntLit.div length (IntLit.of_int 2))), Typ.int) ] + in + let callee_name, method_call_type = + let typ = + CAst_utils.qual_type_of_decl_ptr + (Option.value_exn decl_info.Clang_ast_t.di_parent_pointer) + in + let obj_c_message_expr_info = + { Clang_ast_t.omei_selector= CFrontend_config.dictionaryWithObjects_forKeys_count + ; omei_receiver_kind= `Class typ + ; omei_is_definition_found= true + ; omei_decl_pointer= method_pointer } + in + let callee_ms_opt = + Option.bind method_pointer ~f:(fun method_pointer -> + CMethod_trans.method_signature_of_pointer tenv method_pointer ) + in + get_callee_objc_method context obj_c_message_expr_info callee_ms_opt actuals + in + let method_sil = Exp.Const (Cfun callee_name) in + let call_flags = + { CallFlags.default with + cf_virtual= CMethod_trans.equal_method_call_type method_call_type CMethod_trans.MCVirtual + } + in + create_call_instr trans_state method_type method_sil actuals loc call_flags + ~is_objc_method:true ~is_inherited_ctor:false + in + collect_trans_results procdesc ~return:res_trans_call.return + (res_trans_elems @ [res_trans_array1; res_trans_array2; res_trans_call]) and objCStringLiteral_trans trans_state stmt_info stmts info = diff --git a/infer/tests/codetoanalyze/objc/frontend/boxing/dict_literal.c.dot b/infer/tests/codetoanalyze/objc/frontend/boxing/dict_literal.c.dot index f90d35eb7..c9738c6ea 100644 --- a/infer/tests/codetoanalyze/objc/frontend/boxing/dict_literal.c.dot +++ b/infer/tests/codetoanalyze/objc/frontend/boxing/dict_literal.c.dot @@ -11,14 +11,14 @@ digraph cfg { "get_array1.5988b7ad8acf5c81cef9a72d072073c1_3" -> "get_array1.5988b7ad8acf5c81cef9a72d072073c1_2" ; -"get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_1" [label="1: Start get_array2\nFormals: \nLocals: \n " color=yellow style=filled] +"get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_1" [label="1: Start get_array2\nFormals: \nLocals: 0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0:objc_object* const [6*8] 0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1:objc_object* const [6*8] \n " color=yellow style=filled] "get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_1" -> "get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_3" ; "get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_2" [label="2: Exit get_array2 \n " color=yellow style=filled] -"get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_3" [label="3: Return Stmt \n n$5=_fun_NSString.stringWithUTF8String:(\"Matt\":char* const ) [line 23, column 27]\n n$0=_fun_NSString.stringWithUTF8String:(\"firstName\":char* const ) [line 23, column 12]\n n$1=_fun_NSString.stringWithUTF8String:(\"Galloway\":char* const ) [line 23, column 50]\n n$2=_fun_NSString.stringWithUTF8String:(\"lastName\":char* const ) [line 23, column 36]\n n$3=_fun_NSNumber.numberWithInt:(28:int) [line 23, column 72]\n n$4=_fun_NSString.stringWithUTF8String:(\"age\":char* const ) [line 23, column 63]\n n$6=_fun_NSDictionary.dictionaryWithObjects:forKeys:count:(n$5:objc_object*,n$0:objc_object*,n$1:objc_object*,n$2:objc_object*,n$3:objc_object*,n$4:objc_object*,null:objc_object*) [line 23, column 10]\n *&return:NSDictionary*=n$6 [line 23, column 3]\n " shape="box"] +"get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_3" [label="3: Return Stmt \n n$2=_fun_NSString.stringWithUTF8String:(\"firstName\":char* const ) [line 23, column 12]\n n$3=_fun_NSString.stringWithUTF8String:(\"Matt\":char* const ) [line 23, column 27]\n n$4=_fun_NSString.stringWithUTF8String:(\"lastName\":char* const ) [line 23, column 36]\n n$5=_fun_NSString.stringWithUTF8String:(\"Galloway\":char* const ) [line 23, column 50]\n n$6=_fun_NSString.stringWithUTF8String:(\"age\":char* const ) [line 23, column 63]\n n$7=_fun_NSNumber.numberWithInt:(28:int) [line 23, column 72]\n _=*n$3:objc_object* [line 23, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0[0]:objc_object*=n$3 [line 23, column 10]\n _=*n$5:objc_object* [line 23, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0[1]:objc_object*=n$5 [line 23, column 10]\n _=*n$7:objc_object* [line 23, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0[2]:objc_object*=n$7 [line 23, column 10]\n _=*n$2:objc_object* [line 23, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1[0]:objc_object*=n$2 [line 23, column 10]\n _=*n$4:objc_object* [line 23, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1[1]:objc_object*=n$4 [line 23, column 10]\n _=*n$6:objc_object* [line 23, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1[2]:objc_object*=n$6 [line 23, column 10]\n n$9=_fun_NSDictionary.dictionaryWithObjects:forKeys:count:(&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0:objc_object* const [6*8],&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1:objc_object* const [6*8],3:int) [line 23, column 10]\n *&return:NSDictionary*=n$9 [line 23, column 3]\n " shape="box"] "get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_3" -> "get_array2.84aa3c70cb20e7edbe4f0b8d0bd6aa3d_2" ; diff --git a/infer/tests/codetoanalyze/objc/performance/NSDictionary.m b/infer/tests/codetoanalyze/objc/performance/NSDictionary.m index e614f5f97..1a1271200 100644 --- a/infer/tests/codetoanalyze/objc/performance/NSDictionary.m +++ b/infer/tests/codetoanalyze/objc/performance/NSDictionary.m @@ -100,7 +100,7 @@ void nsdictionary_enumerator_linear(NSDictionary* dict) { } } -void nsdictionary_enumerate_call_constant_FP() { +void nsdictionary_enumerate_call_constant() { NSDictionary* dict = @{@"helloString" : @"Hello, World!", @"magicNumber" : @42}; diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index 9e3f86d55..c636f55ce 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -28,13 +28,13 @@ codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_all_keys_linear2, 6 codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_all_values_linear, 3 + 3 ⋅ dict->elements.length.ub + 4 ⋅ (dict->elements.length.ub + 1), OnUIThread:false, [{dict->elements.length.ub + 1},Loop,{dict->elements.length.ub},Loop] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_dictionary_constant, 13, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_dictionary_with_objects_linear, 14 + 15 ⋅ n_entries + 3 ⋅ n_entries + 2 ⋅ (1+max(0, n_entries)) + 4 ⋅ (1+max(0, n_entries)), OnUIThread:false, [{1+max(0, n_entries)},Loop,{1+max(0, n_entries)},Loop,{n_entries},Loop,{n_entries},Loop] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_call_constant_FP, ⊤, OnUIThread:false, [Unbounded value dict->elements.length.ub,Call to nsdictionary_all_values_linear,Loop] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_constant, 52, OnUIThread:false, [] +codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_call_constant, 65, OnUIThread:false, [] +codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_constant, 69, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerator_linear, 5 + dict->elements.length.ub + 4 ⋅ (dict->elements.length.ub + 1), OnUIThread:false, [{dict->elements.length.ub + 1},Loop,{dict->elements.length.ub},Loop] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_fast_enumerate_linear, 5 + dict->elements.length.ub + 4 ⋅ (dict->elements.length.ub + 1), OnUIThread:false, [{dict->elements.length.ub + 1},Loop,{dict->elements.length.ub},Loop] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_find_key_constant, 19, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_dictionary_constant, 3, OnUIThread:false, [] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_literal_constant, 41, OnUIThread:false, [] +codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_literal_constant, 45, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_with_dictionary_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSInteger.m, nsinteger_value_linear, 3 + 3 ⋅ integer + 2 ⋅ (1+max(0, integer)), OnUIThread:false, [{1+max(0, integer)},Loop,{integer},Loop] codetoanalyze/objc/performance/NSInteger.m, nsnumber_number_with_int_integer_value_constant, 34, OnUIThread:false, [] diff --git a/infer/tests/codetoanalyze/objc/performance/issues.exp b/infer/tests/codetoanalyze/objc/performance/issues.exp index 1a461078d..d2f44ae0f 100644 --- a/infer/tests/codetoanalyze/objc/performance/issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/issues.exp @@ -1,7 +1,5 @@ codetoanalyze/objc/performance/NSArray.m, nsarray_empty_array_constant, 3, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] codetoanalyze/objc/performance/NSArray.m, nsarray_init_constant, 3, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_call_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded value dict->elements.length.ub,Call to nsdictionary_all_values_linear,Loop] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_call_constant_FP, 5, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [Unknown value from: NSDictionary.dictionaryWithObjects:forKeys:count:,Call,,Parameter `dict->elements[*]`,Binary operation: ([0, +oo] + 1):signed32 by call to `nsdictionary_all_values_linear` ] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_with_dictionary_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_with_dictionary_linear_FP, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSDictionary.initWithDictionary:,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/NSMutableArray.m, nsarray_new_constant, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] diff --git a/infer/tests/codetoanalyze/objc/shared/annotations/nullable_annotations.m.dot b/infer/tests/codetoanalyze/objc/shared/annotations/nullable_annotations.m.dot index 6ed225600..c9b7bce09 100644 --- a/infer/tests/codetoanalyze/objc/shared/annotations/nullable_annotations.m.dot +++ b/infer/tests/codetoanalyze/objc/shared/annotations/nullable_annotations.m.dot @@ -1,21 +1,21 @@ /* @generated */ digraph cfg { -"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_1" [label="1: Start npe_property_nullable\nFormals: \nLocals: child:Person* person:Person* \n " color=yellow style=filled] +"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_1" [label="1: Start npe_property_nullable\nFormals: \nLocals: 0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0:objc_object* const [2*8] 0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1:objc_object* const [2*8] child:Person* person:Person* \n " color=yellow style=filled] "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_1" -> "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_5" ; "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_2" [label="2: Exit npe_property_nullable \n " color=yellow style=filled] -"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_3" [label="3: Return Stmt \n n$1=*&child:Person* [line 56, column 21]\n n$0=_fun_NSString.stringWithUTF8String:(\"key\":char* const ) [line 56, column 12]\n n$2=_fun_NSDictionary.dictionaryWithObjects:forKeys:count:(n$1:objc_object*,n$0:objc_object*,null:objc_object*) [line 56, column 10]\n *&return:NSDictionary*=n$2 [line 56, column 3]\n " shape="box"] +"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_3" [label="3: Return Stmt \n n$2=_fun_NSString.stringWithUTF8String:(\"key\":char* const ) [line 56, column 12]\n n$3=*&child:Person* [line 56, column 21]\n _=*n$3:objc_object* [line 56, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0[0]:objc_object*=n$3 [line 56, column 10]\n _=*n$2:objc_object* [line 56, column 10]\n *&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1[0]:objc_object*=n$2 [line 56, column 10]\n n$5=_fun_NSDictionary.dictionaryWithObjects:forKeys:count:(&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count1___n$0:objc_object* const [2*8],&0$?%__sil_tmpSIL_dictionaryWithObjects_forKeys_count2___n$1:objc_object* const [2*8],1:int) [line 56, column 10]\n *&return:NSDictionary*=n$5 [line 56, column 3]\n " shape="box"] "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_3" -> "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_2" ; -"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_4" [label="4: DeclStmt \n VARIABLE_DECLARED(child:Person*); [line 55, column 3]\n n$3=*&person:Person* [line 55, column 19]\n n$4=_fun_Person.child(n$3:Person*) [line 55, column 26]\n *&child:Person*=n$4 [line 55, column 3]\n " shape="box"] +"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_4" [label="4: DeclStmt \n VARIABLE_DECLARED(child:Person*); [line 55, column 3]\n n$6=*&person:Person* [line 55, column 19]\n n$7=_fun_Person.child(n$6:Person*) [line 55, column 26]\n *&child:Person*=n$7 [line 55, column 3]\n " shape="box"] "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_4" -> "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_3" ; -"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_5" [label="5: DeclStmt \n VARIABLE_DECLARED(person:Person*); [line 54, column 3]\n n$5=_fun___objc_alloc_no_fail(sizeof(t=Person):unsigned long) [line 54, column 20]\n n$6=_fun_NSObject.init(n$5:Person*) virtual [line 54, column 20]\n *&person:Person*=n$6 [line 54, column 3]\n " shape="box"] +"npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_5" [label="5: DeclStmt \n VARIABLE_DECLARED(person:Person*); [line 54, column 3]\n n$8=_fun___objc_alloc_no_fail(sizeof(t=Person):unsigned long) [line 54, column 20]\n n$9=_fun_NSObject.init(n$8:Person*) virtual [line 54, column 20]\n *&person:Person*=n$9 [line 54, column 3]\n " shape="box"] "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_5" -> "npe_property_nullable.ba4461b16b55481ab8de5124734d2bf3_4" ;