From 8d648b9c5ab2406bcc629f3565e10899bd1b6a77 Mon Sep 17 00:00:00 2001 From: Qianyi Shu Date: Fri, 17 Jul 2020 02:13:18 -0700 Subject: [PATCH] [cost] add cost model for most common NSString functions Summary: Add cost model for most common `NSString` functions in cost analysis Reviewed By: skcho Differential Revision: D22433005 fbshipit-source-id: 2f57bbda9 --- .../src/bufferoverrun/bufferOverrunModels.ml | 2 ++ .../src/bufferoverrun/bufferOverrunModels.mli | 9 +++++++ infer/src/cost/costModels.ml | 21 ++++++++++++++++ .../codetoanalyze/objc/performance/NSString.m | 13 ++++++++++ .../objc/performance/cost-issues.exp | 25 +++++++++++-------- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index d76a89c85..f6365356f 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -1335,6 +1335,8 @@ module NSString = struct let length = JavaString.length + + let get_length = JavaString.get_length end module Preconditions = struct diff --git a/infer/src/bufferoverrun/bufferOverrunModels.mli b/infer/src/bufferoverrun/bufferOverrunModels.mli index 4e649a638..fd6877d25 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.mli +++ b/infer/src/bufferoverrun/bufferOverrunModels.mli @@ -36,6 +36,15 @@ module Collection : sig (** Evaluate length of Java collection *) end +module NSString : sig + val get_length : + BufferOverrunUtils.ModelEnv.model_env + -> Exp.t + -> BufferOverrunDomain.Mem.t + -> BufferOverrunDomain.Val.t + (** Get length of NSString string *) +end + module JavaString : sig val get_length : BufferOverrunUtils.ModelEnv.model_env diff --git a/infer/src/cost/costModels.ml b/infer/src/cost/costModels.ml index 1a696a648..2d1513531 100644 --- a/infer/src/cost/costModels.ml +++ b/infer/src/cost/costModels.ml @@ -140,6 +140,17 @@ module BoundsOfCollection = BoundsOf (CostUtils.Collection) module BoundsOfArray = BoundsOf (CostUtils.Array) module BoundsOfCString = BoundsOf (CostUtils.CString) +module NSString = struct + let op_on_two_str cost_op ~of_function str1 str2 ({location} as model_env) ~ret:_ mem = + let get_length str = + let itv = + BufferOverrunModels.NSString.get_length model_env str mem |> BufferOverrunDomain.Val.get_itv + in + CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location + in + cost_op (get_length str1) (get_length str2) +end + module ImmutableSet = struct let construct = linear ~of_function:"ImmutableSet.construct" @@ -154,6 +165,16 @@ module Call = struct make_dispatcher [ -"google" &:: "StrLen" <>$ capt_exp $--> BoundsOfCString.linear_length ~of_function:"google::StrLen" + ; -"NSString" &:: "stringWithUTF8String:" <>$ capt_exp + $--> BoundsOfCString.linear_length ~of_function:"NSString.stringWithUTF8String:" + ; -"NSString" &:: "stringByAppendingPathComponent:" <>$ capt_exp $+ capt_exp + $--> NSString.op_on_two_str BasicCost.plus + ~of_function:"NSString.stringByAppendingPathComponent:" + ; -"NSString" &:: "isEqualToString:" <>$ capt_exp $+ capt_exp + $--> NSString.op_on_two_str BasicCost.min_default_left + ~of_function:"NSString.isEqualToString:" + ; -"NSString" &:: "hasPrefix:" <>$ capt_exp $+ capt_exp + $--> NSString.op_on_two_str BasicCost.min_default_left ~of_function:"NSString.hasPrefix:" ; +PatternMatch.implements_collections &:: "sort" $ capt_exp $+...$--> BoundsOfCollection.n_log_n_length ~of_function:"Collections.sort" diff --git a/infer/tests/codetoanalyze/objc/performance/NSString.m b/infer/tests/codetoanalyze/objc/performance/NSString.m index b84886217..3ab14d978 100644 --- a/infer/tests/codetoanalyze/objc/performance/NSString.m +++ b/infer/tests/codetoanalyze/objc/performance/NSString.m @@ -99,3 +99,16 @@ void string_length_linear(NSString* s) { for (int i = 0; i < [s.length integerValue]; i++) { } } + +bool string_is_equal_to_string_linear(NSString* str1, NSString* str2) { + return [str1 isEqualToString:str2]; +} + +NSString* string_by_appending_path_component_linear(NSString* path, + NSString* file) { + return [path stringByAppendingPathComponent:file]; +} + +bool string_has_prefix_linear(NSString* str, NSString* prefix) { + return [str hasPrefix:prefix]; +} diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index 0aa665283..0d5428687 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -2,7 +2,7 @@ codetoanalyze/objc/performance/NSArray.m, nsarray_access_constant, 42, OnUIThre codetoanalyze/objc/performance/NSArray.m, nsarray_access_linear, 3 + 7 ⋅ array.length + 3 ⋅ (array.length + 1), OnUIThread:false, [{array.length + 1},Loop,{array.length},Loop] codetoanalyze/objc/performance/NSArray.m, nsarray_add_object_constant, 8, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_add_objects_from_array_linear_FN, 8, OnUIThread:false, [] -codetoanalyze/objc/performance/NSArray.m, nsarray_array_with_objects_constant, 23, OnUIThread:false, [] +codetoanalyze/objc/performance/NSArray.m, nsarray_array_with_objects_constant, 27, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_binary_search_log_FN, 10, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_contains_object_linear_FN, 4, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_count_bounded_linear, 3 + 3 ⋅ array.length + 3 ⋅ (array.length + 1), OnUIThread:false, [{array.length + 1},Loop,{array.length},Loop] @@ -14,24 +14,24 @@ codetoanalyze/objc/performance/NSArray.m, nsarray_init_constant, 9, OnUIThread: codetoanalyze/objc/performance/NSArray.m, nsarray_init_with_array_constant_FP, ⊤, OnUIThread:false, [Call to nsarray_init_with_array_linear_FP,Unbounded loop,Loop] codetoanalyze/objc/performance/NSArray.m, nsarray_init_with_array_copy_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSArray.m, nsarray_init_with_array_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSArray.m, nsarray_init_with_objects_constant, 26, OnUIThread:false, [] +codetoanalyze/objc/performance/NSArray.m, nsarray_init_with_objects_constant, 39, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_is_equal_to_array_linear_FN, 5, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_iterate_linear_FN, 14, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_last_object_constant, 4, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_next_object_linear_FN, 10, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_object_at_indexed_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSArray.m, nsarray_sort_using_descriptors_constant, 14, OnUIThread:false, [] +codetoanalyze/objc/performance/NSArray.m, nsarray_sort_using_descriptors_constant, 30, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, nsarray_sort_using_descriptors_nlogn_FN, 9, OnUIThread:false, [] codetoanalyze/objc/performance/NSArray.m, objc_blocknsarray_binary_search_log_FN_1, 5, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_all_keys_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_all_values_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_dictionary_with_objects_linear, 21 + 14 ⋅ n_entries + 2 ⋅ (1+max(0, n_entries)), OnUIThread:false, [{1+max(0, n_entries)},Loop,{n_entries},Loop] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_constant, 20, OnUIThread:false, [] +codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_dictionary_with_objects_linear, 21 + 15 ⋅ n_entries + 2 ⋅ (1+max(0, n_entries)), OnUIThread:false, [{1+max(0, n_entries)},Loop,{n_entries},Loop] +codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_constant, 52, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerator_linear_FN, 10, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_fast_enumerate_linear_FN, 10, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_find_key_linear_FN, 19, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_dictionary_constant, 3, OnUIThread:false, [] -codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_literal_constant, 9, OnUIThread:false, [] +codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_literal_constant, 41, OnUIThread:false, [] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_with_dictionary_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSInteger.m, nsinteger_value_linear, 3 + 3 ⋅ integer + 2 ⋅ (1+max(0, integer)), OnUIThread:false, [{1+max(0, integer)},Loop,{integer},Loop] codetoanalyze/objc/performance/NSInteger.m, nsnumber_number_with_int_integer_value_constant, 34, OnUIThread:false, [] @@ -51,17 +51,20 @@ codetoanalyze/objc/performance/NSMutableDictionary.m, nsmutabledictionary_set_el codetoanalyze/objc/performance/NSString.m, call_component_separated_by_string_constant_FP, ⊤, OnUIThread:false, [Call to component_seperated_by_string_linear_FP,Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, call_init_with_string_constant_FP, ⊤, OnUIThread:false, [Call to init_with_string_linear_FP,Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, component_seperated_by_string_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, has_prefix_constant, 15, OnUIThread:false, [] +codetoanalyze/objc/performance/NSString.m, has_prefix_constant, 13, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, init_with_bytes_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, init_with_string_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, init_with_string_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, rangeof_character_from_set_linear_FN, 6, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, rangeof_string_quadratic_FN, 6, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, string_by_appending_same_string_linear_FN, 7, OnUIThread:false, [] +codetoanalyze/objc/performance/NSString.m, string_by_appending_path_component_linear, 4 + path.length.ub + file.length.ub, OnUIThread:false, [{file.length.ub},Modeled call to NSString.stringByAppendingPathComponent:,{path.length.ub},Modeled call to NSString.stringByAppendingPathComponent:] +codetoanalyze/objc/performance/NSString.m, string_by_appending_same_string_linear_FN, 8, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, string_by_appending_string_linear_FN, 7, OnUIThread:false, [] +codetoanalyze/objc/performance/NSString.m, string_has_prefix_linear, 4 + str.length.ub, OnUIThread:false, [{str.length.ub},Modeled call to NSString.hasPrefix:] +codetoanalyze/objc/performance/NSString.m, string_is_equal_to_string_linear, 4 + str1.length.ub, OnUIThread:false, [{str1.length.ub},Modeled call to NSString.isEqualToString:] codetoanalyze/objc/performance/NSString.m, string_length_linear, 3 + 3 ⋅ s.length.ub + 4 ⋅ (s.length.ub + 1), OnUIThread:false, [{s.length.ub + 1},Loop,{s.length.ub},Loop] -codetoanalyze/objc/performance/NSString.m, string_with_utf8_string_linear, 8 + 3 ⋅ p->strlen.ub + 4 ⋅ (p->strlen.ub + 1), OnUIThread:false, [{p->strlen.ub + 1},Loop,{p->strlen.ub},Loop] +codetoanalyze/objc/performance/NSString.m, string_with_utf8_string_linear, 7 + 3 ⋅ p->strlen.ub + p->strlen.ub + 4 ⋅ (p->strlen.ub + 1), OnUIThread:false, [{p->strlen.ub + 1},Loop,{p->strlen.ub},Modeled call to NSString.stringWithUTF8String:,{p->strlen.ub},Loop] codetoanalyze/objc/performance/NSString.m, substring_from_index_linear_FN, 13, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, substring_no_end_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/araii.m, Araii.buffer, 4, OnUIThread:false, [] @@ -126,11 +129,11 @@ codetoanalyze/objc/performance/field_access.m, Person.init_with_name_constant:, codetoanalyze/objc/performance/field_access.m, Person.name, 4, OnUIThread:false, [] codetoanalyze/objc/performance/field_access.m, Person.setBank_account:, 4, OnUIThread:false, [] codetoanalyze/objc/performance/field_access.m, Person.setName:, 4, OnUIThread:false, [] -codetoanalyze/objc/performance/field_access.m, Person.species_name_constant, 3, OnUIThread:false, [] +codetoanalyze/objc/performance/field_access.m, Person.species_name_constant, 7, OnUIThread:false, [] codetoanalyze/objc/performance/field_access.m, Test1.dealloc, 1, OnUIThread:false, [] codetoanalyze/objc/performance/field_access.m, Test1.setX:, 4, OnUIThread:false, [] codetoanalyze/objc/performance/field_access.m, Test1.x, 4, OnUIThread:false, [] -codetoanalyze/objc/performance/field_access.m, create_common_person_constant, 44, OnUIThread:false, [] +codetoanalyze/objc/performance/field_access.m, create_common_person_constant, 57, OnUIThread:false, [] codetoanalyze/objc/performance/field_access.m, iterate_upto_field_size_linear, 3 + 3 ⋅ test->_x.ub + 6 ⋅ (1+max(0, test->_x.ub)), OnUIThread:false, [{1+max(0, test->_x.ub)},Loop,{test->_x.ub},Loop] codetoanalyze/objc/performance/instantiate.m, do_2_times_constant, 20, OnUIThread:false, [] codetoanalyze/objc/performance/instantiate.m, do_half_m2_times_quadratic, 3 + 5 ⋅ (m - 1) × m + 7 ⋅ m + 2 ⋅ m × (max(1, m)) + 2 ⋅ (1+max(0, m)), OnUIThread:false, [{1+max(0, m)},Loop,{max(1, m)},Call to do_n_times,Loop,{m},Loop,{m},Loop,{m - 1},Call to do_n_times,Loop]