[clang] Add is_instance as part of the type of objc methods

Summary:
The methods in objc can have the same name in the same class, but one be instance and the other class,
so that we need to take the instance flag into account when defining unique names for ObjC methods.
master
Dulma Rodriguez 9 years ago
parent 53125b171a
commit 732d23ce4d

@ -30,6 +30,19 @@ type java_signature = {
kind: method_kind kind: method_kind
} }
type objc_method_kind =
| Instance_objc_method
| Class_objc_method
let mangled_of_objc_method_kind kind =
match kind with
| Instance_objc_method -> Some "instance"
| Class_objc_method -> Some "class"
let objc_method_kind_of_bool is_instance =
if is_instance then Instance_objc_method
else Class_objc_method
(* C++/ObjC method signature *) (* C++/ObjC method signature *)
type c_method_signature = { type c_method_signature = {
class_name: string; class_name: string;
@ -117,10 +130,19 @@ let java_sig_compare js1 js2 =
|> next java_return_type_compare js1.returntype js2.returntype |> next java_return_type_compare js1.returntype js2.returntype
|> next method_kind_compare js1.kind js2.kind |> next method_kind_compare js1.kind js2.kind
let c_function_mangled_compare mangled1 mangled2 =
match mangled1, mangled2 with
| Some mangled1, None -> 1
| None, Some mangled2 -> -1
| None, None -> 0
| Some mangled1, Some mangled2 ->
string_compare mangled1 mangled2
(** Compare c_method signatures. *) (** Compare c_method signatures. *)
let c_meth_sig_compare osig1 osig2 = let c_meth_sig_compare osig1 osig2 =
let n = string_compare osig1.class_name osig2.class_name in string_compare osig1.method_name osig2.method_name
if n <> 0 then n else string_compare osig1.method_name osig2.method_name |> next string_compare osig1.class_name osig2.class_name
|> next c_function_mangled_compare osig1.mangled osig2.mangled
(** Given a package.classname string, it looks for the latest dot and split the string in two (package, classname) *) (** Given a package.classname string, it looks for the latest dot and split the string in two (package, classname) *)
let split_classname package_classname = let split_classname package_classname =
@ -357,9 +379,9 @@ let to_readable_string (c1, c2) verbose =
let c_method_to_string osig detail_level = let c_method_to_string osig detail_level =
match detail_level with match detail_level with
| SIMPLE -> | SIMPLE -> osig.method_name
osig.method_name | NON_VERBOSE -> osig.class_name ^ "_" ^ osig.method_name
| VERBOSE | NON_VERBOSE -> | VERBOSE ->
let m_str = match osig.mangled with let m_str = match osig.mangled with
| None -> "" | None -> ""
| Some s -> "{" ^ s ^ "}" in | Some s -> "{" ^ s ^ "}" in

@ -21,6 +21,16 @@ type method_kind =
| Static (* in Java, procedures called with invokestatic *) | Static (* in Java, procedures called with invokestatic *)
| Non_Static (* in Java, procedures called with invokevirtual, invokespecial, and invokeinterface *) | Non_Static (* in Java, procedures called with invokevirtual, invokespecial, and invokeinterface *)
type objc_method_kind =
| Instance_objc_method (* for instance methods in ObjC *)
| Class_objc_method (* for class methods in ObjC *)
(** Mangled string for method types *)
val mangled_of_objc_method_kind : objc_method_kind -> string option
(** Create ObjC method type from a bool is_instance *)
val objc_method_kind_of_bool : bool -> objc_method_kind
(** Comparison for proc names *) (** Comparison for proc names *)
val compare : t -> t -> int val compare : t -> t -> int

@ -511,13 +511,11 @@ let check_inherently_dangerous_function caller_pname callee_pname =
let pre_opt = State.get_normalized_pre (Abs.abstract_no_symop caller_pname) in let pre_opt = State.get_normalized_pre (Abs.abstract_no_symop caller_pname) in
Reporting.log_warning caller_pname ~pre: pre_opt exn Reporting.log_warning caller_pname ~pre: pre_opt exn
let is_defined cfg pname = let is_defined cfg pname =
match Cfg.Procdesc.find_from_name cfg pname with match Cfg.Procdesc.find_from_name cfg pname with
| None -> false | None -> false
| Some pdesc -> Cfg.Procdesc.is_defined pdesc | Some pdesc -> Cfg.Procdesc.is_defined pdesc
let call_should_be_skipped callee_pname summary = let call_should_be_skipped callee_pname summary =
(* skip all procedures in intra-precedural mode *) (* skip all procedures in intra-precedural mode *)
!Config.intraprocedural !Config.intraprocedural
@ -528,7 +526,6 @@ let call_should_be_skipped callee_pname summary =
(* treat calls with no specs as skip functions in angelic mode *) (* treat calls with no specs as skip functions in angelic mode *)
|| (!Config.angelic_execution && Specs.get_specs_from_payload summary == []) || (!Config.angelic_execution && Specs.get_specs_from_payload summary == [])
let report_raise_memory_leak tenv msg hpred prop = let report_raise_memory_leak tenv msg hpred prop =
L.d_strln msg; L.d_strln msg;
L.d_increase_indent 1; L.d_increase_indent 1;
@ -663,7 +660,6 @@ let method_exists right_proc_name methods =
else (* ObjC case *) else (* ObjC case *)
Specs.summary_exists right_proc_name Specs.summary_exists right_proc_name
let resolve_method tenv class_name proc_name = let resolve_method tenv class_name proc_name =
let found_class = let found_class =
let visited = ref Mangled.MangledSet.empty in let visited = ref Mangled.MangledSet.empty in
@ -694,7 +690,6 @@ let resolve_method tenv class_name proc_name =
proc_name proc_name
| Some proc_name -> proc_name | Some proc_name -> proc_name
let resolve_typename prop arg = let resolve_typename prop arg =
let (arg_exp, _) = arg in let (arg_exp, _) = arg in
let typexp_opt = let typexp_opt =
@ -707,7 +702,6 @@ let resolve_typename prop arg =
| Some (Sil.Sizeof (Sil.Tstruct (_, _, Sil.Class, class_name_opt, _, _, _), _)) -> class_name_opt | Some (Sil.Sizeof (Sil.Tstruct (_, _, Sil.Class, class_name_opt, _, _, _), _)) -> class_name_opt
| _ -> None | _ -> None
(** If the dynamic type of the object calling a method is known, the method from the dynamic type (** If the dynamic type of the object calling a method is known, the method from the dynamic type
is called *) is called *)
let resolve_virtual_pname cfg tenv prop args pname : Procname.t = let resolve_virtual_pname cfg tenv prop args pname : Procname.t =
@ -1261,7 +1255,6 @@ and sym_exe_check_variadic_sentinel ?(fails_on_nil=false) cfg pdesc tenv prop pa
(* error on the first premature nil argument *) (* error on the first premature nil argument *)
list_fold_left check_allocated [(prop, path)] non_terminal_argsi list_fold_left check_allocated [(prop, path)] non_terminal_argsi
and sym_exe_check_variadic_sentinel_if_present cfg pdesc tenv prop path actual_params callee_pname loc = and sym_exe_check_variadic_sentinel_if_present cfg pdesc tenv prop path actual_params callee_pname loc =
match Cfg.Procdesc.find_from_name cfg callee_pname with match Cfg.Procdesc.find_from_name cfg callee_pname with
| None -> [(prop, path)] | None -> [(prop, path)]
@ -2321,12 +2314,14 @@ module ModelBuiltins = struct
execute_objc_NSArray_alloc_no_fail cfg pdesc tenv res' ret_ids loc execute_objc_NSArray_alloc_no_fail cfg pdesc tenv res' ret_ids loc
let _ = let _ =
let method_kind = Procname.mangled_of_objc_method_kind Procname.Class_objc_method in
Builtin.register_procname Builtin.register_procname
(Procname.mangled_c_method "NSArray" "arrayWithObjects:count:" None) (Procname.mangled_c_method "NSArray" "arrayWithObjects:count:" method_kind)
execute_NSArray_arrayWithObjects_count execute_NSArray_arrayWithObjects_count
let _ = let _ =
let method_kind = Procname.mangled_of_objc_method_kind Procname.Class_objc_method in
Builtin.register_procname Builtin.register_procname
(Procname.mangled_c_method "NSArray" "arrayWithObjects:" None) (Procname.mangled_c_method "NSArray" "arrayWithObjects:" method_kind)
execute_NSArray_arrayWithObjects execute_NSArray_arrayWithObjects
end end
(* ============== END of ModelBuiltins ============== *) (* ============== END of ModelBuiltins ============== *)

@ -171,7 +171,9 @@ struct
let process_objc_method_decl tenv cg cfg namespace curr_class decl_info name_info method_decl_info = let process_objc_method_decl tenv cg cfg namespace curr_class decl_info name_info method_decl_info =
let class_name = CContext.get_curr_class_name curr_class in let class_name = CContext.get_curr_class_name curr_class in
let method_name = name_info.Clang_ast_t.ni_name in let method_name = name_info.Clang_ast_t.ni_name in
let procname = CMethod_trans.mk_procname_from_method class_name method_name in let is_instance = method_decl_info.Clang_ast_t.omdi_is_instance_method in
let method_kind = Procname.objc_method_kind_of_bool is_instance in
let procname = CMethod_trans.mk_procname_from_method class_name method_name method_kind in
let method_decl = Meth_decl_info (method_decl_info, class_name) in let method_decl = Meth_decl_info (method_decl_info, class_name) in
let is_generated = Ast_utils.is_generated name_info in let is_generated = Ast_utils.is_generated name_info in
let ms = build_method_signature decl_info procname method_decl false false is_generated in let ms = build_method_signature decl_info procname method_decl false false is_generated in

@ -30,25 +30,9 @@ let mk_procname_from_function name type_name =
let type_name_crc = CRC.crc16 type_name in let type_name_crc = CRC.crc16 type_name in
Procname.mangled_c_fun name type_name_crc Procname.mangled_c_fun name type_name_crc
let mk_procname_from_method class_name method_name = let mk_procname_from_method class_name method_name method_kind =
Procname.mangled_c_method class_name method_name None let mangled = Procname.mangled_of_objc_method_kind method_kind in
Procname.mangled_c_method class_name method_name mangled
let resolve_method_class tenv class_name method_name =
let type_name = Sil.TN_csu (Sil.Class, class_name) in
match Sil.tenv_lookup tenv type_name with
| Some (Sil.Tstruct (_, _, Sil.Class, cls, super_classes, methods, iann)) ->
Some type_name
| _ -> None
let resolve_method tenv class_name method_name =
let class_name_mangled = Mangled.from_string class_name in
match resolve_method_class tenv class_name_mangled method_name with
| Some (Sil.TN_csu (Sil.Class, class_name)) ->
let class_method_name = mk_procname_from_method (Mangled.to_string class_name) method_name in
(try let ms = CMethod_signature.find class_method_name in
Some ms
with Not_found -> None)
| _ -> None
let get_superclass_curr_class context = let get_superclass_curr_class context =
let retrive_super cname super_opt = let retrive_super cname super_opt =

@ -27,13 +27,11 @@ val create_external_procdesc : Cfg.cfg -> Procname.t -> bool -> (Sil.typ * Sil.t
val captured_vars_from_block_info : CContext.t -> Clang_ast_t.block_captured_variable list -> (Mangled.t * Sil.typ * bool) list val captured_vars_from_block_info : CContext.t -> Clang_ast_t.block_captured_variable list -> (Mangled.t * Sil.typ * bool) list
val mk_procname_from_method : string -> string -> Procname.t val mk_procname_from_method : string -> string -> Procname.objc_method_kind -> Procname.t
val mk_procname_from_function : string -> string -> Procname.t val mk_procname_from_function : string -> string -> Procname.t
val get_class_selector_instance : CContext.t -> Clang_ast_t.obj_c_message_expr_info -> (Sil.exp * Sil.typ) list val get_class_selector_instance : CContext.t -> Clang_ast_t.obj_c_message_expr_info -> (Sil.exp * Sil.typ) list
-> (string * string * method_call_type) -> (string * string * method_call_type)
val resolve_method : Sil.tenv -> string -> string -> CMethod_signature.method_signature option
val should_create_procdesc : Cfg.cfg -> Procname.t -> bool -> bool -> bool val should_create_procdesc : Cfg.cfg -> Procname.t -> bool -> bool -> bool

@ -43,21 +43,21 @@ struct
let (class_name, method_name, mc_type) = let (class_name, method_name, mc_type) =
CMethod_trans.get_class_selector_instance context obj_c_message_expr_info act_params in CMethod_trans.get_class_selector_instance context obj_c_message_expr_info act_params in
let is_instance = mc_type != CMethod_trans.MCStatic in let is_instance = mc_type != CMethod_trans.MCStatic in
let method_kind = Procname.objc_method_kind_of_bool is_instance in
match CTrans_models.get_predefined_model_method_signature class_name method_name match CTrans_models.get_predefined_model_method_signature class_name method_name
CMethod_trans.mk_procname_from_method with CMethod_trans.mk_procname_from_method with
| Some ms -> | Some ms ->
ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv ms [] [] is_instance); ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv ms [] [] is_instance);
CMethod_signature.ms_get_name ms, CMethod_trans.MCNoVirtual CMethod_signature.ms_get_name ms, CMethod_trans.MCNoVirtual
| None -> | None ->
match CMethod_trans.resolve_method context.tenv class_name method_name with let procname = CMethod_trans.mk_procname_from_method class_name method_name method_kind in
| Some callee_ms -> try
(let procname = CMethod_signature.ms_get_name callee_ms in let callee_ms = CMethod_signature.find procname in
if not (M.process_getter_setter context procname) then if not (M.process_getter_setter context procname) then
(let is_instance = is_instance || (CMethod_signature.ms_is_instance callee_ms) in (ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv callee_ms [] [] is_instance));
ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv callee_ms [] [] is_instance)); procname, mc_type
procname, mc_type) with Not_found ->
| None -> let callee_pn = CMethod_trans.mk_procname_from_method class_name method_name method_kind in
let callee_pn = CMethod_trans.mk_procname_from_method class_name method_name in
CMethod_trans.create_external_procdesc context.cfg callee_pn is_instance None; CMethod_trans.create_external_procdesc context.cfg callee_pn is_instance None;
callee_pn, mc_type callee_pn, mc_type

@ -121,13 +121,13 @@ let is_assert_log sil_fe =
let is_objc_memory_model_controlled o = let is_objc_memory_model_controlled o =
Core_foundation_model.is_objc_memory_model_controlled o Core_foundation_model.is_objc_memory_model_controlled o
let get_predefined_ms_method condition class_name method_name mk_procname let get_predefined_ms_method condition class_name method_name method_kind mk_procname
arguments return_type attributes builtin = arguments return_type attributes builtin =
if condition then if condition then
let procname = let procname =
match builtin with match builtin with
| Some procname -> procname | Some procname -> procname
| None -> mk_procname class_name method_name in | None -> mk_procname class_name method_name method_kind in
let ms = CMethod_signature.make_ms procname arguments return_type attributes let ms = CMethod_signature.make_ms procname arguments return_type attributes
(Ast_expressions.dummy_source_range ()) false false in (Ast_expressions.dummy_source_range ()) false false in
Some ms Some ms
@ -135,26 +135,27 @@ let get_predefined_ms_method condition class_name method_name mk_procname
let get_predefined_ms_stringWithUTF8String class_name method_name mk_procname = let get_predefined_ms_stringWithUTF8String class_name method_name mk_procname =
let condition = class_name = nsstring_cl && method_name = string_with_utf8_m in let condition = class_name = nsstring_cl && method_name = string_with_utf8_m in
get_predefined_ms_method condition class_name method_name mk_procname [("x", "char *", None)] get_predefined_ms_method condition class_name method_name Procname.Class_objc_method
id_cl [] None mk_procname [("x", "char *", None)] id_cl [] None
let get_predefined_ms_retain_release class_name method_name mk_procname = let get_predefined_ms_retain_release class_name method_name mk_procname =
let condition = is_retain_or_release method_name in let condition = is_retain_or_release method_name in
let return_type = let return_type =
if is_retain_method method_name || is_autorelease_method method_name if is_retain_method method_name || is_autorelease_method method_name
then id_cl else void in then id_cl else void in
get_predefined_ms_method condition nsobject_cl method_name mk_procname get_predefined_ms_method condition nsobject_cl method_name Procname.Instance_objc_method
[(self, class_name, None)] return_type [] (get_builtinname method_name) mk_procname [(self, class_name, None)] return_type [] (get_builtinname method_name)
let get_predefined_ms_autoreleasepool_init class_name method_name mk_procname = let get_predefined_ms_autoreleasepool_init class_name method_name mk_procname =
let condition = (method_name = init) && (class_name = nsautorelease_pool_cl) in let condition = (method_name = init) && (class_name = nsautorelease_pool_cl) in
get_predefined_ms_method condition class_name method_name mk_procname get_predefined_ms_method condition class_name method_name Procname.Instance_objc_method
[(self, class_name, None)] void [] None mk_procname [(self, class_name, None)] void [] None
let get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname = let get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname =
let condition = (method_name = release || method_name = drain) && let condition = (method_name = release || method_name = drain) &&
(class_name = nsautorelease_pool_cl) in (class_name = nsautorelease_pool_cl) in
get_predefined_ms_method condition class_name method_name mk_procname [(self, class_name, None)] get_predefined_ms_method condition class_name method_name Procname.Instance_objc_method
mk_procname [(self, class_name, None)]
void [] (Some SymExec.ModelBuiltins.__objc_release_autorelease_pool) void [] (Some SymExec.ModelBuiltins.__objc_release_autorelease_pool)
let get_predefined_model_method_signature class_name method_name mk_procname = let get_predefined_model_method_signature class_name method_name mk_procname =

@ -25,7 +25,8 @@ val is_modeled_builtin : string -> bool
val is_toll_free_bridging : Procname.t option -> bool val is_toll_free_bridging : Procname.t option -> bool
val get_predefined_model_method_signature : string -> string -> (string -> string -> Procname.t) -> val get_predefined_model_method_signature : string -> string ->
(string -> string -> Procname.objc_method_kind -> Procname.t) ->
CMethod_signature.method_signature option CMethod_signature.method_signature option
val is_dispatch_function_name : string -> (string * int) option val is_dispatch_function_name : string -> (string * int) option

@ -303,7 +303,7 @@ let new_trans trans_state loc stmt_info cls_name function_type =
let init_ret_id = Ident.create_fresh Ident.knormal in let init_ret_id = Ident.create_fresh Ident.knormal in
let is_instance = true in let is_instance = true in
let call_flags = { Sil.cf_virtual = is_instance; Sil.cf_noreturn = false; Sil.cf_is_objc_block = false; } in let call_flags = { Sil.cf_virtual = is_instance; Sil.cf_noreturn = false; Sil.cf_is_objc_block = false; } in
let pname = CMethod_trans.mk_procname_from_method cls_name CFrontend_config.init in let pname = CMethod_trans.mk_procname_from_method cls_name CFrontend_config.init Procname.Instance_objc_method in
CMethod_trans.create_external_procdesc trans_state.context.cfg pname is_instance None; CMethod_trans.create_external_procdesc trans_state.context.cfg pname is_instance None;
let args = [(Sil.Var alloc_ret_id, alloc_ret_type)] in let args = [(Sil.Var alloc_ret_id, alloc_ret_type)] in
let init_stmt_call = Sil.Call([init_ret_id], (Sil.Const (Sil.Cfun pname)), args, loc, call_flags) in let init_stmt_call = Sil.Call([init_ret_id], (Sil.Const (Sil.Cfun pname)), args, loc, call_flags) in

@ -243,15 +243,6 @@ let prepare_dynamic_property curr_class decl_info property_impl_decl_info =
(* No names of fields/method to collect from ObjCPropertyImplDecl when Synthesized *) (* No names of fields/method to collect from ObjCPropertyImplDecl when Synthesized *)
[] []
(*NOTE: Assumption: if there is a getter or a setter defined manually, *)
(* it has been translated already at this point. *)
let method_exists cfg class_name name attributes =
let procname = CMethod_trans.mk_procname_from_method class_name name in
match Cfg.Procdesc.find_from_name cfg procname with
| Some procdesc ->
Cfg.Procdesc.is_defined procdesc
| None -> false
let is_property_read_only attributes = let is_property_read_only attributes =
list_mem (Ast_utils.property_attribute_eq) `Readonly attributes list_mem (Ast_utils.property_attribute_eq) `Readonly attributes
@ -374,10 +365,12 @@ let get_methods curr_class decl_list =
let get_method decl list_methods = let get_method decl list_methods =
match decl with match decl with
ObjCMethodDecl(decl_info, name_info, method_decl_info) as d -> ObjCMethodDecl(decl_info, name_info, method_decl_info) as d ->
let is_instance = method_decl_info.Clang_ast_t.omdi_is_instance_method in
let method_kind = Procname.objc_method_kind_of_bool is_instance in
let method_name = name_info.Clang_ast_t.ni_name in let method_name = name_info.Clang_ast_t.ni_name in
Printing.log_out " ...Adding Method '%s' \n" (class_name^"_"^method_name); Printing.log_out " ...Adding Method '%s' \n" (class_name^"_"^method_name);
let _ = check_for_property curr_class method_name d method_decl_info.Clang_ast_t.omdi_body in let _ = check_for_property curr_class method_name d method_decl_info.Clang_ast_t.omdi_body in
let meth_name = CMethod_trans.mk_procname_from_method class_name method_name in let meth_name = CMethod_trans.mk_procname_from_method class_name method_name method_kind in
meth_name:: list_methods meth_name:: list_methods
| _ -> list_methods in | _ -> list_methods in
list_fold_right get_method decl_list [] list_fold_right get_method decl_list []

@ -0,0 +1,43 @@
/*
* Copyright (c) 2014 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/NSObject.h>
@interface A : NSObject {
@public int x;
}
+(A*) meth;
@property (nonatomic, readonly) A* meth;
@end
@implementation A
+(A*) meth {
return [A new];
}
-(A*) meth {
return nil;
}
@end
int test() {
A* para = [A new];
A *a = [para meth];
return a->x;
}
int test2(A* para) {
A *a = [A meth];
return a->x;
}

@ -0,0 +1,68 @@
/*
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package endtoend.objc;
import static org.hamcrest.MatcherAssert.assertThat;
import static utils.matchers.ResultContainsExactly.containsExactly;
import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain;
import static utils.matchers.ResultContainsErrorInMethod.contains;
import com.google.common.collect.ImmutableList;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import java.io.IOException;
import utils.DebuggableTemporaryFolder;
import utils.InferException;
import utils.InferResults;
import utils.InferRunner;
public class NPEEqualNamesTest {
public static final String NPE_FILE =
"infer/tests/codetoanalyze/objc/errors/npe/Npe_with_equal_names.m";
private static ImmutableList<String> inferCmdNPD;
public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE";
@ClassRule
public static DebuggableTemporaryFolder folderNPD = new DebuggableTemporaryFolder();
@BeforeClass
public static void runInfer() throws InterruptedException, IOException {
inferCmdNPD = InferRunner.createiOSInferCommandWithMLBuckets(
folderNPD,
NPE_FILE,
"cf",
false);
}
@Test
public void nullDereferenceTest() throws InterruptedException, IOException, InferException {
InferResults inferResults = InferRunner.runInferObjC(inferCmdNPD);
String[] procedures = {
"test",
};
assertThat(
"Results should contain null pointer dereference error",
inferResults,
containsExactly(
NULL_DEREFERENCE,
NPE_FILE,
procedures
)
);
}
}
Loading…
Cancel
Save