diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index 11abc24c3..701ffd076 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -1089,6 +1089,15 @@ module NSCollection = struct let get_first coll_id = get_at_index coll_id Exp.zero + let remove_last coll_id = {exec= change_size_by ~size_f:Itv.decr coll_id; check= no_check} + + let remove_all coll_id = + let exec model_env ~ret mem = + change_size_by ~size_f:(fun _ -> Itv.zero) coll_id model_env ~ret mem + in + {exec; check= no_check} + + let of_list list = let exec env ~ret:((id, _) as ret) mem = let mem = new_collection.exec env ~ret mem in @@ -1628,6 +1637,19 @@ module Call = struct &:: "arrayWithObjects:count:" <>$ capt_exp $+ capt_exp $--> NSCollection.create_from_array ; +PatternMatch.ObjectiveC.implements "NSArray" &:: "arrayWithObjects" &++> NSCollection.of_list + ; +PatternMatch.ObjectiveC.implements "NSMutableArray" + &:: "addObject:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.add + ; +PatternMatch.ObjectiveC.implements "NSMutableArray" + &:: "removeLastObject" <>$ capt_var_exn $--> NSCollection.remove_last + ; +PatternMatch.ObjectiveC.implements "NSMutableArray" + &:: "insertObject:atIndex:" <>$ capt_var_exn $+ any_arg $+ capt_exp + $--> NSCollection.add_at_index + ; +PatternMatch.ObjectiveC.implements "NSMutableArray" + &:: "removeObjectAtIndex:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.remove_at_index + ; +PatternMatch.ObjectiveC.implements "NSMutableArray" + &:: "removeAllObjects:" <>$ capt_var_exn $--> NSCollection.remove_all + ; +PatternMatch.ObjectiveC.implements "NSMutableArray" + &:: "addObjectsFromArray:" <>$ capt_var_exn $+ capt_exp $--> NSCollection.addAll ; +PatternMatch.ObjectiveC.implements "NSDictionary" &:: "dictionaryWithObjects:forKeys:count:" <>$ any_arg $+ capt_exp $+ capt_exp $--> NSCollection.create_from_array diff --git a/infer/src/cost/costModels.ml b/infer/src/cost/costModels.ml index f83489e54..72fc71bf3 100644 --- a/infer/src/cost/costModels.ml +++ b/infer/src/cost/costModels.ml @@ -221,6 +221,10 @@ module Call = struct ~of_function:"NSArray.isEqualToArray:" ; -"NSArray" &:: "containsObject:" <>$ capt_exp $+ any_arg $--> NSCollection.get_length ~of_function:"NSArray.containsObject:" + ; -"NSMutableArray" &:: "removeAllObjects" <>$ capt_exp + $--> NSCollection.get_length ~of_function:"NSArray.removeAllObjects" + ; -"NSMutableArray" &:: "addObjectsFromArray:" <>$ any_arg $+ capt_exp + $--> NSCollection.get_length ~of_function:"NSArray.addObjectsFromArray:" ; +PatternMatch.Java.implements_collections &:: "sort" $ capt_exp $+...$--> BoundsOfCollection.n_log_n_length ~of_function:"Collections.sort" diff --git a/infer/tests/codetoanalyze/objc/performance/NSMutableArray.m b/infer/tests/codetoanalyze/objc/performance/NSMutableArray.m index 9381a81ff..4a49c193b 100644 --- a/infer/tests/codetoanalyze/objc/performance/NSMutableArray.m +++ b/infer/tests/codetoanalyze/objc/performance/NSMutableArray.m @@ -23,7 +23,10 @@ void nsmarray_empty_ok_costant() { [array insertObject:@1 atIndex:0]; } -void nsmarray_add_in_loop_constant() { +// top cost caused by not able to estimate the +// length of array after adding elements to the +// array in a loop +void nsmarray_add_in_loop_constant_FP() { NSMutableArray* array = [[NSMutableArray alloc] init]; for (int i = 0; i < 10; i++) { [array addObject:[NSNumber numberWithInt:i]]; @@ -32,12 +35,12 @@ void nsmarray_add_in_loop_constant() { } } -void nsmarray_add_in_loop_linear(NSUInteger n) { +void nsmarray_add_in_loop_linear_FP(NSUInteger n) { NSMutableArray* array = [[NSMutableArray alloc] init]; for (int i = 0; i < n; i++) { [array addObject:[NSNumber numberWithInt:i]]; } - for (int i = 0; i < n; i++) { + for (int i = 0; i < array.count; i++) { } } @@ -77,8 +80,6 @@ void nsmarray_add_all_constant() { [array2 addObject:@0]; [array2 addObject:@1]; - NSIndexSet* index_set = [[NSIndexSet alloc] initWithIndex:0]; - [array2 addObjectsFromArray:array1]; } @@ -108,10 +109,11 @@ id nsmarray_remove_constant() { [array addObject:@0]; [array addObject:@1]; [array removeObjectAtIndex:0]; + [array removeLastObject]; return array[0]; } -void nsmarray_remove_in_loop_constant() { +void nsmarray_remove_in_loop_constant_FP() { NSMutableArray* array = [[NSMutableArray alloc] init]; for (int i = 0; i < 10; i++) { [array addObject:[NSNumber numberWithInt:i]]; @@ -120,3 +122,10 @@ void nsmarray_remove_in_loop_constant() { [array removeObjectAtIndex:i]; } } + +void nsmarray_remove_all_linear(NSMutableArray* array) { + + [array removeAllObjects]; + for (int i = 0; i < array.count; i++) { + } +} diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index 4fb9711e1..02ed5c3e0 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -37,16 +37,17 @@ codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_literal_constan 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, [] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_all_constant, 25, OnUIThread:false, [] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_constant, 92, OnUIThread:false, [] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_linear, 8 + 7 ⋅ n + 3 ⋅ n + 2 ⋅ (n + 1) + 2 ⋅ (n + 1), OnUIThread:false, [{n + 1},Loop,{n + 1},Loop,{n},Loop,{n},Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_all_constant, 23, OnUIThread:false, [] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_quadratic, 6 + 5 ⋅ n + 8 ⋅ n × m + 2 ⋅ n × (m + 1) + 2 ⋅ (n + 1), OnUIThread:false, [{n + 1},Loop,{m + 1},Loop,{m},Loop,{n},Loop] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_then_loop_constant, 42, OnUIThread:false, [] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_then_loop_constant, 108, OnUIThread:false, [] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_empty_ok_costant, 7, OnUIThread:false, [] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_init_with_capacity_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_constant, 15, OnUIThread:false, [] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_in_loop_constant, 92, OnUIThread:false, [] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_in_loop_constant, 18, OnUIThread:false, [] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_all_linear, 4 + 3 ⋅ array->elements.length.ub + array->elements.length.ub + 3 ⋅ (array->elements.length.ub + 1), OnUIThread:false, [{array->elements.length.ub + 1},Loop,{array->elements.length.ub},Modeled call to NSArray.removeAllObjects,{array->elements.length.ub},Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_constant, 17, OnUIThread:false, [] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_in_loop_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_in_loop_constant, 51, OnUIThread:false, [] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_linear, 3 + 11 ⋅ array->elements.length.ub + 3 ⋅ (array->elements.length.ub + 1), OnUIThread:false, [{array->elements.length.ub + 1},Loop,{array->elements.length.ub},Loop] codetoanalyze/objc/performance/NSMutableDictionary.m, nsmutabledictionary_set_element_in_loop_linear_FN, 14, OnUIThread:false, [] codetoanalyze/objc/performance/NSMutableString.m, nsmstring_append_string_constant, 14 + 3 ⋅ (str.length.ub + 5) + 3 ⋅ (str.length.ub + 6), OnUIThread:false, [{str.length.ub + 6},Loop,{str.length.ub + 5},Loop] diff --git a/infer/tests/codetoanalyze/objc/performance/issues.exp b/infer/tests/codetoanalyze/objc/performance/issues.exp index a5f7530b4..952e6c9f8 100644 --- a/infer/tests/codetoanalyze/objc/performance/issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/issues.exp @@ -6,13 +6,16 @@ codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_enumerate_constant, codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_find_key_linear_FN, 3, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: NSEnumerator.nextObject,Assignment,,Unknown value from: NSEnumerator.nextObject,Array access: Offset: [-oo, +oo] (⇐ [-oo, +oo] + [-oo, +oo]) Size: [0, +oo]] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_with_dictionary_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSDictionary.m, nsdictionary_init_with_dictionary_linear_FP, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSDictionary.initWithDictionary:,Binary operation: ([0, +oo] + 1):signed32] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_constant, 5, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_then_loop_constant, 14, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_constant_FP, 5, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Array declaration,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_add_in_loop_linear_FP, 5, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Array declaration,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_init_with_capacity_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_init_with_capacity_constant_FP, 3, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSMutableArray.initWithCapacity:,Binary operation: ([0, +oo] + 1):signed32] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_constant, 5, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 0 Size: 0] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_in_loop_constant, 5, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] -codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_in_loop_constant, 6, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_constant, 6, BUFFER_OVERRUN_L3, no_bucket, ERROR, [,Array declaration,Array access: Offset: 0 Size: [0, 2]] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_in_loop_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_in_loop_constant_FP, 5, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Array declaration,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_remove_in_loop_constant_FP, 6, BUFFER_OVERRUN_L5, no_bucket, ERROR, [,Array declaration,,Array declaration,Array access: Offset: [0, +oo] Size: [0, +oo]] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_linear, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSNumber.intValue,Binary operation: ([-oo, +oo] + 1):signed32] codetoanalyze/objc/performance/NSString.m, init_string_constant, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop]