diff --git a/infer/src/absint/PatternMatch.ml b/infer/src/absint/PatternMatch.ml index c1812891e..a266027b9 100644 --- a/infer/src/absint/PatternMatch.ml +++ b/infer/src/absint/PatternMatch.ml @@ -102,6 +102,8 @@ module Java = struct let implements_arrays = implements "java.util.Arrays" + let implements_iterable = implements "java.lang.Iterable" + let implements_iterator = implements "java.util.Iterator" let implements_collection = implements "java.util.Collection" diff --git a/infer/src/absint/PatternMatch.mli b/infer/src/absint/PatternMatch.mli index f78fe9c60..78f9915a8 100644 --- a/infer/src/absint/PatternMatch.mli +++ b/infer/src/absint/PatternMatch.mli @@ -32,6 +32,9 @@ module Java : sig val implements_arrays : Tenv.t -> string -> bool (** Check whether class implements Java's Arrays *) + val implements_iterable : Tenv.t -> string -> bool + (** Check whether class implements Java's Iterable *) + val implements_iterator : Tenv.t -> string -> bool (** Check whether class implements Java's Iterator *) diff --git a/infer/src/cost/costModels.ml b/infer/src/cost/costModels.ml index 9ba0d5467..4affd95ba 100644 --- a/infer/src/cost/costModels.ml +++ b/infer/src/cost/costModels.ml @@ -269,6 +269,12 @@ module Call = struct ; +PatternMatch.Java.implements_collections &:: "copy" <>$ capt_exp $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.copy" + (* TODO: T72085946; take the cost of function into account *) + ; +PatternMatch.Java.implements_iterable + &:: "forEach" $ capt_exp + $+...$--> BoundsOfCollection.linear_length ~of_function:"Iterable.forEach" + ; +PatternMatch.Java.implements_map &:: "forEach" $ capt_exp + $+...$--> BoundsOfCollection.linear_length ~of_function:"Map.forEach" ; +PatternMatch.Java.implements_collections &:: "fill" <>$ capt_exp $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.fill" diff --git a/infer/tests/codetoanalyze/java/performance/ForEachTest.java b/infer/tests/codetoanalyze/java/performance/ForEachTest.java new file mode 100644 index 000000000..da9cb1a85 --- /dev/null +++ b/infer/tests/codetoanalyze/java/performance/ForEachTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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.Map; + +class ForEachTest { + + int add(Integer x, Integer y) { + return x + y; + } + + int loop_linear(Integer x, List list) { + int sum = 0; + for (Integer el : list) { + sum = +el + x; + } + return sum; + } + + void map_linear(Map map) { + map.forEach((key, value) -> add(key, value)); + } + + void list_linear(List myList) { + myList.forEach(el -> add(el, 1)); + } + + // FN: We have limited lambda support and canot incur costs of the lambda calls yet. + void list_quadratic_FN(List myList) { + myList.forEach(el -> loop_linear(el, myList)); + } +} diff --git a/infer/tests/codetoanalyze/java/performance/cost-issues.exp b/infer/tests/codetoanalyze/java/performance/cost-issues.exp index 4c308d8fd..935ea7d27 100644 --- a/infer/tests/codetoanalyze/java/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/java/performance/cost-issues.exp @@ -183,6 +183,12 @@ codetoanalyze/java/performance/EvilCfg.java, EvilCfg.foo_FP(int,int,boolean):voi codetoanalyze/java/performance/FieldAccess.java, codetoanalyze.java.performance.FieldAccess$Test.(codetoanalyze.java.performance.FieldAccess), 6, OnUIThread:false, [] codetoanalyze/java/performance/FieldAccess.java, codetoanalyze.java.performance.FieldAccess.(), 3, OnUIThread:false, [] codetoanalyze/java/performance/FieldAccess.java, codetoanalyze.java.performance.FieldAccess.iterate_upto_field_size_linear(codetoanalyze.java.performance.FieldAccess$Test):void, 6 + 6 ⋅ test.a, OnUIThread:false, [{test.a},Loop] +codetoanalyze/java/performance/ForEachTest.java, ForEachTest.(), 3, OnUIThread:false, [] +codetoanalyze/java/performance/ForEachTest.java, ForEachTest.add(java.lang.Integer,java.lang.Integer):int, 10, OnUIThread:false, [] +codetoanalyze/java/performance/ForEachTest.java, ForEachTest.list_linear(java.util.List):void, 12 + myList.length, OnUIThread:false, [{myList.length},Modeled call to Iterable.forEach] +codetoanalyze/java/performance/ForEachTest.java, ForEachTest.list_quadratic_FN(java.util.List):void, 22 + myList.length, OnUIThread:false, [{myList.length},Modeled call to Iterable.forEach] +codetoanalyze/java/performance/ForEachTest.java, ForEachTest.loop_linear(java.lang.Integer,java.util.List):int, 9 + 17 ⋅ list.length + 3 ⋅ (list.length + 1), OnUIThread:false, [{list.length + 1},Loop,{list.length},Loop] +codetoanalyze/java/performance/ForEachTest.java, ForEachTest.map_linear(java.util.Map):void, 12 + map.length, OnUIThread:false, [{map.length},Modeled call to Map.forEach] codetoanalyze/java/performance/InferAnnotationTest.java, InferAnnotationTest.(), 3, OnUIThread:false, [] codetoanalyze/java/performance/InferAnnotationTest.java, InferAnnotationTest.assert_get_list_constant():void, 81, OnUIThread:false, [] codetoanalyze/java/performance/InferAnnotationTest.java, InferAnnotationTest.assert_get_map_constant():void, 102, OnUIThread:false, []