From 5cff9c91f970d0448178d7cc843149b32051a6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Tue, 10 Jul 2018 02:35:18 -0700 Subject: [PATCH] Add Java support to ProcnameDispatcher Reviewed By: mbouaziz Differential Revision: D8768497 fbshipit-source-id: 0f642f9 --- infer/src/IR/ProcnameDispatcher.ml | 86 ++++++++++++++----- .../src/bufferoverrun/bufferOverrunModels.ml | 5 +- .../java/performance/ArrayListTest.java | 17 ++++ .../codetoanalyze/java/performance/issues.exp | 2 + 4 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 infer/tests/codetoanalyze/java/performance/ArrayListTest.java diff --git a/infer/src/IR/ProcnameDispatcher.ml b/infer/src/IR/ProcnameDispatcher.ml index 957e9d07a..d914dc190 100644 --- a/infer/src/IR/ProcnameDispatcher.ml +++ b/infer/src/IR/ProcnameDispatcher.ml @@ -25,6 +25,8 @@ type c = Typ.Procname.C.t type objc_cpp = Typ.Procname.ObjC_Cpp.t +type java = Typ.Procname.Java.t + type qual_name = QualifiedCppName.t type templated_name = qual_name * Typ.template_arg list @@ -42,8 +44,6 @@ let template_args_of_template_spec_info = function args -exception DoNotHandleJavaYet - let templated_name_of_class_name class_name = let open Typ in match class_name with @@ -51,8 +51,16 @@ let templated_name_of_class_name class_name = (qual_name, []) | CppClass (qual_name, template_spec_info) -> (qual_name, template_args_of_template_spec_info template_spec_info) - | JavaClass _ -> - raise DoNotHandleJavaYet + | JavaClass mangled_name -> + (QualifiedCppName.of_list [Mangled.to_string mangled_name], []) + + +let templated_name_of_java java = + let qual_name = + QualifiedCppName.of_list + [Typ.Procname.Java.get_class_name java; Typ.Procname.Java.get_method java] + in + (qual_name, []) (* Intermediate matcher types *) @@ -446,7 +454,8 @@ module Call = struct type ('f_in, 'f_out, 'captured_types) proc_matcher = { on_objc_cpp: 'f_in -> objc_cpp -> ('f_out * 'captured_types) option - ; on_c: 'f_in -> c -> ('f_out * 'captured_types) option } + ; on_c: 'f_in -> c -> ('f_out * 'captured_types) option + ; on_java: 'f_in -> java -> ('f_out * 'captured_types) option } type ('f_in, 'f_out, 'captured_types) on_args = 'captured_types -> 'f_in * FuncArg.t list -> ('f_out * FuncArg.t list) option @@ -475,7 +484,9 @@ module Call = struct ; marker_static_checker: 'markers -> bool } type 'f matcher = - {on_objc_cpp: objc_cpp -> FuncArg.t list -> 'f option; on_c: c -> FuncArg.t list -> 'f option} + { on_objc_cpp: objc_cpp -> FuncArg.t list -> 'f option + ; on_c: c -> FuncArg.t list -> 'f option + ; on_java: java -> FuncArg.t list -> 'f option } type 'f pre_result = DoesNotMatch | Matches of 'f | RetryWith of 'f matcher @@ -489,7 +500,8 @@ module Call = struct type ('f_in, 'f_out) all_args_matcher = { on_objc_cpp: 'f_in -> objc_cpp -> FuncArg.t list -> 'f_out pre_result - ; on_c: 'f_in -> c -> FuncArg.t list -> 'f_out pre_result } + ; on_c: 'f_in -> c -> FuncArg.t list -> 'f_out pre_result + ; on_java: 'f_in -> java -> FuncArg.t list -> 'f_out pre_result } type 'f dispatcher = Typ.Procname.t -> FuncArg.t list -> 'f option @@ -505,8 +517,11 @@ module Call = struct let template_args = template_args_of_template_spec_info c.template_args in on_templated_name f (c.name, template_args) |> Option.map ~f:get_captures in + let on_java f (java: java) = + on_templated_name f (templated_name_of_java java) |> Option.map ~f:get_captures + in let on_objc_cpp f objc_cpp = on_objc_cpp f objc_cpp |> Option.map ~f:get_captures in - let on_proc : (_, _, _) proc_matcher = {on_objc_cpp; on_c} in + let on_proc : (_, _, _) proc_matcher = {on_objc_cpp; on_c; on_java} in {on_proc; on_args; markers} @@ -526,17 +541,18 @@ module Call = struct : ('f_in, 'f_proc_out, 'f_out, 'captured_types, 'markers) args_matcher -> ('f_proc_out, 'f_out, 'captured_types) func_args_end -> ('f_in, 'f_out) all_args_matcher = fun m func_args_end -> - let {on_proc= {on_c; on_objc_cpp}; on_args} = m in + let {on_proc= {on_c; on_java; on_objc_cpp}; on_args} = m in let on_c f c args = on_c f c |> pre_bind_opt ~f:(func_args_end ~on_args args) in + let on_java f java args = on_java f java |> pre_bind_opt ~f:(func_args_end ~on_args args) in let on_objc_cpp f objc_cpp args = on_objc_cpp f objc_cpp |> pre_bind_opt ~f:(func_args_end ~on_args args) in - {on_c; on_objc_cpp} + {on_c; on_java; on_objc_cpp} let make_matcher : ('f_in, 'f_out) all_args_matcher -> 'f_in -> 'f_out matcher = fun m f -> - let {on_c; on_objc_cpp} : (_, _) all_args_matcher = m in + let {on_c; on_java; on_objc_cpp} : (_, _) all_args_matcher = m in let on_objc_cpp objc_cpp args = match on_objc_cpp f objc_cpp args with | DoesNotMatch -> @@ -555,7 +571,16 @@ module Call = struct | RetryWith {on_c} -> on_c c args in - {on_objc_cpp; on_c} + let on_java java args = + match on_java f java args with + | DoesNotMatch -> + None + | Matches res -> + Some res + | RetryWith {on_java} -> + on_java java args + in + {on_objc_cpp; on_c; on_java} (** Simple implementation of a dispatcher, could be optimized later *) @@ -567,12 +592,17 @@ module Call = struct let on_c c args = List.find_map matchers ~f:(fun (matcher: _ matcher) -> matcher.on_c c args) in + let on_java java args = + List.find_map matchers ~f:(fun (matcher: _ matcher) -> matcher.on_java java args) + in fun procname args -> match procname with | ObjC_Cpp objc_cpp -> on_objc_cpp objc_cpp args | C c -> on_c c args + | Java java -> + on_java java args | _ -> None @@ -756,8 +786,9 @@ module Call = struct "Unexpected number/types of arguments for %a" Typ.Procname.pp procname in let on_c c _args = on_procname (C c) in + let on_java java _args = on_procname (Java java) in let on_objc_cpp objc_cpp _args = on_procname (ObjC_Cpp objc_cpp) in - {on_c; on_objc_cpp} + {on_c; on_java; on_objc_cpp} let ( $! ) path_matcher () = args_begin path_matcher @@ -864,14 +895,29 @@ module ProcName = struct let on_objc_cpp objc_cpp = List.find_map matchers ~f:(fun (matcher: _ matcher) -> matcher.on_objc_cpp objc_cpp) in + let on_templated_name templated_name = + List.find_map matchers ~f:(fun (matcher: _ matcher) -> + matcher.on_templated_name templated_name ) + in + let on_java (java: Typ.Procname.Java.t) = + let templated_name = templated_name_of_java java in + on_templated_name templated_name + in let on_c (c: c) = let template_args = template_args_of_template_spec_info c.template_args in let templated_name = (c.name, template_args) in - List.find_map matchers ~f:(fun (matcher: _ matcher) -> - matcher.on_templated_name templated_name ) + on_templated_name templated_name in fun procname -> - match procname with ObjC_Cpp objc_cpp -> on_objc_cpp objc_cpp | C c -> on_c c | _ -> None + match procname with + | ObjC_Cpp objc_cpp -> + on_objc_cpp objc_cpp + | C c -> + on_c c + | Java java -> + on_java java + | _ -> + None end module TypName = struct @@ -881,10 +927,6 @@ module TypName = struct let make_dispatcher : 'f matcher list -> 'f dispatcher = fun matchers typname -> - match templated_name_of_class_name typname with - | exception DoNotHandleJavaYet -> - None - | templated_name -> - List.find_map matchers ~f:(fun (matcher: _ matcher) -> - matcher.on_templated_name templated_name ) + let templated_name = templated_name_of_class_name typname in + List.find_map matchers ~f:(fun (matcher: _ matcher) -> matcher.on_templated_name templated_name) end diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index eb4916a2e..1c82979e9 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -185,7 +185,7 @@ let set_array_length array length_exp = mem |> Dom.Mem.add_stack (Loc.of_pvar array_pvar) v |> set_uninitialized location elt (Dom.Val.get_array_locs v) | _ -> - L.(die InternalError) "Unexpected type of first argument for __set_array_length()" + L.(die InternalError) "Unexpected type of first argument for __set_array_length() " and check = check_alloc_size length_exp in {exec; check} @@ -318,7 +318,8 @@ module Call = struct ; std_array0 >:: "array" &--> StdArray.constructor ; std_array2 >:: "at" $ capt_arg $+ capt_arg $!--> StdArray.at ; std_array2 >:: "operator[]" $ capt_arg $+ capt_arg $!--> StdArray.at - ; -"std" &:: "array" &::.*--> StdArray.no_model ] + ; -"std" &:: "array" &::.*--> StdArray.no_model + ; -"java.util.ArrayList" &:: "size" <>$ capt_exp $!--> get_array_length ] end module TypName = struct diff --git a/infer/tests/codetoanalyze/java/performance/ArrayListTest.java b/infer/tests/codetoanalyze/java/performance/ArrayListTest.java new file mode 100644 index 000000000..6d5ae1dc5 --- /dev/null +++ b/infer/tests/codetoanalyze/java/performance/ArrayListTest.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2018-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. + */ + +import java.util.List; +import java.util.ArrayList; + +public class ArrayListTest { + + public void iterate_over_arraylist(ArrayList list) { + for (int i = 0, size = list.size(); i < size; ++i) { + } + } +} diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index 2852d3c17..7dbd4ef5b 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -4,6 +4,8 @@ codetoanalyze/java/performance/ArrayCost.java, boolean ArrayCost.isPowOfTwo_FP(i codetoanalyze/java/performance/ArrayCost.java, boolean ArrayCost.isPowOfTwo_FP(int), 12, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 889] codetoanalyze/java/performance/ArrayCost.java, void ArrayCost.ArrayCost(int[]), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 6 + 5 * mag.length.ub] codetoanalyze/java/performance/ArrayCost.java, void ArrayCost.ArrayCost(int[]), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * mag.length.ub] +codetoanalyze/java/performance/ArrayListTest.java, void ArrayListTest.iterate_over_arraylist(ArrayList), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * list.length.ub] +codetoanalyze/java/performance/ArrayListTest.java, void ArrayListTest.iterate_over_arraylist(ArrayList), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 6 + 5 * list.length.ub] codetoanalyze/java/performance/Break.java, int Break.break_constant(int), 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 8 + 7 * p.ub] codetoanalyze/java/performance/Break.java, int Break.break_loop(int,int), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 1 + 7 * p.ub] codetoanalyze/java/performance/Break.java, int Break.break_loop(int,int), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2 + 7 * p.ub]