[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

@ -1,12 +1,12 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* 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.
*)
* Copyright (c) 2009 - 2013 Monoidics ltd.
* 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.
*)
(** Module for Procedure Names *)
@ -30,6 +30,19 @@ type java_signature = {
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 *)
type c_method_signature = {
class_name: string;
@ -117,10 +130,19 @@ let java_sig_compare js1 js2 =
|> next java_return_type_compare js1.returntype js2.returntype
|> 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. *)
let c_meth_sig_compare osig1 osig2 =
let n = string_compare osig1.class_name osig2.class_name in
if n <> 0 then n else string_compare osig1.method_name osig2.method_name
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) *)
let split_classname package_classname =
@ -357,9 +379,9 @@ let to_readable_string (c1, c2) verbose =
let c_method_to_string osig detail_level =
match detail_level with
| SIMPLE ->
osig.method_name
| VERBOSE | NON_VERBOSE ->
| SIMPLE -> osig.method_name
| NON_VERBOSE -> osig.class_name ^ "_" ^ osig.method_name
| VERBOSE ->
let m_str = match osig.mangled with
| None -> ""
| Some s -> "{" ^ s ^ "}" in

@ -21,6 +21,16 @@ type method_kind =
| Static (* in Java, procedures called with invokestatic *)
| 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 *)
val compare : t -> t -> int

@ -1,12 +1,12 @@
(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* 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.
*)
* Copyright (c) 2009 - 2013 Monoidics ltd.
* 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.
*)
(** Symbolic Execution *)
@ -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
Reporting.log_warning caller_pname ~pre: pre_opt exn
let is_defined cfg pname =
match Cfg.Procdesc.find_from_name cfg pname with
| None -> false
| Some pdesc -> Cfg.Procdesc.is_defined pdesc
let call_should_be_skipped callee_pname summary =
(* skip all procedures in intra-precedural mode *)
!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 *)
|| (!Config.angelic_execution && Specs.get_specs_from_payload summary == [])
let report_raise_memory_leak tenv msg hpred prop =
L.d_strln msg;
L.d_increase_indent 1;
@ -663,7 +660,6 @@ let method_exists right_proc_name methods =
else (* ObjC case *)
Specs.summary_exists right_proc_name
let resolve_method tenv class_name proc_name =
let found_class =
let visited = ref Mangled.MangledSet.empty in
@ -694,7 +690,6 @@ let resolve_method tenv class_name proc_name =
proc_name
| Some proc_name -> proc_name
let resolve_typename prop arg =
let (arg_exp, _) = arg in
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
| _ -> None
(** If the dynamic type of the object calling a method is known, the method from the dynamic type
is called *)
let resolve_virtual_pname cfg tenv prop args pname : Procname.t =
@ -1226,7 +1220,7 @@ and call_unknown_or_scan is_scan cfg pdesc tenv pre path
let path_pos = State.get_path_pos () in
[(Prop.mark_vars_as_undefined pre''' exps_to_mark callee_pname loc path_pos, path)]
and sym_exe_check_variadic_sentinel ?(fails_on_nil=false) cfg pdesc tenv prop path n_formals actual_params (sentinel, null_pos) callee_pname loc =
and sym_exe_check_variadic_sentinel ?(fails_on_nil = false) cfg pdesc tenv prop path n_formals actual_params (sentinel, null_pos) callee_pname loc =
(* from clang's lib/Sema/SemaExpr.cpp: *)
(* "nullPos" is the number of formal parameters at the end which *)
(* effectively count as part of the variadic arguments. This is *)
@ -1237,8 +1231,8 @@ and sym_exe_check_variadic_sentinel ?(fails_on_nil=false) cfg pdesc tenv prop pa
(* sentinels start counting from the last argument to the function *)
let sentinel_pos = nargs - sentinel - 1 in
let mk_non_terminal_argsi (acc, i) a =
if i < first_var_arg_pos || i >= sentinel_pos then (acc, i+1)
else ((a,i)::acc, i+1) in
if i < first_var_arg_pos || i >= sentinel_pos then (acc, i +1)
else ((a, i):: acc, i +1) in
(* list_fold_left reverses the arguments *)
let non_terminal_argsi = fst (list_fold_left mk_non_terminal_argsi ([], 0) actual_params) in
let check_allocated result ((lexp, typ), i) =
@ -1261,14 +1255,13 @@ and sym_exe_check_variadic_sentinel ?(fails_on_nil=false) cfg pdesc tenv prop pa
(* error on the first premature nil argument *)
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 =
match Cfg.Procdesc.find_from_name cfg callee_pname with
| None -> [(prop, path)]
| Some callee_pdesc ->
let proc_attributes = Cfg.Procdesc.get_attributes callee_pdesc in
match Sil.get_sentinel_func_attribute_value proc_attributes.Sil.func_attributes with
| None -> [(prop,path)]
| None -> [(prop, path)]
| Some sentinel_arg ->
let formals = Cfg.Procdesc.get_formals callee_pdesc in
sym_exe_check_variadic_sentinel cfg pdesc tenv prop path (list_length formals) actual_params sentinel_arg callee_pname loc
@ -2312,7 +2305,7 @@ module ModelBuiltins = struct
let execute_NSArray_arrayWithObjects_count cfg pdesc instr tenv prop path ret_ids args callee_pname loc =
let n_formals = 1 in
let res' = sym_exe_check_variadic_sentinel ~fails_on_nil:true cfg pdesc tenv prop path n_formals args (0,1) callee_pname loc in
let res' = sym_exe_check_variadic_sentinel ~fails_on_nil: true cfg pdesc tenv prop path n_formals args (0,1) callee_pname loc in
execute_objc_NSArray_alloc_no_fail cfg pdesc tenv res' ret_ids loc
let execute_NSArray_arrayWithObjects cfg pdesc instr tenv prop path ret_ids args callee_pname loc =
@ -2321,12 +2314,14 @@ module ModelBuiltins = struct
execute_objc_NSArray_alloc_no_fail cfg pdesc tenv res' ret_ids loc
let _ =
let method_kind = Procname.mangled_of_objc_method_kind Procname.Class_objc_method in
Builtin.register_procname
(Procname.mangled_c_method "NSArray" "arrayWithObjects:count:" None)
(Procname.mangled_c_method "NSArray" "arrayWithObjects:count:" method_kind)
execute_NSArray_arrayWithObjects_count
let _ =
let method_kind = Procname.mangled_of_objc_method_kind Procname.Class_objc_method in
Builtin.register_procname
(Procname.mangled_c_method "NSArray" "arrayWithObjects:" None)
(Procname.mangled_c_method "NSArray" "arrayWithObjects:" method_kind)
execute_NSArray_arrayWithObjects
end
(* ============== 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 class_name = CContext.get_curr_class_name curr_class 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 is_generated = Ast_utils.is_generated name_info 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
Procname.mangled_c_fun name type_name_crc
let mk_procname_from_method class_name method_name =
Procname.mangled_c_method class_name method_name None
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 mk_procname_from_method class_name method_name method_kind =
let mangled = Procname.mangled_of_objc_method_kind method_kind in
Procname.mangled_c_method class_name method_name mangled
let get_superclass_curr_class context =
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 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 get_class_selector_instance : CContext.t -> Clang_ast_t.obj_c_message_expr_info -> (Sil.exp * Sil.typ) list
-> (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

@ -43,21 +43,21 @@ struct
let (class_name, method_name, mc_type) =
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 method_kind = Procname.objc_method_kind_of_bool is_instance in
match CTrans_models.get_predefined_model_method_signature class_name method_name
CMethod_trans.mk_procname_from_method with
| Some ms ->
ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv ms [] [] is_instance);
CMethod_signature.ms_get_name ms, CMethod_trans.MCNoVirtual
| None ->
match CMethod_trans.resolve_method context.tenv class_name method_name with
| Some callee_ms ->
(let procname = CMethod_signature.ms_get_name callee_ms in
let procname = CMethod_trans.mk_procname_from_method class_name method_name method_kind in
try
let callee_ms = CMethod_signature.find procname in
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));
procname, mc_type)
| None ->
let callee_pn = CMethod_trans.mk_procname_from_method class_name method_name in
(ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv callee_ms [] [] is_instance));
procname, mc_type
with Not_found ->
let callee_pn = CMethod_trans.mk_procname_from_method class_name method_name method_kind in
CMethod_trans.create_external_procdesc context.cfg callee_pn is_instance None;
callee_pn, mc_type

@ -121,13 +121,13 @@ let is_assert_log sil_fe =
let 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 =
if condition then
let procname =
match builtin with
| 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
(Ast_expressions.dummy_source_range ()) false false in
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 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)]
id_cl [] None
get_predefined_ms_method condition class_name method_name Procname.Class_objc_method
mk_procname [("x", "char *", None)] id_cl [] None
let get_predefined_ms_retain_release class_name method_name mk_procname =
let condition = is_retain_or_release method_name in
let return_type =
if is_retain_method method_name || is_autorelease_method method_name
then id_cl else void in
get_predefined_ms_method condition nsobject_cl method_name mk_procname
[(self, class_name, None)] return_type [] (get_builtinname method_name)
get_predefined_ms_method condition nsobject_cl method_name Procname.Instance_objc_method
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 condition = (method_name = init) && (class_name = nsautorelease_pool_cl) in
get_predefined_ms_method condition class_name method_name mk_procname
[(self, class_name, None)] void [] None
get_predefined_ms_method condition class_name method_name Procname.Instance_objc_method
mk_procname [(self, class_name, None)] void [] None
let get_predefined_ms_nsautoreleasepool_release class_name method_name mk_procname =
let condition = (method_name = release || method_name = drain) &&
(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)
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 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
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 is_instance = true 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;
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

@ -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 *)
[]
(*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 =
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 =
match decl with
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
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 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
| _ -> list_methods in
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