@ -35,6 +35,13 @@ module AddAbstractionInstructions = struct
Procdesc . iter_nodes do_node pdesc
end
let objc_get_first_arg_typ = function
| [ ( _ , { Typ . desc = Tptr ( { desc = Tstruct ( ( ObjcClass _ | ObjcProtocol _ ) as objc_class ) } , _ ) } ) ] ->
Some objc_class
| _ ->
None
(* * In ObjC, [NSObject.copy] returns the object returned by [copyWithZone:] on the given class. This
method must be implemented if the class complies with [ NSCopying ] protocol . Since we don't have
access to NSObject's code , to follow calls into [ copyWithZone : ] , we replace such [ copy ] calls
@ -50,13 +57,6 @@ module ReplaceObjCCopy = struct
type copy_kind =
{ protocol : string ; method_name : string ; method_with_zone : string ; is_mutable : bool }
let get_first_arg_typ = function
| [ ( _ , { Typ . desc = Tptr ( { desc = Tstruct objc_class } , _ ) } ) ] ->
Some objc_class
| _ ->
None
let get_copy_kind_opt pname =
let matches_nsobject_proc method_name =
String . equal ( Procname . get_method pname ) method_name
@ -88,8 +88,8 @@ module ReplaceObjCCopy = struct
let get_replaced_instr { protocol ; method_name ; method_with_zone ; is_mutable } pdesc tenv params
instr ret_id_typ loc flags =
match get_first_arg_typ params with
ret_id_typ loc flags =
match objc_ get_first_arg_typ params with
| Some cl ->
let class_name = Typ . Name . name cl in
if
@ -103,25 +103,69 @@ module ReplaceObjCCopy = struct
used by Objective - C . We still need to satisfy the
signature though . * )
L . ( debug Capture Verbose ) " REPLACING %s with '%s'@ \n " method_name method_with_zone ;
Sil . Call
( ret_id_typ , function_exp , params @ [ ( Exp . null , Typ . pointer_to_objc_nszone ) ] , loc , flags )
)
else instr
Some
( Sil . Call
( ret_id_typ
, function_exp
, params @ [ ( Exp . null , Typ . pointer_to_objc_nszone ) ]
, loc
, flags ) ) )
else None
| _ ->
instr
None
let process tenv pdesc =
let instr_replace_copy_method _ node ( instr : Sil . instr ) =
match instr with
| Call ( ret_id_typ , Const ( Cfun pn ) , params , loc , flags ) ->
get_copy_kind_opt pn
| > Option . value_map ~ default : instr ~ f : ( fun copy_kind ->
get_replaced_instr copy_kind pdesc tenv params instr ret_id_typ loc flags )
let process tenv pdesc ret_id_typ callee params loc flags =
get_copy_kind_opt callee
| > Option . bind ~ f : ( fun copy_kind ->
get_replaced_instr copy_kind pdesc tenv params ret_id_typ loc flags )
end
module ReplaceObjCOverridden = struct
let may_be_super_call class_name_opt object_name =
Option . exists class_name_opt ~ f : ( Typ . Name . equal object_name )
let get_overridden_method_opt tenv ~ caller_class_name ~ callee params =
let open IOption . Let_syntax in
let * sup_class_name = Procname . get_class_type_name callee in
let * sub_class_name = objc_get_first_arg_typ params in
if
PatternMatch . is_subtype tenv sub_class_name sup_class_name
&& not ( may_be_super_call caller_class_name sub_class_name )
then
let callee' = Procname . replace_class callee sub_class_name in
if Option . is_some ( Procdesc . load callee' ) then Some callee' else None
else None
let process tenv caller ret_id_typ callee params loc flags =
get_overridden_method_opt tenv
~ caller_class_name : ( Procname . get_class_type_name caller )
~ callee params
| > Option . map ~ f : ( fun overridden_method ->
Logging . d_printfln_escaped " Replace overridden method %a to %a " Procname . pp callee
Procname . pp overridden_method ;
Sil . Call ( ret_id_typ , Const ( Cfun overridden_method ) , params , loc , flags ) )
end
module ReplaceObjCMethodCall = struct
let process tenv pdesc caller =
let replace_method instr =
match ( instr : Sil . instr ) with
| Call ( ret_id_typ , Const ( Cfun callee ) , params , loc , flags ) ->
IOption . if_none_evalopt
( ReplaceObjCCopy . process tenv pdesc ret_id_typ callee params loc flags ) ~ f : ( fun () ->
ReplaceObjCOverridden . process tenv caller ret_id_typ callee params loc flags )
| > Option . value ~ default : instr
| _ ->
instr
in
Procdesc . replace_instrs pdesc ~ f : instr_replace_copy_method | > ignore
Procdesc . replace_instrs pdesc ~ f : ( fun node instr ->
NodePrinter . with_session node ~ kind : ` ComputePre
~ pp_name : ( fun fmt -> Format . pp_print_string fmt " Replace ObjC method " )
~ f : ( fun () -> replace_method instr ) )
| > ignore
end
(* * Find synthetic ( including access and bridge ) Java methods in the procedure and inline them in
@ -466,7 +510,7 @@ let do_preanalysis exe_env pdesc =
if not ( Procname . is_java proc_name ) then (
ClosuresSubstitution . process_closure_call summary ;
ClosureSubstSpecializedMethod . process summary ;
ReplaceObjC Copy . process tenv pdesc ) ;
ReplaceObjC MethodCall . process tenv pdesc proc_name ) ;
Liveness . process summary tenv ;
AddAbstractionInstructions . process pdesc ;
if Procname . is_java proc_name then Devirtualizer . process summary tenv ;