Make sure index and count do not overflow for long sequences

Throw an exception immediately before an overflow becomes observable.
Place check to prevent negative index from indexOf, indexOfFirst.
Do not insert overflow checks for arrays, lists, maps and char sequences.

#KT-16097
This commit is contained in:
Ilya Gorbunov
2018-08-09 23:31:31 +03:00
parent c44f62a3d4
commit 357c5be4fb
16 changed files with 252 additions and 58 deletions
@@ -155,6 +155,7 @@ object Aggregates : TemplateGroupBase() {
doc { "Returns the number of ${f.element.pluralize()} matching the given [predicate]." }
returns("Int")
body {
fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkCountOverflow($value)" else value
"""
${when (f) {
Iterables -> "if (this is Collection && isEmpty()) return 0"
@@ -162,7 +163,7 @@ object Aggregates : TemplateGroupBase() {
else -> ""
}}
var count = 0
for (element in this) if (predicate(element)) count++
for (element in this) if (predicate(element)) ${checkOverflow("++count")}
return count
"""
}
@@ -175,10 +176,11 @@ object Aggregates : TemplateGroupBase() {
doc { "Returns the number of ${f.element.pluralize()} in this ${f.collection}." }
returns("Int")
body {
fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkCountOverflow($value)" else value
"""
${if (f == Iterables) "if (this is Collection) return size" else ""}
var count = 0
for (element in this) count++
for (element in this) ${checkOverflow("++count")}
return count
"""
}
@@ -474,10 +476,11 @@ object Aggregates : TemplateGroupBase() {
typeParam("R")
returns("R")
body {
fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value
"""
var index = 0
var accumulator = initial
for (element in this) accumulator = operation(index++, accumulator, element)
for (element in this) accumulator = operation(${checkOverflow("index++")}, accumulator, element)
return accumulator
"""
}
@@ -617,6 +620,7 @@ object Aggregates : TemplateGroupBase() {
typeParam("T : S")
returns("S")
body {
fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value
"""
val iterator = this.iterator()
if (!iterator.hasNext()) throw UnsupportedOperationException("Empty ${f.doc.collection} can't be reduced.")
@@ -624,7 +628,7 @@ object Aggregates : TemplateGroupBase() {
var index = 1
var accumulator: S = iterator.next()
while (iterator.hasNext()) {
accumulator = operation(index++, accumulator, iterator.next())
accumulator = operation(${checkOverflow("index++")}, accumulator, iterator.next())
}
return accumulator
"""
@@ -899,9 +903,10 @@ object Aggregates : TemplateGroupBase() {
""" }
returns("Unit")
body {
fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value
"""
var index = 0
for (item in this) action(index++, item)
for (item in this) action(${checkOverflow("index++")}, item)
"""
}
}
@@ -52,6 +52,7 @@ object Elements : TemplateGroupBase() {
${if (f == Iterables) "if (this is List) return this.indexOf(element)" else ""}
var index = 0
for (item in this) {
checkIndexOverflow(index)
if (element == item)
return index
index++
@@ -106,6 +107,7 @@ object Elements : TemplateGroupBase() {
var lastIndex = -1
var index = 0
for (item in this) {
checkIndexOverflow(index)
if (element == item)
lastIndex = index
index++
@@ -157,12 +159,13 @@ object Elements : TemplateGroupBase() {
"""
var index = 0
for (item in this) {
${if (f != Lists) "checkIndexOverflow(index)" else ""}
if (predicate(item))
return index
index++
}
return -1
"""
""".lines().filterNot { it.isBlank() }.joinToString("\n")
}
body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) {
@@ -190,6 +193,7 @@ object Elements : TemplateGroupBase() {
var lastIndex = -1
var index = 0
for (item in this) {
checkIndexOverflow(index)
if (predicate(item))
lastIndex = index
index++
@@ -204,10 +204,11 @@ object Mapping : TemplateGroupBase() {
returns("C")
body {
fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value
"""
var index = 0
for (item in this)
destination.add(transform(index++, item))
destination.add(transform(${checkOverflow("index++")}, item))
return destination
"""
}
@@ -41,12 +41,13 @@ object Numeric : TemplateGroupBase() {
returns("Double")
platformName("averageOf<T>")
body {
fun checkOverflow(value: String) = if (f == Family.Sequences || f == Family.Iterables) "checkCountOverflow($value)" else value
"""
var sum: Double = 0.0
var count: Int = 0
for (element in this) {
sum += element
count += 1
${checkOverflow("++count")}
}
return if (count == 0) Double.NaN else sum / count
"""