You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
13 KiB
364 lines
13 KiB
(*
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
|
|
open! IStd
|
|
|
|
(** Methods for creating a procdesc from a method or function declaration
|
|
and for resolving a method call and finding the right callee *)
|
|
|
|
module L = Logging
|
|
|
|
(** When the methoc call is MCStatic, means that it is a class method. When it is MCVirtual, it
|
|
means that it is an instance method and that the method to be called will be determined at
|
|
runtime. If it is MCNoVirtual it means that it is an instance method but that the method to be
|
|
called will be determined at compile time *)
|
|
type method_call_type = MCVirtual | MCNoVirtual | MCStatic [@@deriving compare]
|
|
|
|
let equal_method_call_type = [%compare.equal : method_call_type]
|
|
|
|
let method_signature_of_pointer tenv pointer =
|
|
try
|
|
match CAst_utils.get_decl pointer with
|
|
| Some meth_decl ->
|
|
let procname = CType_decl.CProcname.from_decl ~tenv meth_decl in
|
|
let ms = CType_decl.method_signature_of_decl tenv meth_decl procname in
|
|
Some ms
|
|
| None ->
|
|
None
|
|
with CFrontend_config.Invalid_declaration -> None
|
|
|
|
|
|
let get_method_name_from_clang tenv ms_opt =
|
|
match ms_opt with
|
|
| Some ms -> (
|
|
match CAst_utils.get_decl_opt ms.CMethodSignature.pointer_to_parent with
|
|
| Some decl
|
|
-> (
|
|
ignore (CType_decl.add_types_from_decl_to_tenv tenv decl) ;
|
|
match ObjcCategory_decl.get_base_class_name_from_category decl with
|
|
| Some class_typename ->
|
|
let procname = ms.CMethodSignature.name in
|
|
let new_procname = Typ.Procname.replace_class procname class_typename in
|
|
Some new_procname
|
|
| None ->
|
|
Some ms.CMethodSignature.name )
|
|
| None ->
|
|
Some ms.CMethodSignature.name )
|
|
| None ->
|
|
None
|
|
|
|
|
|
let get_superclass_curr_class_objc context =
|
|
let open Clang_ast_t in
|
|
let decl_ref =
|
|
match CContext.get_curr_class context with
|
|
| CContext.ContextClsDeclPtr ptr -> (
|
|
match CAst_utils.get_decl ptr with
|
|
| Some decl ->
|
|
CAst_utils.get_superclass_curr_class_objc_from_decl decl
|
|
| None ->
|
|
Logging.die InternalError
|
|
"Expected that the current class ptr in the context is a valid pointer to class decl, \
|
|
but didn't find declaration, ptr is %d "
|
|
ptr )
|
|
| CContext.ContextNoCls ->
|
|
Logging.die InternalError
|
|
"This should only be called in the context of a class, but got CContext.ContextNoCls"
|
|
in
|
|
match decl_ref |> Option.value_map ~f:(fun dr -> dr.dr_name) ~default:None with
|
|
| Some name ->
|
|
Typ.Name.Objc.from_qual_name (CAst_utils.get_qualified_name name)
|
|
| None ->
|
|
Logging.die InternalError "Expected to always find a superclass, but found none"
|
|
|
|
|
|
(* Gets the class name from a method signature found by clang, if search is successful *)
|
|
let get_class_name_method_call_from_clang tenv obj_c_message_expr_info =
|
|
match obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer with
|
|
| Some pointer -> (
|
|
match method_signature_of_pointer tenv pointer with
|
|
| Some ms -> (
|
|
match ms.CMethodSignature.name with
|
|
| Typ.Procname.ObjC_Cpp objc_cpp ->
|
|
Some (Typ.Procname.ObjC_Cpp.get_class_type_name objc_cpp)
|
|
| _ ->
|
|
None )
|
|
| None ->
|
|
None )
|
|
| None ->
|
|
None
|
|
|
|
|
|
(* Get class name from a method call accorsing to the info given by the receiver kind *)
|
|
let get_class_name_method_call_from_receiver_kind context obj_c_message_expr_info act_params =
|
|
match obj_c_message_expr_info.Clang_ast_t.omei_receiver_kind with
|
|
| `Class qt ->
|
|
let sil_type = CType_decl.qual_type_to_sil_type context.CContext.tenv qt in
|
|
CType.objc_classname_of_type sil_type
|
|
| `Instance -> (
|
|
match act_params with
|
|
| (_, {Typ.desc= Tptr (t, _)}) :: _ | (_, t) :: _ ->
|
|
CType.objc_classname_of_type t
|
|
| _ ->
|
|
assert false )
|
|
| `SuperInstance ->
|
|
get_superclass_curr_class_objc context
|
|
| `SuperClass ->
|
|
get_superclass_curr_class_objc context
|
|
|
|
|
|
let get_objc_method_data obj_c_message_expr_info =
|
|
let selector = obj_c_message_expr_info.Clang_ast_t.omei_selector in
|
|
let pointer = obj_c_message_expr_info.Clang_ast_t.omei_decl_pointer in
|
|
match obj_c_message_expr_info.Clang_ast_t.omei_receiver_kind with
|
|
| `Instance ->
|
|
(selector, pointer, MCVirtual)
|
|
| `SuperInstance ->
|
|
(selector, pointer, MCNoVirtual)
|
|
| `Class _ | `SuperClass ->
|
|
(selector, pointer, MCStatic)
|
|
|
|
|
|
let sil_func_attributes_of_attributes attrs =
|
|
let rec do_translation acc al =
|
|
match al with
|
|
| [] ->
|
|
List.rev acc
|
|
| Clang_ast_t.SentinelAttr attribute_info :: tl ->
|
|
let sentinel, null_pos =
|
|
match attribute_info.Clang_ast_t.ai_parameters with
|
|
| [a; b] ->
|
|
(int_of_string a, int_of_string b)
|
|
| _ ->
|
|
assert false
|
|
in
|
|
do_translation (PredSymb.FA_sentinel (sentinel, null_pos) :: acc) tl
|
|
| _ :: tl ->
|
|
do_translation acc tl
|
|
in
|
|
do_translation [] attrs
|
|
|
|
|
|
let should_create_procdesc cfg procname defined set_objc_accessor_attr =
|
|
match Typ.Procname.Hash.find cfg procname with
|
|
| previous_procdesc ->
|
|
let is_defined_previous = Procdesc.is_defined previous_procdesc in
|
|
if (defined || set_objc_accessor_attr) && not is_defined_previous then (
|
|
Typ.Procname.Hash.remove cfg procname ;
|
|
true )
|
|
else false
|
|
| exception Caml.Not_found ->
|
|
true
|
|
|
|
|
|
(** Returns a list of the indices of expressions in [args] which point to const-typed values. Each
|
|
index is offset by [shift]. *)
|
|
let get_const_params_indices ~shift params =
|
|
let i = ref shift in
|
|
let rec aux result = function
|
|
| [] ->
|
|
List.rev result
|
|
| ({is_pointer_to_const}: CMethodSignature.param_type) :: tl ->
|
|
incr i ;
|
|
if is_pointer_to_const then aux (!i - 1 :: result) tl else aux result tl
|
|
in
|
|
aux [] params
|
|
|
|
|
|
let get_byval_params_indices ~shift params =
|
|
List.filter_mapi params ~f:(fun index ({is_value}: CMethodSignature.param_type) ->
|
|
let index' = index + shift in
|
|
Option.some_if is_value index' )
|
|
|
|
|
|
let get_objc_property_accessor tenv ms =
|
|
let open Clang_ast_t in
|
|
match CAst_utils.get_decl_opt ms.CMethodSignature.pointer_to_property_opt with
|
|
| Some (ObjCPropertyDecl (_, _, obj_c_property_decl_info))
|
|
-> (
|
|
let ivar_decl_ref = obj_c_property_decl_info.Clang_ast_t.opdi_ivar_decl in
|
|
match CAst_utils.get_decl_opt_with_decl_ref ivar_decl_ref with
|
|
| Some (ObjCIvarDecl (_, name_decl_info, _, _, _))
|
|
-> (
|
|
let class_tname =
|
|
Typ.Name.Objc.from_qual_name
|
|
(QualifiedCppName.from_field_qualified_name
|
|
(QualifiedCppName.of_rev_list name_decl_info.ni_qual_name))
|
|
in
|
|
let field_name = CGeneral_utils.mk_class_field_name class_tname name_decl_info.ni_name in
|
|
match Tenv.lookup tenv class_tname with
|
|
| Some {fields}
|
|
-> (
|
|
let field_opt =
|
|
List.find ~f:(fun (name, _, _) -> Typ.Fieldname.equal name field_name) fields
|
|
in
|
|
match field_opt with
|
|
| Some field when CMethodSignature.is_getter ms ->
|
|
Some (ProcAttributes.Objc_getter field)
|
|
| Some field when CMethodSignature.is_setter ms ->
|
|
Some (ProcAttributes.Objc_setter field)
|
|
| _ ->
|
|
None )
|
|
| None ->
|
|
None )
|
|
| _ ->
|
|
None )
|
|
| _ ->
|
|
None
|
|
|
|
|
|
(** Creates a procedure description. *)
|
|
let create_local_procdesc ?(set_objc_accessor_attr= false) trans_unit_ctx cfg tenv ms fbody
|
|
captured =
|
|
let defined = not (List.is_empty fbody) in
|
|
let proc_name = ms.CMethodSignature.name in
|
|
let pname = Typ.Procname.to_string proc_name in
|
|
let attributes = sil_func_attributes_of_attributes ms.CMethodSignature.attributes in
|
|
let clang_method_kind = ms.CMethodSignature.method_kind in
|
|
let is_cpp_nothrow = ms.CMethodSignature.is_cpp_nothrow in
|
|
let access =
|
|
match ms.CMethodSignature.access with
|
|
| `None ->
|
|
PredSymb.Default
|
|
| `Private ->
|
|
PredSymb.Private
|
|
| `Protected ->
|
|
PredSymb.Protected
|
|
| `Public ->
|
|
PredSymb.Protected
|
|
in
|
|
let create_new_procdesc () =
|
|
let all_params = Option.to_list ms.CMethodSignature.class_param @ ms.CMethodSignature.params in
|
|
let params_annots =
|
|
List.map ~f:(fun ({annot}: CMethodSignature.param_type) -> annot) all_params
|
|
in
|
|
let return_annot = snd ms.CMethodSignature.ret_type in
|
|
let has_added_return_param = ms.CMethodSignature.has_added_return_param in
|
|
let method_annotation = (return_annot, params_annots) in
|
|
let formals =
|
|
List.map ~f:(fun ({name; typ}: CMethodSignature.param_type) -> (name, typ)) all_params
|
|
in
|
|
let captured_mangled = List.map ~f:(fun (var, t) -> (Pvar.get_name var, t)) captured in
|
|
(* Captured variables for blocks are treated as parameters *)
|
|
let formals = captured_mangled @ formals in
|
|
let const_formals =
|
|
get_const_params_indices ~shift:(List.length captured_mangled) all_params
|
|
in
|
|
let by_vals = get_byval_params_indices ~shift:(List.length captured_mangled) all_params in
|
|
let source_range = ms.CMethodSignature.loc in
|
|
L.(debug Capture Verbose) "@\nCreating a new procdesc for function: '%s'@\n@." pname ;
|
|
L.(debug Capture Verbose) "@\nms = %a@\n@." CMethodSignature.pp ms ;
|
|
L.(debug Capture Verbose)
|
|
"@\nbyvals = [ %s ]@\n@."
|
|
(String.concat ~sep:", " (List.map by_vals ~f:string_of_int)) ;
|
|
let loc_start =
|
|
CLocation.location_of_source_range trans_unit_ctx.CFrontend_config.source_file source_range
|
|
in
|
|
let loc_exit =
|
|
CLocation.location_of_source_range ~pick_location:`End
|
|
trans_unit_ctx.CFrontend_config.source_file source_range
|
|
in
|
|
let ret_type = fst ms.CMethodSignature.ret_type in
|
|
let objc_property_accessor =
|
|
if set_objc_accessor_attr then get_objc_property_accessor tenv ms else None
|
|
in
|
|
let translation_unit = trans_unit_ctx.CFrontend_config.source_file in
|
|
let procdesc =
|
|
let proc_attributes =
|
|
{ (ProcAttributes.default translation_unit proc_name) with
|
|
ProcAttributes.captured= captured_mangled
|
|
; formals
|
|
; const_formals
|
|
; by_vals
|
|
; has_added_return_param
|
|
; access
|
|
; func_attributes= attributes
|
|
; is_defined= defined
|
|
; is_cpp_noexcept_method= is_cpp_nothrow
|
|
; is_model= Config.models_mode
|
|
; is_variadic= ms.CMethodSignature.is_variadic
|
|
; loc= loc_start
|
|
; clang_method_kind
|
|
; objc_accessor= objc_property_accessor
|
|
; method_annotation
|
|
; ret_type }
|
|
in
|
|
Cfg.create_proc_desc cfg proc_attributes
|
|
in
|
|
if defined then (
|
|
let start_kind = Procdesc.Node.Start_node proc_name in
|
|
let start_node = Procdesc.create_node procdesc loc_start start_kind [] in
|
|
let exit_kind = Procdesc.Node.Exit_node proc_name in
|
|
let exit_node = Procdesc.create_node procdesc loc_exit exit_kind [] in
|
|
Procdesc.set_start_node procdesc start_node ;
|
|
Procdesc.set_exit_node procdesc exit_node )
|
|
in
|
|
if should_create_procdesc cfg proc_name defined set_objc_accessor_attr then (
|
|
create_new_procdesc () ; true )
|
|
else false
|
|
|
|
|
|
(** Create a procdesc for objc methods whose signature cannot be found. *)
|
|
let create_external_procdesc trans_unit_ctx cfg proc_name clang_method_kind type_opt =
|
|
if not (Typ.Procname.Hash.mem cfg proc_name) then
|
|
let ret_type, formals =
|
|
match type_opt with
|
|
| Some (ret_type, arg_types) ->
|
|
(ret_type, List.map ~f:(fun typ -> (Mangled.from_string "x", typ)) arg_types)
|
|
| None ->
|
|
(Typ.mk Typ.Tvoid, [])
|
|
in
|
|
let proc_attributes =
|
|
{ (ProcAttributes.default trans_unit_ctx.CFrontend_config.source_file proc_name) with
|
|
ProcAttributes.formals; clang_method_kind; ret_type }
|
|
in
|
|
ignore (Cfg.create_proc_desc cfg proc_attributes)
|
|
|
|
|
|
let create_procdesc_with_pointer context pointer class_name_opt name =
|
|
let open CContext in
|
|
match method_signature_of_pointer context.tenv pointer with
|
|
| Some callee_ms ->
|
|
ignore
|
|
(create_local_procdesc context.translation_unit_context context.cfg context.tenv callee_ms
|
|
[] []) ;
|
|
callee_ms.CMethodSignature.name
|
|
| None ->
|
|
let callee_name, method_kind =
|
|
match class_name_opt with
|
|
| Some class_name ->
|
|
( CType_decl.CProcname.NoAstDecl.cpp_method_of_string context.tenv class_name name
|
|
, ClangMethodKind.CPP_INSTANCE )
|
|
| None ->
|
|
( CType_decl.CProcname.NoAstDecl.c_function_of_string context.tenv name
|
|
, ClangMethodKind.C_FUNCTION )
|
|
in
|
|
create_external_procdesc context.translation_unit_context context.cfg callee_name method_kind
|
|
None ;
|
|
callee_name
|
|
|
|
|
|
let get_procname_from_cpp_lambda context dec =
|
|
match dec with
|
|
| Clang_ast_t.CXXRecordDecl (_, _, _, _, _, _, _, cxx_rdi) -> (
|
|
match cxx_rdi.xrdi_lambda_call_operator with
|
|
| Some dr ->
|
|
let name_info, decl_ptr, _ = CAst_utils.get_info_from_decl_ref dr in
|
|
create_procdesc_with_pointer context decl_ptr None name_info.ni_name
|
|
| _ ->
|
|
assert false )
|
|
| _ ->
|
|
assert false
|
|
|
|
|
|
let get_captures_from_cpp_lambda dec =
|
|
match dec with
|
|
| Clang_ast_t.CXXRecordDecl (_, _, _, _, _, _, _, cxx_rdi) ->
|
|
cxx_rdi.xrdi_lambda_captures
|
|
| _ ->
|
|
assert false
|